SwiftUI应用已推送本地通知未在通知中心显示的解决方法
我开发了一款SwiftUI应用,创建了一个NotificationCenter结构体用于推送本地通知,核心函数代码如下:
@MainActor struct NotificationCenter { // 从通知中心移除已删除的项目 // 推送所有新增和修改的项目到通知中心 // 让通知中心更新已修改的项目 static func pushNotifications(_ items: [Item]) async throws { let center = UNUserNotificationCenter.current() let items = Set(items) let pending: Set<Item> = Set((await center.pendingNotificationRequests()).map{ Item(identifier: $0.identifier, notificationContent: $0.content) }) let toBeRemovedPendingItems = pending.filter{ !items.map{ $0.id }.contains($0.id) } let pushed: Set<Item> = Set((await center.deliveredNotifications()).map{ Item(identifier: $0.request.identifier, notificationContent: $0.request.content) }) let toBeRemovedPushedItems = pushed.filter{ !items.map{ $0.id }.contains($0.id) } let toBePushedItems = items .subtracting(toBeRemovedPendingItems) .subtracting((pushed.subtracting(toBeRemovedPushedItems))) print(pushed) print(toBePushedItems) center.removePendingNotificationRequests(withIdentifiers: toBeRemovedPendingItems.map{ $0.id.uuidString }) center.removeDeliveredNotifications(withIdentifiers: toBeRemovedPushedItems.map{ $0.id.uuidString }) for item in toBePushedItems { try await center.add(item.notificationRequest) } UIApplication.shared.applicationIconBadgeNumber = (await center.deliveredNotifications()).count } }
pushNotifications函数会在应用scenePhase变为.inactive时被调用。
我在主SwiftUI App结构体中使用@UIApplicationDelegateAdaptor(AppDelegate.self)记录通知到达情况,并在应用打开时显示通知。目前通知到达日志记录正常,print(pushed)输出和应用图标角标设置都显示推送的通知已存在于通知中心,但通知并未在系统通知中心显示。模拟器和iPhone真机测试均出现该异常。
相关补充代码:
extension Item { var notificationRequest: UNNotificationRequest { let content = UNMutableNotificationContent() content.title = "\(name ?? "(nil)") (\(type ?? "(nil)"))" content.body = "Expiring on \(dateFormatter.string(from: date))" // 试过用和不用trigger两种情况 return UNNotificationRequest(identifier: id.uuidString, content: content, trigger: nil) } }
1. 确认通知权限已正确获取
确保应用已获得用户授予的通知显示权限,仅请求权限不够,必须用户明确允许。可以在App启动时添加权限请求代码:
func requestNotificationPermissions() async { let center = UNUserNotificationCenter.current() let isGranted = await center.requestAuthorization(options: [.alert, .badge, .sound]) guard isGranted else { print("用户未授予通知权限") return } }
可将该方法放在@main结构体的init或视图的onAppear中调用。
2. 修正trigger的错误用法
当创建UNNotificationRequest时传入trigger: nil,通知会直接被标记为已送达(进入deliveredNotifications),但系统不会弹出通知提示。如果需要立即显示通知,应使用UNTimeIntervalNotificationTrigger设置一个极短延迟(比如0.1秒),触发系统展示通知:
extension Item { var notificationRequest: UNNotificationRequest { let content = UNMutableNotificationContent() content.title = "\(name ?? "(nil)") (\(type ?? "(nil)"))" content.body = "Expiring on \(dateFormatter.string(from: date))" // 添加短延迟触发器,确保系统弹出通知 let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false) return UNNotificationRequest(identifier: id.uuidString, content: content, trigger: trigger) } }
3. 确保Item正确实现Hashable协议
当前代码中Set的比较逻辑依赖Item的Hashable实现,如果协议实现不正确,会导致subtracting等操作失效,出现重复添加或错误过滤通知的情况。示例实现如下:
struct Item: Hashable { let id: UUID var name: String? var type: String? var date: Date // 仅以唯一标识id作为哈希和相等判断依据 func hash(into hasher: inout Hasher) { hasher.combine(id) } static func == (lhs: Item, rhs: Item) -> Bool { return lhs.id == rhs.id } }
4. 调整通知推送的时机
当应用从活跃状态转为.inactive时,系统可能限制通知的即时展示。可以尝试将推送逻辑延迟到应用进入.background状态时执行,或者提前在应用活跃阶段完成通知调度。
5. 检查设备通知设置
在设备的「设置-通知-你的应用」中,确认以下选项已开启:
- 允许通知
- 已勾选通知样式(横幅/提醒)
- 已开启锁定屏幕/通知中心/横幅的显示权限
内容的提问来源于stack exchange,提问作者Austin

