Mike Miller
October 23, 2025

Apple is changing how UIKit apps start — and it directly impacts push notifications.
Starting in iOS 26, developers began seeing console warnings urging adoption of the scene-based life cycle (UIScene). With iOS 27, that shift becomes mandatory.
If your app doesn’t use scenes, it won’t launch when built with the new SDK — and without launch, there’s no way to register for remote notifications or receive APNs tokens.
In iOS 26, iPadOS 26, Mac Catalyst 26, tvOS 26, visionOS 26, you'll start seeing the following message:
"UIScene lifecycle will soon be required. Failure to adopt will result in an assert in the future."
This guide explains exactly what’s changing, what stays the same, and the fastest way to migrate — so your push notifications keep flowing and app keeps working.
UISceneDelegate.AppDelegate.UISceneDelegate now to ensure your push registration and Courier token sync continue working with iOS 27 and beyond.didRegisterForRemoteNotificationsWithDeviceToken go now? It stays in your AppDelegate — there’s no UISceneDelegate equivalent. Apple hasn’t introduced a scene-based version of that callback, so keep handling APNs tokens in AppDelegate (or let CourierDelegate handle it automatically).Apple is retiring the single-window AppDelegate life cycle and making UIScene mandatory for UIKit apps — a shift that started back in iOS 13 but is only now being enforced. Scenes allow each window to manage its own state, enabling features like iPad multitasking and smoother background/foreground transitions.
By iOS 26, Apple began warning developers:
“This process does not adopt the UIScene lifecycle. This will become an assert in a future version.”
That future version is the iOS 27 SDK.
If your app doesn’t declare a scene, it won’t launch — meaning didRegisterForRemoteNotificationsWithDeviceToken() in your App Delegate never runs, and APNs tokens are never issued. No token = no push.
Supporting a scene in your app ensures:
Apple hasn’t changed the push fundamentals.
Here’s the core flow that still applies in both UIKit and SwiftUI:
Copied!
// 1. Ask permissionUNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ inif granted {DispatchQueue.main.async {UIApplication.shared.registerForRemoteNotifications()}}}// 2. Receive the device tokenfunc application(_ application: UIApplication,didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {let token = deviceToken.map { String(format: "%02x", $0) }.joined()print("APNs token: \(token)")// Send to backend / Courier}
AppDelegate.CourierDelegate, all of this is done automatically for you.UNUserNotificationCenter is still how you request notification authorization.Copied!
<key>UIApplicationSceneManifest</key><dict><key>UIApplicationSupportsMultipleScenes</key><false/><key>UISceneConfigurations</key><dict><key>UIWindowSceneSessionRoleApplication</key><array><dict><key>UISceneConfigurationName</key><string>Default Configuration</string><key>UISceneDelegateClassName</key><string>$(PRODUCT_MODULE_NAME).SceneDelegate</string></dict></array></dict></dict>
Copied!
class SceneDelegate: UIResponder, UIWindowSceneDelegate {var window: UIWindow?func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).guard let _ = (scene as? UIWindowScene) else { return }}func sceneDidDisconnect(_ scene: UIScene) {// Called as the scene is being released by the system.// This occurs shortly after the scene enters the background, or when its session is discarded.// Release any resources associated with this scene that can be re-created the next time the scene connects.// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).}func sceneDidBecomeActive(_ scene: UIScene) {// Called when the scene has moved from an inactive state to an active state.// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.}func sceneWillResignActive(_ scene: UIScene) {// Called when the scene will move from an active state to an inactive state.// This may occur due to temporary interruptions (ex. an incoming phone call).}func sceneWillEnterForeground(_ scene: UIScene) {// Called as the scene transitions from the background to the foreground.// Use this method to undo the changes made on entering the background.}func sceneDidEnterBackground(_ scene: UIScene) {// Called as the scene transitions from the foreground to the background.// Use this method to save data, release shared resources, and store enough scene-specific state information// to restore the scene back to its current state.}}
Copied!
@mainclass AppDelegate: UIResponder, UIApplicationDelegate {func application(_ application: UIApplication,didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {let token = deviceToken.map { String(format: "%02x", $0) }.joined()print("APNs token: \(token)")// Sync to Courier}}
CourierDelegate, this is done automatically for you.SwiftUI already uses scenes. You just need to bridge an AppDelegate to keep receiving push callbacks.
Copied!
@mainstruct MyApp: App {@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate...}
iOS 26 — Warning Stage
Your legacy app still launches but logs:
Copied!
This process does not adopt the UIScene lifecycle.This will become an assert in a future version.
Push registration still works, but you’re officially on borrowed time.
iOS 27 — Enforcement Stage
When you build with the iOS 27 SDK, UIKit enforces the assert.
No scene manifest = immediate crash on launch — before AppDelegate methods fire.
Summary Table
| iOS Version | Behavior | Migration Status |
|---|---|---|
| ≤ 25 | Legacy AppDelegate lifecycle works | Optional |
| 26 | Console warning | Strongly recommended |
| 27 SDK | Launch assert (crash) | Required |
Bottom line: migrate now, not when Xcode 17 flips the SDK default.
Quick Checklist
SceneDelegate to your app (UIKit).AppDelegate for APNs callbacks. (Or use CourierDelegate to automatically sync push notification tokens)CourierDelegate does this automatically)Key Takeaway
Supporting the UIScene lifecycle isn’t just future-proofing — it’s required for your app to launch and receive push notifications once you adopt the iOS 27 SDK.
Add scene support, validate token registration, and your Courier push pipeline will stay reliable for iOS 27, 28, and beyond.

How We Investigate Support Tickets at Courier
Courier's support team resolves complex issues 4X faster using parallel investigation. Here's how it works: when a ticket comes in, an AI agent starts exploring the codebase while the support engineer examines actual customer data. The agent traces code paths and searches past investigations. The human reads event logs and forms hypotheses based on real state. Running both simultaneously catches mismatches fast—the agent sees what could cause a problem, the human sees what actually happened. This post breaks down the workflow, tools, and documentation structure that makes it repeatable.
By Thomas Schiavone
December 18, 2025

How to Use WhatsApp Typing Indicators on Twilio (Public Beta Guide)
Twilio now supports typing indicators for WhatsApp. When your backend takes a few seconds to generate a response, you can show users that something's happening instead of leaving them staring at a silent chat. The indicator appears when you call the new /v2/Indicators/Typing endpoint, automatically marks the message as read, and disappears after your response arrives or 25 seconds pass. This guide covers the API details, implementation patterns for Node.js and Python, when to use typing indicators, and current beta limitations.
By Kyle Seyler
December 03, 2025

Nodemailer Alternative: Outgrowing DIY Email Infrastructure
Nodemailer works great for transactional email in early-stage products. But B2B customer journeys demand more: Slack notifications for engineering teams, Microsoft Teams messages for enterprise customers, in-app notification centers, SMS escalations, and push alerts. Building this yourself means maintaining integrations with SendGrid, Twilio, Firebase, Slack's API, and Microsoft Graph. Courier provides omnichannel notification infrastructure through one API, handling routing, preferences, delivery, and analytics across every channel.
By Kyle Seyler
November 26, 2025
© 2025 Courier. All rights reserved.