应用关闭时点击UNNotificationAction如何保存数据到UserDefaults?
问题:应用完全关闭时点击通知操作无法保存数据到UserDefaults
应用处于前台或后台状态时,userNotificationCenter(_:didReceive:withCompletionHandler:)能正常触发,数据可成功保存至UserDefaults;但应用完全关闭时,点击通知的操作按钮,数据无法保存,无任何响应。
相关代码
通知注册代码:
UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound, .criticalAlert] UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { (granted, error) in if granted { } }) let PKW = UNNotificationAction(identifier: "PKW", title: "Komme per PKW", options: .foreground) let Fuß = UNNotificationAction(identifier: "Fuß", title: "Komme per Fuß", options: .foreground) let Rad = UNNotificationAction(identifier: "Rad", title: "Komme per Rad", options: .foreground) let nicht = UNNotificationAction(identifier: "Nicht", title: "Komme per Komme nicht", options: .foreground) let category = UNNotificationCategory(identifier: "ComeBy", actions: [PKW, Fuß, Rad, nicht], intentIdentifiers: [], options: []) UNUserNotificationCenter.current().setNotificationCategories([category]) application.registerForRemoteNotifications()
通知代理方法:
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent: UNNotification, withCompletionHandler: @escaping (UNNotificationPresentationOptions)->()) { withCompletionHandler([.alert, .sound, .badge]) } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler: @escaping ()->()) { let eventData = response.actionIdentifier switch eventData { case "PKW", "Fuß", "Rad", "Nicht": UserDefaults.standard.set(eventData, forKey: prefixedKey(UserDefaultsKeys.comeBy.key)) default: break } withCompletionHandler() }
问题原因分析
- 代理设置时机过晚:应用完全关闭后冷启动时,系统会先触发通知回调,若
UNUserNotificationCenter的代理在回调触发后才设置,didReceive方法就不会被调用。 - 进程提前终止:冷启动时,若未确保数据写入完成就调用
withCompletionHandler(),系统可能在UserDefaults写入完成前终止应用进程,导致数据丢失。 - 推送payload不匹配:若推送通知的payload未指定正确的
category字段,系统无法匹配到对应的操作按钮,回调不会触发。
解决方案
1. 提前设置通知中心代理
将代理设置代码移至application(_:didFinishLaunchingWithOptions:)的最开头,确保系统触发回调前代理已生效:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 优先设置代理 UNUserNotificationCenter.current().delegate = self // 后续的授权、类别注册、远程通知注册逻辑 let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound, .criticalAlert] UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in if granted { // 授权成功逻辑 } } let PKW = UNNotificationAction(identifier: "PKW", title: "Komme per PKW", options: .foreground) let Fuß = UNNotificationAction(identifier: "Fuß", title: "Komme per Fuß", options: .foreground) let Rad = UNNotificationAction(identifier: "Rad", title: "Komme per Rad", options: .foreground) let nicht = UNNotificationAction(identifier: "Nicht", title: "Komme per Komme nicht", options: .foreground) let category = UNNotificationCategory(identifier: "ComeBy", actions: [PKW, Fuß, Rad, nicht], intentIdentifiers: [], options: []) UNUserNotificationCenter.current().setNotificationCategories([category]) application.registerForRemoteNotifications() return true }
2. 确保数据写入完成后调用completionHandler
在冷启动场景下,可通过synchronize()确保UserDefaults数据写入完成(官方虽不推荐,但冷启动时可临时使用),再调用withCompletionHandler():
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler: @escaping ()->()) { let eventData = response.actionIdentifier switch eventData { case "PKW", "Fuß", "Rad", "Nicht": UserDefaults.standard.set(eventData, forKey: prefixedKey(UserDefaultsKeys.comeBy.key)) // 确保数据写入完成 UserDefaults.standard.synchronize() default: break } // 所有操作完成后再调用completionHandler withCompletionHandler() }
3. 验证推送payload的category字段
确保推送通知的payload中指定了正确的category值"ComeBy",示例payload:
{ "aps": { "alert": "Test Notification", "category": "ComeBy", "sound": "default" } }
内容的提问来源于stack exchange,提问作者lawayben
相关产品推荐
相关产品推荐

