ถ้าคุณ build แอปด้วย iOS 27 SDK โดยไม่ทำ UIScene lifecycle แอปจะไม่เปิดเลยครับ และแอปที่ไม่เปิด ก็ไม่มีทาง register push ได้ — notification ของคุณก็จะเงียบไปเฉยๆ โดยไม่มี crash report ไหนชี้ไปที่ push เป็นสาเหตุ อาการที่เห็นดูเหมือนปัญหาการส่ง (delivery) แต่จริงๆ แล้วเป็นปัญหาตอนแอปเปิด (launch) ครับ

จุดนี้แหละที่ทำให้หลายทีมงงเพราะ push กับการเปิดแอปมันดูเหมือนคนละเรื่องกันเลย ทั้งในโค้ดและในหัวของทีมด้วย iOS 27 เอาสองเรื่องนี้มาผูกกัน บทความนี้จะพาดูว่าอะไรพังบ้าง ห่วงโซ่ที่ทำให้ notification เงียบไปทีละขั้น ใครอยู่ในกลุ่มเสี่ยง และ checklist สำหรับย้ายระบบให้แก้ปัญหาได้ Pushwoosh เป็นแพลตฟอร์ม customer engagement และ iOS SDK ของเราจัดการเรื่องนี้ไว้ให้แล้ว แต่วิธีแก้ด้านล่างใช้ได้ไม่ว่าคุณจะใช้ Pushwoosh หรือไม่ก็ตามครับ

📖

บทความนี้เป็นส่วนหนึ่งของ คู่มือรับมือ iOS 27 ระหว่างที่กำลังแก้ SDK อยู่ ลองเช็ค ขอบเขต device ที่แอปคุณเข้าถึงได้จริง ไปด้วยเลยครับ

อะไรพังจริงๆ

Apple ประกาศใน WWDC25 ว่า version ถัดจาก iOS 26 จะบังคับให้ UIKit app ที่ build ด้วย SDK ล่าสุดต้องใช้ UIScene lifecycle iOS 27 คือ version นั้นครับ และตอนนี้ข้อบังคับนี้บังคับใช้แล้ว

จุดสำคัญที่หลายกระทู้ตื่นตระหนกมักเข้าใจผิดคือ ตัวบังคับจะทำงานตอนที่คุณ build ด้วย iOS 27 SDK ใน Xcode 27 ไม่ใช่แค่ตอนที่ user เปิดแอปเดิมของคุณบน iOS 27 build ที่คุณ publish อยู่ตอนนี้ยังใช้งานได้ปกติบนเครื่องที่อัปเดตแล้ว ปัญหาจะมาตอน release ถัดไปที่ compile ด้วย SDK ใหม่ ความต่างตรงนี้ให้เวลาคุณเตรียมตัว แต่แค่จนถึง submission ครั้งถัดไปเท่านั้นครับ

สิ่งที่ไม่เปลี่ยนเลย และเป็นจุดที่บทความแนวตื่นตระหนกมักพูดเกินจริง คือ APNs flow เองยังคงเดิมทุกอย่าง คุณยังคงขอ authorization เรียก registerForRemoteNotifications() และยังได้รับ device token ที่ didRegisterForRemoteNotificationsWithDeviceToken เหมือนเดิม callback ตัวนี้ยังอยู่ใน AppDelegate และตอนนี้ iOS 27 ยังไม่มี scene-based equivalent ให้ย้ายไปครับ กลไก push เองไม่มีปัญหาอะไรเลย สิ่งที่ Apple เปลี่ยนคือขั้นตอนตอนแอปเปิด

ห่วงโซ่ที่ทำให้ push เงียบไป

เมื่อแอปที่ไม่มี scene manifest ถูก build ด้วย iOS 27 SDK ความล้มเหลวจะเกิดขึ้นตามลำดับนี้ครับ

  1. UIKit จะเช็คว่ามี scene ตอนเปิดแอป แต่ไม่เจอ UIApplicationSceneManifest ใน Info.plist
  2. แอปปิดตัวเองก่อนที่ method ไหนใน AppDelegate จะทำงานเลย
  3. เพราะ application(_:didFinishLaunchingWithOptions:) ไม่เคยทำงาน registerForRemoteNotifications() ก็เลยไม่ทำงานตามไปด้วย
  4. ไม่มีการ register didRegisterForRemoteNotificationsWithDeviceToken ก็เลยไม่ถูกเรียก แอปจึงไม่ได้รับ APNs token เลย
  5. ไม่มี token ก็ไม่มีการส่ง และเพราะแอป crash ตอนเปิด log ก็จะชี้ไปที่ขั้นตอนเปิดแอป ไม่ใช่ pipeline ของ notification

สาเหตุที่เรื่องนี้กิน debugging time เยอะมาก เพราะอาการกับต้นเหตุมันอยู่ไกลกันมาก คุณเห็นว่า delivery หายไป ก็เริ่มไปเช็ค push provider, payload, certificate แต่จุดที่พังจริงอยู่สูงขึ้นไป 3 ชั้น ในไฟล์ manifest ที่ไม่เกี่ยวอะไรกับ notification เลยครับ

