Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions apps/ios/Feature/Shared/AppPermissionCenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
5 changes: 1 addition & 4 deletions apps/ios/Platform/Notifications/NotificationScheduler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions apps/ios/Tests/NotificationSchedulerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading