From 03796606aa3371d2bfc2c3373a7201c471de701b Mon Sep 17 00:00:00 2001 From: SamGu-NRX Date: Wed, 7 Oct 2026 07:34:26 -0500 Subject: [PATCH 1/2] fix(ios): read notification permission without sending UNNotificationSettings Xcode 16's SDK doesn't mark UNNotificationSettings Sendable, so awaiting notificationSettings() from the main actor failed the Swift 6 build in CI. Read the settings through the callback API and return only the mapped permission status. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../Feature/Shared/AppPermissionCenter.swift | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/ios/Feature/Shared/AppPermissionCenter.swift b/apps/ios/Feature/Shared/AppPermissionCenter.swift index 84991f4d..d85d09e5 100644 --- a/apps/ios/Feature/Shared/AppPermissionCenter.swift +++ b/apps/ios/Feature/Shared/AppPermissionCenter.swift @@ -41,8 +41,18 @@ enum AppPermissionCenter { @MainActor static func notificationStatus() async -> AppPermissionStatus { - let settings = await UNUserNotificationCenter.current().notificationSettings() - return mapNotificationSettings(settings) + await currentNotificationStatus() + } + + /// Maps inside the callback so only the Sendable status crosses actors. Xcode 16's SDK + /// doesn't mark `UNNotificationSettings` Sendable, so awaiting `notificationSettings()` from + /// the main actor fails the Swift 6 build there. + private static func currentNotificationStatus() async -> AppPermissionStatus { + await withCheckedContinuation { continuation in + UNUserNotificationCenter.current().getNotificationSettings { settings in + continuation.resume(returning: mapNotificationSettings(settings)) + } + } } @MainActor @@ -102,8 +112,7 @@ enum AppPermissionCenter { } case .notifications: - let settings = await UNUserNotificationCenter.current().notificationSettings() - switch mapNotificationSettings(settings) { + switch await currentNotificationStatus() { case .authorized, .limited: return .granted case .denied, .restricted: From 35d583008859eef65344084ffdd5e541c7879d65 Mon Sep 17 00:00:00 2001 From: SamGu-NRX Date: Wed, 7 Oct 2026 07:38:26 -0500 Subject: [PATCH 2/2] fix(ios): pass pending notification identifiers instead of requests The scheduler only needs identifiers, and Xcode 16's SDK doesn't mark UNNotificationRequest Sendable, so returning requests to the scheduler actor failed the Swift 6 build in CI. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../Notifications/NotificationScheduler.swift | 5 +---- .../Notifications/UserNotificationCenterClient.swift | 12 +++++++++--- apps/ios/Tests/NotificationSchedulerTests.swift | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/ios/Platform/Notifications/NotificationScheduler.swift b/apps/ios/Platform/Notifications/NotificationScheduler.swift index adfb35c8..eacf7752 100644 --- a/apps/ios/Platform/Notifications/NotificationScheduler.swift +++ b/apps/ios/Platform/Notifications/NotificationScheduler.swift @@ -19,10 +19,7 @@ actor NotificationScheduler { permissionStatus: AppPermissionStatus ) async throws { let knownMealIdentifiers = NotificationRuleKind.orderedMealKinds.map(\.notificationIdentifier) - let pending = await center.pendingNotificationRequests() - let staleFreshnessIdentifiers = - pending - .map(\.identifier) + let staleFreshnessIdentifiers = await center.pendingNotificationIdentifiers() .filter { $0.hasPrefix("freshness.") } await center.removePendingNotificationRequests( diff --git a/apps/ios/Platform/Notifications/UserNotificationCenterClient.swift b/apps/ios/Platform/Notifications/UserNotificationCenterClient.swift index b22d5545..5cae5a7a 100644 --- a/apps/ios/Platform/Notifications/UserNotificationCenterClient.swift +++ b/apps/ios/Platform/Notifications/UserNotificationCenterClient.swift @@ -1,15 +1,21 @@ import Foundation import UserNotifications +/// Returns pending identifiers rather than `UNNotificationRequest`s: Xcode 16's SDK doesn't +/// mark the requests Sendable, so returning them to the scheduler actor fails the Swift 6 build. protocol UserNotificationCenterClient: Sendable { - func pendingNotificationRequests() async -> [UNNotificationRequest] + func pendingNotificationIdentifiers() async -> [String] func add(_ request: UNNotificationRequest) async throws func removePendingNotificationRequests(withIdentifiers identifiers: [String]) async } struct SystemUserNotificationCenterClient: UserNotificationCenterClient { - func pendingNotificationRequests() async -> [UNNotificationRequest] { - await UNUserNotificationCenter.current().pendingNotificationRequests() + func pendingNotificationIdentifiers() async -> [String] { + await withCheckedContinuation { continuation in + UNUserNotificationCenter.current().getPendingNotificationRequests { requests in + continuation.resume(returning: requests.map(\.identifier)) + } + } } func add(_ request: UNNotificationRequest) async throws { diff --git a/apps/ios/Tests/NotificationSchedulerTests.swift b/apps/ios/Tests/NotificationSchedulerTests.swift index e2ba4ab6..2088015b 100644 --- a/apps/ios/Tests/NotificationSchedulerTests.swift +++ b/apps/ios/Tests/NotificationSchedulerTests.swift @@ -127,8 +127,8 @@ final class MockNotificationCenter: @unchecked Sendable, UserNotificationCenterC self.pendingRequests = pendingRequests } - func pendingNotificationRequests() async -> [UNNotificationRequest] { - pendingRequests + func pendingNotificationIdentifiers() async -> [String] { + pendingRequests.map(\.identifier) } func add(_ request: UNNotificationRequest) async throws {