ใครอยู่ในกลุ่มเสี่ยง

ข้อบังคับนี้กระทบ UIKit app แต่จะกระทบมากน้อยแค่ไหนขึ้นอยู่กับ stack ที่ใช้ครับ

Stackความเสี่ยง
Native UIKitโดนตรงๆ ถ้าคุณดูแล AppDelegate lifecycle เอง คุณก็ต้องดูแลการย้ายระบบเอง
SwiftUIเสี่ยงน้อยกว่าถ้าใช้ App protocol กับ WindowGroup ที่เป็น scene-based อยู่แล้ว แต่ถ้าแอปไหนยังพึ่ง UIApplicationDelegateAdaptor ที่มี custom window logic ควรเช็คให้ดี
Flutterframework จะ auto-migrate แอปที่ไม่ได้แก้ AppDelegate ให้ใน version ล่าสุด แต่ custom native logic ต้อง migrate เองครับ
React Nativeขึ้นอยู่กับ native module ของคุณ และ third-party SDK wrapper ที่ไป hook AppDelegate ไว้
Stack
1 / 4
Native UIKit
ความเสี่ยง
โดนตรงๆ ถ้าคุณดูแล AppDelegate lifecycle เอง คุณก็ต้องดูแลการย้ายระบบเอง
Stack
2 / 4
SwiftUI
ความเสี่ยง
เสี่ยงน้อยกว่าถ้าใช้ App protocol กับ WindowGroup ที่เป็น scene-based อยู่แล้ว แต่ถ้าแอปไหนยังพึ่ง UIApplicationDelegateAdaptor ที่มี custom window logic ควรเช็คให้ดี
Stack
3 / 4
Flutter
ความเสี่ยง
framework จะ auto-migrate แอปที่ไม่ได้แก้ AppDelegate ให้ใน version ล่าสุด แต่ custom native logic ต้อง migrate เองครับ
Stack
4 / 4
React Native
ความเสี่ยง
ขึ้นอยู่กับ native module ของคุณ และ third-party SDK wrapper ที่ไป hook AppDelegate ไว้

จุดที่ทีม cross-platform ต้องระวังจริงๆ ไม่ใช่ตัว framework แต่เป็น SDK ที่ซ้อนอยู่บนนั้นครับ SDK push หรือ analytics ตัวไหนที่ install ตัวเองด้วยการ wrap AppDelegate ของคุณ อาจบล็อกการย้ายไป scene ไว้จนกว่า vendor เจ้านั้นจะออก scene support ถ้า dependency ตัวไหนถือ entry point @main ของคุณอยู่ คุณก็ต้องรอ release ของเขา ไม่ใช่แค่ของคุณเองครับ เราเคยเห็น migration ค้างอยู่ 2 สัปดาห์เพราะเรื่องนี้เป๊ะๆ — dependency ตัวเดียวที่ไม่ยอมขยับ ทั้งที่งานส่วนที่ทีมควบคุมได้เองเสร็จหมดแล้ว อย่าเพิ่งคิดว่า migration จะเสร็จวันเดียว เช็ค scene compatibility ของ notification SDK ที่ใช้อยู่ก่อนครับ ถ้าคุณใช้ Flutter plugin ของเรา หน้า known migration issues คือสิ่งแรกที่ควรอ่านครับ

Checklist การย้ายระบบ

วิธีแก้ไม่ได้เปลี่ยนไปเลยตั้งแต่ scene เปิดตัวปี 2019 สิ่งที่เปลี่ยนคือมันไม่ใช่ตัวเลือกอีกต่อไปครับ

  • เพิ่ม scene manifest ใส่ UIApplicationSceneManifest ใน Info.plist หรือ config scene ผ่านโค้ดด้วย method ของ UIApplicationDelegate และ UISceneDelegate
  • สร้าง SceneDelegate ถ้ายังไม่มีให้เพิ่มเข้าไป และเช็คให้แน่ใจว่ามัน compile เข้า target จริงๆ SceneDelegate ที่มีอยู่ในโปรเจกต์แต่ไม่เคยถูกเพิ่มเข้า build sources เป็นกับดักที่พบบ่อย
  • ย้ายการสร้าง window ความเป็นเจ้าของ window จะย้ายออกจาก AppDelegate เปลี่ยน UIWindow(frame:) เป็น UIWindow(windowScene:) และ host root view จาก scene
  • ย้ายการจัดการ UI lifecycle การเปลี่ยน foreground, background, active/inactive ย้ายไปที่ UISceneDelegate หลัง migration แล้ว UIKit จะไม่เรียก method สถานะ UI บน AppDelegate อีก อะไรที่เหลืออยู่ตรงนั้นจะหยุดทำงานแบบเงียบๆ
  • ปล่อย push registration ไว้ที่เดิม การ register token และ callback ยังอยู่ใน AppDelegate เหมือนเดิม อย่าย้ายมันเพียงเพื่อความสมมาตร
  • ย้ายการจัดการ URL และ deep link URL ที่เข้ามาตอนนี้มาผ่าน method ของ scene ถ้า push ของคุณเปิดหน้าจอเฉพาะ ขั้นตอนนี้แหละที่ทำให้ deep link ยังทำงานได้
  • build ใหม่และตรวจสอบการออก token build ด้วย iOS 27 SDK เปิดแอป แล้วเช็คว่าแอปได้รับ APNs token จริง ถ้าไม่ได้ ลองเริ่มจากคู่มือ แก้ปัญหา error บน iOS ตรวจสอบบน build จริงก่อน release ไม่ใช่หลัง release

