Swift 6迁移后AppDelegate中Firebase推送通知启动崩溃问题求助
Swift 6迁移后AppDelegate中Firebase推送通知启动崩溃问题求助
看起来你遇到了Swift 6迁移后Firebase推送相关的启动崩溃问题,我来帮你梳理下可能的原因和解决办法:
问题根源分析
你提到注释掉UNUserNotificationCenter.current().delegate = self和权限请求代码后崩溃消失,结合Swift 6的严格并发规则以及你的代码细节,主要问题可能出在两个地方:
- @AppStorage在非View类中的错误使用:
@AppStorage是专门为SwiftUI View设计的属性包装器,在AppDelegate这种UIKit类中直接使用会因为初始化顺序、线程隔离问题导致崩溃,尤其是Swift 6对并发检查更严格。 - 代理方法的错误隔离修饰:你给
userNotificationCenter代理方法加了nonisolated修饰符,但AppDelegate在Swift 6中默认是@MainActor隔离的,nonisolated会强制方法脱离主Actor,而推送通知的代理方法必须在主线程执行,这会引发线程安全问题。
具体修改方案
1. 替换@AppStorage为UserDefaults直接操作
把AppDelegate中所有@AppStorage属性换成UserDefaults的直接读写,避免SwiftUI专属API的兼容性问题:
// 替换原来的@AppStorage("whatWas") var whatWas: String { get { UserDefaults.standard.string(forKey: "whatWas") ?? "" } set { UserDefaults.standard.set(newValue, forKey: "whatWas") } } // 在messaging代理方法中替换@AppStorage("notifToken") func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { print("Firebase registration token: \(String(describing: fcmToken))") guard let fcmToken = fcmToken else { return } let currentToken = UserDefaults.standard.string(forKey: "notifToken") ?? "" if fcmToken != currentToken { UserDefaults.standard.set(fcmToken, forKey: "notifToken") Task { @MainActor in await Functions.lastOnline() } } let dataDict: [String: String] = ["token": fcmToken] NotificationCenter.default.post( name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict ) }
2. 移除代理方法的nonisolated修饰符
AppDelegate默认是@MainActor隔离的,推送通知的代理方法需要在主线程执行,所以去掉nonisolated修饰符,让方法运行在正确的线程上下文:
// 修正后的willPresent方法 func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { completionHandler([.banner, .badge]) } // 修正后的didReceive response方法 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo let notifType = userInfo["notifType"] as? String if notifType == "comment" || notifType == "reply" { guard let meme = userInfo["meme"] as? String, let memeid = userInfo["memeID"] as? String, let size = userInfo["size"] as? String, let title = userInfo["memeTitle"] as? String else { completionHandler() return } var fin = meme + "||" + memeid + "||" + size fin += title.isEmpty ? "||null" : "||" + title Task { @MainActor in self.whatWas = fin } } completionHandler() }
3. 确保权限请求的闭包在主Actor执行
在请求通知权限的闭包前加上@MainActor,避免Swift 6的并发警告或崩溃:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { @MainActor (granted, error) in if let error = error { print("Notification authorization error: \(error.localizedDescription)") } // 可在这里处理权限授予结果 }
额外建议
- 在Swift 6中,所有UI相关操作必须在
@MainActor上执行,尽量用Task { @MainActor in }包裹UI或状态修改代码。 - 避免在非SwiftUI View类中使用SwiftUI专属属性包装器(如
@AppStorage、@Environment),改用Foundation原生API。 - 可以在Xcode Build Settings中开启「Strict Concurrency Checking」,提前发现潜在的并发问题。
内容来源于stack exchange




