Notification Service Extension is killed during startup

We are observing an issue where the iOS Notification Service Extension (NSE) is terminated by the system during startup, before either didReceive(_:withContentHandler:) or serviceExtensionTimeWillExpire(_:) is invoked. When this occurs, the notification is delivered without modification (for example, an encrypted payload is shown to the user). System logs frequently contain the message “Extension will be killed because it used its runtime in starting up”.

During testing, we observed that CPU-intensive operations or heavy initialization performed early in the extension lifecycle — especially inside init() or directly on the main thread in didReceive often cause the system to kill the NSE almost immediately. These terminations happen significantly earlier than the commonly observed ~30-second execution window where the OS normally invokes serviceExtensionTimeWillExpire(_:) before ending the extension. When these early terminations occur, there is no call to the expiry handler, and the process appears to be forcefully shut down.

Moving the same operations to a background thread changes the behavior: the extension eventually expires around the usual 30-second window, after which the OS calls serviceExtensionTimeWillExpire(_:).

We also observed that memory usage plays a role in early termination. During tests involving large memory allocations, the system consistently killed the extension once memory consumption exceeded a certain threshold (in our measurements, this occurred around 150–180 MB). Again, unlike normal time-based expiration, the system did not call the expiry handler and no crash report was produced.

Since Apple’s documentation does not specify concrete CPU, memory, or startup-cost constraints for Notification Service Extensions or any other extensions beyond the general execution limit, we are seeking clarification and best-practice guidance on expected behaviors, particularly around initialization cost and the differences between startup termination.

NSE Setup:

class NotificationService: UNNotificationServiceExtension {

    static var notificationContentHandler: ((UNNotificationContent) -> Void)?
    static var notificationContent: UNMutableNotificationContent?
    static var shoudLoop = true

    override func didReceive(_ request: UNNotificationRequest,
                             withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {

        NotificationService.notificationContentHandler = contentHandler
        NotificationService.notificationContent =
            request.content.mutableCopy() as? UNMutableNotificationContent

        NotificationService.notificationContent!.title = "Weekly meeting"
        NotificationService.notificationContent!.body = "Updated inside didReceive"
        
        // Failing scenarios
    }

    override func serviceExtensionTimeWillExpire() {
        NotificationService.shoudLoop = false

        guard let handler = NotificationService.notificationContentHandler,
              let content = NotificationService.notificationContent else { return }

        content.body = "Updated inside serviceExtensionTimeWillExpire()"
        handler(content)
    }
}
Notification Service Extension is killed during startup
 
 
Q