goh*_*tis 6 ios unusernotificationcenter
我有一个带有通知操作的应用程序。多年来,使用符合UNUserNotificationCenterDelegate其方法并处理其操作的类,一切都运行良好userNotificationCenter(_:didReceive:withCompletionHandler)。
最近,我重构了很多我的 app\xe2\x80\x99s 代码以使用 async/await。因此,我切换到委托方法的异步版本,userNotificationCenter(_:didReceive:) async它不再使用完成处理程序。
在发布使用该方法的异步版本的更新后,我开始在我的设备上和野外看到大量崩溃。这里\xe2\x80\x99s 是一个示例,删除了应用程序名称:
\nException Type: EXC_CRASH (SIGABRT)\nException Codes: 0x0000000000000000, 0x0000000000000000\nTriggered by Thread: 16\n\nApplication Specific Information:\nabort() called\n\n\nLast Exception Backtrace:\n0 CoreFoundation 0x197ba2248 __exceptionPreprocess + 164\n1 libobjc.A.dylib 0x190f63a68 objc_exception_throw + 60\n2 Foundation 0x19252281c _userInfoForFileAndLine + 0\n3 UIKitCore 0x19aa4fe94 -[UIApplication _performBlockAfterCATransactionCommitSynchronizes:] + 404\n4 UIKitCore 0x19aa5c20c -[UIApplication _updateStateRestorationArchiveForBackgroundEvent:saveState:exitIfCouldNotRestoreState:updateSnapshot:windowScene:] + 528\n5 UIKitCore 0x19aa5c4d0 -[UIApplication _updateSnapshotAndStateRestorationWithAction:windowScene:] + 144\n6 0x1006c36a8 @objc closure #1 in NotificationDelegate.userNotificationCenter(_:didReceive:) + 132\n7 0x1006c37d1 partial apply for @objc closure #1 in NotificationDelegate.userNotificationCenter(_:didReceive:) + 1\n8 0x10049d845 thunk for @escaping @callee_guaranteed @Sendable @async () -> () + 1\n9 0x1005ceb3d thunk for @escaping @callee_guaranteed @Sendable @async () -> ()partial apply + 1\n10 0x1005cea01 specialized thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out A) + 1\n11 0x1005cec75 partial apply for specialized thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out A) + 1\n12 libswift_Concurrency.dylib 0x1a1dce2d1 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) + 1\nRun Code Online (Sandbox Code Playgroud)\n根据测试,当对通知采取操作时会发生崩溃,例如点击通知打开应用程序(响应UNNotificationDefaultActionIdentifier)。
从委托方法中,我将中心和响应传递给异步处理的方法。为了缩小问题范围,我从我的processResponse(_:didReceive:) async方法中一次删除了一个部分,直到只剩下一条打印语句:
func userNotificationCenter(\n _ center: UNUserNotificationCenter,\n didReceive response: UNNotificationResponse)\nasync\n{\n await processResponse(center,\n response: response)\n}\n\nprivate func processResponse(_ center: UNUserNotificationCenter,\n response: UNNotificationResponse)\nasync\n{\n print("Do almost nothing...")\n}\nRun Code Online (Sandbox Code Playgroud)\n即使是这个最小的例子也会导致崩溃。我如何进一步解决此问题以使该userNotificationCenter(_:didReceive:) async方法正常工作而不会发生这些崩溃?
我设置通知的方式非常简单。在我的 AppDelegate 中,before 中didFinishLaunchingWithOptions,我将NotificationController(负责设置和调度通知)设置为惰性变量,然后将NotificationDelegate(负责处理操作)设置为惰性变量,以便我可以将NotificationController 传递到NotificationDelegate 中:
private lazy var notificationController =\n NotificationController(statsProvider: statsProvider)\n\nprivate lazy var notificationDelegate: NotificationDelegate =\n NotificationDelegate(\n notificationController: notificationController\n )\nRun Code Online (Sandbox Code Playgroud)\n然后,在 中didFinishLaunchingWithOptions,我将委托设置为UNUserNotificationCenter.current():
UNUserNotificationCenter.current().delegate = notificationDelegate\nRun Code Online (Sandbox Code Playgroud)\n我不认为任何设置是异常的,除非我缺少某些东西,所以我可以xe2x80x99t 看看几乎空的委托方法如何仍然崩溃。
\n为了单独说明该问题,我创建了一个演示应用程序来复制我所看到的崩溃。我想知道正确的使用方法userNotificationCenter(_:didReceive:) async委托方法而不崩溃的正确方法。
这个简单的应用程序在启动 5 秒后发送通知。点击通知打开应用程序本应在 5 秒后安排另一个通知,但应用程序崩溃了。
\n要复制此操作,请使用 UIKit 创建一个新的 iOS 应用程序项目并为其提供推送通知功能。这是 AppDelegate:
\nimport UIKit\n\n@main\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n private lazy var notificationController = NotificationController()\n\n private lazy var notificationDelegate = NotificationDelegate(\n notificationController: notificationController\n )\n\n func application(_ application: UIApplication,\n didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)\n -> Bool\n {\n UNUserNotificationCenter.current().delegate = notificationDelegate\n\n return true\n }\n\n // MARK: UISceneSession Lifecycle\n\n func application(_ application: UIApplication,\n configurationForConnecting connectingSceneSession: UISceneSession,\n options: UIScene.ConnectionOptions)\n -> UISceneConfiguration\n {\n // Called when a new scene session is being created.\n // Use this method to select a configuration to create the new scene with.\n return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n通知控制器:
\nimport UserNotifications\n\nclass NotificationController {\n\n init()\n {\n Task {\n await setUpPermissions()\n }\n }\n\n func scheduleNotification()\n async\n {\n let content = UNMutableNotificationContent()\n content.body = "Test notification"\n content.sound = .default\n\n let trigger = UNTimeIntervalNotificationTrigger(\n timeInterval: 5,\n repeats: false\n )\n\n let request = UNNotificationRequest(\n identifier: "Test",\n content: content,\n trigger: trigger\n )\n\n do {\n try await UNUserNotificationCenter.current()\n .add(request)\n }\n catch {\n print(error.localizedDescription)\n }\n }\n\n private func setUpPermissions()\n async\n {\n let authorizationStatus = await UNUserNotificationCenter.current()\n .notificationSettings()\n .authorizationStatus\n \n switch authorizationStatus {\n\n case .notDetermined:\n\n do {\n let granted = try await UNUserNotificationCenter.current()\n .requestAuthorization(options: [.sound, .alert])\n \n if granted\n {\n await scheduleNotification()\n }\n }\n catch { print(error.localizedDescription) }\n \n case .authorized:\n\n await scheduleNotification()\n\n default:\n break\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n通知委托:
\nimport UserNotifications\n\nclass NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {\n \n private let notificationController: NotificationController\n \n init(notificationController: NotificationController)\n {\n self.notificationController = notificationController\n }\n \n func userNotificationCenter(_ center: UNUserNotificationCenter,\n willPresent notification: UNNotification)\n async -> UNNotificationPresentationOptions\n {\n return [.banner, .sound]\n }\n \n func userNotificationCenter(_ center: UNUserNotificationCenter,\n didReceive response: UNNotificationResponse)\n async\n {\n print("didReceive response")\n \n if response.actionIdentifier == UNNotificationDefaultActionIdentifier\n {\n await notificationController.scheduleNotification()\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n请注意,如果不尝试安排另一个通知,这甚至会崩溃:
\nfunc userNotificationCenter(_ center: UNUserNotificationCenter,\n didReceive response: UNNotificationResponse)\nasync\n{\n print("didReceive response")\n}\nRun Code Online (Sandbox Code Playgroud)\n如何userNotificationCenter(_:didReceive:) async正确使用它以避免崩溃?
小智 16
我有同样的问题。
看起来断言失败是由在后台线程上调用的方法引起的。
如果您用它注释委托方法@MainActor可以解决问题:
@MainActor
public func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse
) async {
// implementation
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1294 次 |
| 最近记录: |