From dd8f51e0657c81a2a27b15ae8e2c7e83f2b2ff28 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 3 Feb 2026 11:04:32 +0000 Subject: [PATCH] Fix notification delivery after powering on a device. (#5035) * Debugging with Notifications. * Don't run the NSE until the device is unlocked. * Let the NSE run long enough to deliver the default notification content. Turns out that exit(0) results in the notification be discarded when you have the notification filtering entitlement. * Remove debugging notifications. --------- Co-authored-by: Stefan Ceriu --- .../Sources/Application/AppDelegate.swift | 6 +-- .../NotificationServiceExtension.swift | 43 ++++++++++++++----- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/ElementX/Sources/Application/AppDelegate.swift b/ElementX/Sources/Application/AppDelegate.swift index ac9d2d06d..51d6f92b3 100644 --- a/ElementX/Sources/Application/AppDelegate.swift +++ b/ElementX/Sources/Application/AppDelegate.swift @@ -29,15 +29,15 @@ final class AppDelegate: NSObject, UIApplicationDelegate { NSTextAttachment.registerViewProviderClass(PillAttachmentViewProvider.self, forFileType: InfoPlistReader.main.pillsUTType) return true } - + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { callbacks.send(.registeredNotifications(deviceToken: deviceToken)) } - + func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { callbacks.send(.failedToRegisteredNotifications(error: error)) } - + func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { orientationLock } diff --git a/NSE/Sources/NotificationServiceExtension.swift b/NSE/Sources/NotificationServiceExtension.swift index 37f6735b2..efd175af7 100644 --- a/NSE/Sources/NotificationServiceExtension.swift +++ b/NSE/Sources/NotificationServiceExtension.swift @@ -51,7 +51,12 @@ class NotificationServiceExtension: UNNotificationServiceExtension { appHooks = AppHooks() appHooks.setUp() - if Self.targetConfiguration == nil { + // If the device is still locked then we can't write to the app group container and + // the target configuration will fail. We could call exit(0) here, however with the + // notification filtering entitlement that results in the notification being discarded + // so we need to wait for the delegate method to be called and bail out there instead. + if !DataProtectionManager.isDeviceLockedAfterReboot(containerURL: URL.appGroupContainerDirectory), + Self.targetConfiguration == nil { Self.targetConfiguration = Target.nse.configure(logLevel: settings.logLevel, traceLogPacks: settings.traceLogPacks, sentryURL: nil, @@ -67,16 +72,32 @@ class NotificationServiceExtension: UNNotificationServiceExtension { } private func handle(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) async { - guard !DataProtectionManager.isDeviceLockedAfterReboot(containerURL: URL.appGroupContainerDirectory), - let roomID = request.content.roomID, - let eventID = request.content.eventID, - let clientID = request.content.pusherNotificationClientIdentifier, - let credentials = keychainController.restorationTokens().first(where: { $0.restorationToken.pusherNotificationClientIdentifier == clientID }) else { - // We cannot process this notification, it might be due to one of these: - // - Device rebooted and locked - // - Not a Matrix notification - // - User is not signed in - // - NotificationID could not be resolved + // If we skipped configuring the target it means we can't write to the app group, so we're unlikely + // to be able to create a session (and even if we could, we would be missing the lightweightTokioRuntime), + // so instead lets deliver the default generic notification and avoid attempting to process the notification. + guard Self.targetConfiguration != nil else { + // swiftlint:disable:next print_deprecation (MXLog isn't configured) + print("Device is locked after reboot, delivering the unmodified notification.") + return contentHandler(request.content) + } + + guard let roomID = request.content.roomID else { + MXLog.error("Invalid roomID, bailing out: \(request.content)") + return contentHandler(request.content) + } + + guard let eventID = request.content.eventID else { + MXLog.error("Invalid eventID, bailing out: \(request.content)") + return contentHandler(request.content) + } + + guard let clientID = request.content.pusherNotificationClientIdentifier else { + MXLog.error("Invalid eventID, bailing out: \(request.content)") + return contentHandler(request.content) + } + + guard let credentials = keychainController.restorationTokens().first(where: { $0.restorationToken.pusherNotificationClientIdentifier == clientID }) else { + MXLog.error("Invalid credentials, bailing out: \(request.content)") return contentHandler(request.content) }