I've been studying push notifications, specifically background push. I've followed Apple docs and various stackoverflow posts - Added Push Notifications capability, Remote Notifications in Background modes (as shown below),
registered with APNS, obtained a device token and implemented the application(_:didReceiveRemoteNotification:fetchCompletionHandler:) to receive background pushes.
// In AppDelegate.swift
static let sApp: UIApplication = UIApplication.shared
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Register with APNS for device token.
RegisterForRemoteNotifications()
return true;
}
func RegisterForRemoteNotifications() {
AppDelegate.sApp.registerForRemoteNotifications()
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
Log(String(format: "error.localizedDescription = %@", error.localizedDescription))
}
// Log(_:) is my custom log function.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken pDeviceToken: Data) {
// Print the device token as a string.
Log("pDeviceToken = " + pDeviceToken.hexEncodedString())
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
....
}
I don't have a server.. I'm using commandLine utilities to send background pushes as described here.
This is my curl command,
curl -v --header "apns-push-type:background" --header "apns-priority:5" --header "apns-topic: $TOPIC" --header "authorization: bearer $AUTHENTICATION_TOKEN" --data '{"aps":{"content-available":1},"Purpose":"Background update"}' --http2 https://${APNS_HOST_NAME}/3/device/${DEVICE_TOKEN}
When app is in foreground, I get background pushes without fail i.e., the application(_didReceiveRemoteNotiofication:fetchCompletionHandler:) is invoked to process the background push. But when app is not running, it doesn't work. When app is brought to foreground, the background push which was supposed to happen when app is not running, is now received. Same is when app is in suspended state.
I believe I have everything that is required for background pushes. All push notifications (including background pushes) are supposed to work even when app is not running. But in my case, it happens only when app is in foreground. What's the point of background push which never works when app is not running or in background? In foreground, app can just sync with server by itself. Background push is to update the content when app is not running.
What is missing here?
Environment: iOS 16.0 (real device), Xcode 14.2, phone battery = 100% (connected to Mac).
Update1: Few days after posting this question, the background notification are now waking up the app. Based on comments from Paulw11, I was convinced that this was not supposed to happen. But now, it happens... not consistently, but once in 3 times. Pasting the logs,
application(_:willFinishLaunchingWithOptions:)
...
RequestUserPermission() // Ask user permission to show notifications (not relevant for this post)
RegisterForRemoteNotifications() // Register with APNS
application(_:didFinishLaunchingWithOptions:)
...
CreateMainWindow()
...
application(_:didRegisterForRemoteNotificationsWithDeviceToken:) // Registration with APNS is successful
...
applicationDidBecomeActive(_:)
applicationWillResignActive(_:)
applicationDidEnterBackground(_:)
...
applicationwillTerminate(_:) // swiped the app from app switcher
// New launch due to background notification
application(_:willFinishLaunchingWithOptions:)
RequestUserPermission()
RegisterForRemoteNotifications()
application(_:didFinishLaunchingWithOptions:)
CreateMainWindow()
application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
...
application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
...