เก็บ AppDelegate ไว้นะครับ นี่คือจุดที่หลายทีมเข้าใจผิดตอนอ่านว่า “AppDelegate กำลังจะหายไป” มันไม่หายไปครับ บทบาทของมันแค่แคบลงเหลือแค่ event ระดับ process กับ app ในขณะที่ UI lifecycle ย้ายไป scene การ register push เป็นเรื่องระดับ app ซึ่งนั่นแหละคือเหตุผลที่มันยังอยู่ที่เดิม

ทำไมพึ่งแค่ push อย่างเดียวถึงเสี่ยง

ถอยออกมามองภาพรวมกันครับ key ตัวเดียวใน manifest ไฟล์ที่แทบไม่มีใครในทีมเปิดดู สามารถทำให้ push channel ทั้งหมดของคุณหยุดทำงานได้ใน release ถัดไป นี่คือความเปราะบางที่มากเกินไปสำหรับ channel การส่งแค่ช่องทางเดียว

และนี่ยังซ้อนทับกับข้อจำกัดที่ push มีอยู่แล้วคือ opt-in แม้จะมี กลยุทธ์ขอ permission ที่ดีแค่ไหน ผู้ใช้ส่วนใหญ่ก็ยังไม่ให้ push permission ตั้งแต่แรกอยู่ดี ดังนั้นในวันที่ดีที่สุด push ก็เข้าถึงได้แค่บางส่วนของ user base เท่านั้นครับ ปัญหาระดับ platform แบบนี้ยิ่งขยายช่องว่างระหว่างคนที่คุณเข้าถึงได้กับคนที่คุณเข้าถึงจริงให้กว้างขึ้นไปอีก

ทีมที่รับมือกับการเปลี่ยนแปลงแบบนี้ได้ดี คือทีมที่ไม่พึ่ง channel เดียว เมื่อ push เป็นแค่หนึ่งใน channel ที่ส่งคู่กับ email, SMS และ in-app การเกิดปัญหาตอนเปิดแอปใน platform เดียวก็แค่ลด reach ลงบางส่วน ไม่ได้ลบทิ้งทั้งหมด นี่คือ margin ที่คุณสร้างไว้รับมือกับความประหลาดใจแบบที่ Apple เพิ่งปล่อยออกมาครับ

👉🏻

ระหว่างที่งาน SDK ยังเปิดอยู่ ก็เป็นโอกาสดีที่จะทบทวนด้วยว่า notification แบบไหนถึงคุ้มค่าที่จะส่ง

ให้ Pushwoosh ช่วยเช็ค iOS 27 push setup ของคุณ

Pushwoosh SDK รองรับ scene lifecycle ของ iOS 27 อยู่แล้ว และการตั้งค่าแบบ multichannel หมายความว่าปัญหาใน platform เดียวจะไม่ดึงกลยุทธ์ notification ทั้งหมดของคุณลงไปด้วย ถ้าคุณกำลังย้ายระบบอยู่แล้วมีอะไรไม่ register iOS SDK FAQ ครอบคลุมปัญหาที่พบบ่อยไว้แล้วครับ และถ้าอยากให้เราช่วยดูอีกรอบว่า push registration ของคุณจะรอดจากการเปลี่ยนแปลงนี้หรือไม่ เราพร้อมช่วยเช็ค integration ไปด้วยกันครับ รวมถึงถ้าคุณใช้ LINE เป็นช่องทางหลักด้วย เราก็ช่วยดูภาพรวมทั้งระบบให้ได้เลยครับ

เช็ค iOS 27 push setup ของคุณ
เช็คความเข้ากันได้กับ iOS 27

คำถามที่พบบ่อย

ไม่ใช่เองครับ แอปที่อยู่บน App Store อยู่แล้วจะยังทำงานได้ปกติบนเครื่อง iOS 27 ปัญหาจะเกิดตอนที่คุณ build version ใหม่ด้วย iOS 27 SDK โดยไม่ทำ scene lifecycle ซึ่ง build นั้นจะไม่เปิดเลย build ที่ publish อยู่ตอนนี้ปลอดภัยจนกว่าจะถึง submission ครั้งถัดไปครับ

Pushwoosh Team
Content Team ที่ Pushwoosh
แชร์

บทความที่เกี่ยวข้อง

ดูทั้งหมด