Post

Replies

Boosts

Views

Activity

Reply to Notification Service Extension is killed during startup
Thank you for the clarification about the 24 MB memory limit and jetsam-based termination for Notification Service Extensions. I wanted to share two specific behaviors I'm observing in our extension related to heavy CPU work, because both scenarios produce different outcomes. Scenario 1 — Heavy CPU loop on a background thread DispatchQueue.global(qos: .userInitiated).async { var sum: Int = 0 for i in 0..<5_000_000_000 { sum += i if i % 100_000_000 == 0 { Log(String(format: "Scenario: Progress i=%d", i)) } if !NotificationService.shoudLoop { break } } NotificationService.notificationContent!.title = "[Scenario Finished]" NotificationService.notificationContent!.body = "CPU-intensive work done" NotificationService.notificationContentHandler!(NotificationService.notificationContent!) } Observation: In this case, the Notification Service Extension stays alive long enough for didReceive(_:withContentHandler:) to complete. The notification text is successfully decrypted and displayed. As for the logs, of the Progress of the loop on the background thread, it gets executed till around i=100_000_000 and after that no logs are printed for the loop, as process gets terminated. Scenario 2 — The same CPU loop but on the main thread Log("Scenario 2A: Blocking main thread in didReceive") var sum = 0 for i in 0..<5_000_000_000 { sum += i if i % 100_000_000 == 0 { Log(String(format: "Scenario 2A: Progress i=%d", i)) } if !NotificationService.shoudLoop { break } } NotificationService.notificationContent!.title = "[Scenario 2 on main thread Finished]" NotificationService.notificationContent!.body = "CPU-intensive work done" NotificationService.notificationContentHandler!(NotificationService.notificationContent!) Observation: When this same long-running loop runs directly on the main thread, the extension becomes unresponsive. The system logs show messages such as: tearing down context in extension due to invalidation [xpcservice...] [xpcservice<world.tally.IOSPushNotifications.NotificationServiceExtension([osservice<com.apple.SpringBoard>:34])>{vt hash: 0}:1962] Set jetsam priority to 0 [0] flag[1] The extension is terminated immediately, and the notification appears with the original encrypted payload — meaning didReceive never got a chance to complete. The expiry callback (serviceExtensionTimeWillExpire()) is also never invoked. Could you help explain the reason behind the different behaviours when the work runs on the main thread versus a background thread? Are there any recommended best practices for what type of work should (or should not) run on the main thread inside a Notification Service Extension? Also, if an extension needs to perform some initialisation work, are there guidelines on where that work should be placed to avoid premature termination?
3w
Reply to Notification Service Extension not getting invoked on macOS
Thanks for the detailed response. To clarify and add some details about my testing setup and observations: Verification that the extension isn’t being triggered: I’ve confirmed that the Notification Service Extension (NSE) is not being invoked at all and not just failing to modify the content with the following pointers: The didReceive(_:withContentHandler:) and serviceExtensionTimeWillExpire() methods never log or execute (I’ve added multiple test print/log statements and file-based logs for confirmation). The extension process itself does not appear in Console.app or in the process list. On iOS, using the exact same payload and code, I can see clear logs from the extension when it runs — so the extension logic and payload are validated there. Console logs checked: I’ve filtered the macOS Console for both the main app bundle ID and the extension’s bundle ID. There are no messages indicating the NSE being launched, terminated, or crashing. The system only logs the standard “Received remote notification” message for the main app’s bundle ID. Extension setup: The NSE target is correctly added to the same macOS app project. It uses the standard UNNotificationServiceExtension class. The extension has a valid Info.plist with the correct NSExtension dictionary (NSExtensionPointIdentifier = com.apple.usernotifications.service). Code signing and entitlements match across the app and extension. The app requests and receives remote notification permissions normally. Payload details: The remote notification payload contains "mutable-content": 1 inside the aps dictionary: { "aps": { "alert": { "title": "Meeting Reminder", "body": "Join the weekly sync call" }, "mutable-content": 1 }, "MEETING_ORGANIZER": "Alex Johnson" } Behavior summary: On iOS → NSE launches and modifies the notification title (confirmed via logs and on-device display). On macOS → Notification displays as sent, but no logs, no extension process, and no indication that the NSE was invoked, while remote notifications behavior is working as expected. If there is any supported way to intercept or modify remote notification content before display on macOS (e.g., using a different extension type, entitlement, or API path), that would also be very helpful to know? Or any other way to check if the NSE/NCE is getting invoked. Do tell me if you need any details or the full project.
Nov ’25