Irbisa · Cheatsheet
App Lifecycle, Background & Push
Cold launch, scene phases, background modes, BGTaskScheduler, APNs, silent push, universal links
What states can an iOS app be in (not running, inactive, active, background, suspended), and what moves it from one to the next?
final class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? // background -> inactive: about to return to the screen func sceneWillEnterForeground(_ scene: UIScene) { feed.refreshIfStale() }…What is the difference between
AppDelegateandSceneDelegate, and why did iOS 13 move the UI lifecycle into scenes?@mainfinal class AppDelegate: UIResponder, UIApplicationDelegate { // Once per process, with or without UI func application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Analytics.configure()…How do you observe the app lifecycle in SwiftUI with
scenePhase, and when do you still need@UIApplicationDelegateAdaptor?@mainstruct ShopApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate @Environment(\.scenePhase) private var scenePhase @State private var store = CartStore()…Is
applicationWillTerminateguaranteed to run, and where should you save state instead?What is the difference between local and remote notifications, and how do you ask the user for permission?
import UIKitimport UserNotifications @MainActorfunc enableReminders() async throws { let center = UNUserNotificationCenter.current()…Session counts doubled after this shipped. Why, and which callback should start a session?
final class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func sceneDidBecomeActive(_ scene: UIScene) { Analytics.log("session_start") } func sceneDidEnterBackground(_ scene: UIScene) { Analytics.log("session_end") }}What happens between the user tapping the icon and
application(_:didFinishLaunchingWithOptions:)?How does iOS limit background work, and what legitimate ways are there to run code after the user leaves the app?
<!-- Info.plist: declare only what the app genuinely does --><key>UIBackgroundModes</key><array> <!-- lets the system wake you for BGAppRefreshTask --> <string>fetch</string> <!-- lets the system wake you for BGProcessingTask -->…This code keeps an upload alive after the user leaves the app. What is wrong with it?
func sceneDidEnterBackground(_ scene: UIScene) { let app = UIApplication.shared let id = app.beginBackgroundTask(withName: "upload") uploader.upload(draft) { result in if case .success = result { app.endBackgroundTask(id) } }}Find the bug: a user checks another app for a few minutes mid-workout, and on return this timer is minutes behind.
struct WorkoutTimerView: View { @State private var elapsed = 0 private let ticker = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { Text(Duration.seconds(elapsed).formatted(.time(pattern: .minuteSecond))) .monospacedDigit() .onReceive(ticker) { _ in elapsed += 1 } }}How do you schedule work with
BGTaskScheduler, and why might a registered task never run?// Register before didFinishLaunching returnsfunc application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.app.nightly-sync", using: nil) { task in handleSync(task as! BGProcessingTask)…How do you test a
BGTaskSchedulertask without waiting for the system to run it?# 1. Run on a device from Xcode, let the app call submit(),# then send it to the background.# 2. Press Pause in Xcode and enter in the LLDB console: e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.app.nightly-sync"]…How do push notifications work end to end: APNs, the device token and the payload?
func application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Every launch: the token may have changed since last time. application.registerForRemoteNotifications() return true}…What does a silent push need to wake your app, and why might it still not arrive?
// What the server sends:// apns-push-type: background <- alert here means no background wake// apns-priority: 5 <- 10 is not allowed for background pushes// apns-topic: com.example.app// { "aps": { "content-available": 1 }, "reason": "feed" }…Find the bug in this silent push handler: the push arrives, yet the inbox is still stale when the user opens the app.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { guard userInfo["reason"] as? String == "inbox" else { completionHandler(.noData) return } Task { try await inbox.sync() } completionHandler(.newData)}How do you show or hide a notification that arrives while the app is open, and how do taps and action buttons reach
userNotificationCenter(_:didReceive:)?func application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let reply = UNTextInputNotificationAction(identifier: "REPLY", title: "Reply", options: [], textInputButtonTitle: "Send", textInputPlaceholder: "Message") let message = UNNotificationCategory(identifier: "MESSAGE", // "category": "MESSAGE" in aps actions: [reply], intentIdentifiers: [])…What does provisional notification authorization give you, and why can an
.authorizeduser still see nothing?let center = UNUserNotificationCenter.current() func requestProvisional() async throws { // Day one: no prompt. Delivered quietly to Notification Center. let granted = try await center.requestAuthorization(options: [.alert, .sound, .badge, .provisional]) // granted is true immediately; the user saw nothing.…What is the difference between universal links and custom URL schemes, and what does a universal link need to open your app?
{ "applinks": { "details": [ { "appIDs": ["ABCDE12345.com.example.app"], "components": […Universal links open the right screen while the app is running, but after a cold start they land on the home screen. What is missing here?
final class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } window = UIWindow(windowScene: windowScene) window?.rootViewController = RootViewController() window?.makeKeyAndVisible() } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { guard let url = userActivity.webpageURL else { return } router.open(Route(url) ?? .home) }}How can iOS launch your app straight into the background, and what must
didFinishLaunchingavoid on that path?func application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Needed on every launch, visible or not. registerBackgroundTasks() UNUserNotificationCenter.current().delegate = self return true…After the user swipes your app away in the app switcher, which background wake-ups still reach it?
What is app prewarming, and how does it distort your launch-time metrics?
import UIKitimport os @MainActorenum LaunchClock { // Set by the system only when this process was started by prewarming.…What is
BGContinuedProcessingTaskin iOS 26, and when would you use it instead ofBGProcessingTask?import BackgroundTasks func startExport(_ video: Video) throws { // Info.plist lists com.example.app.export.* in BGTaskSchedulerPermittedIdentifiers let id = "com.example.app.export.\(video.id)"…