You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Swift 6迁移后AppDelegate中Firebase推送通知启动崩溃问题求助

Swift 6迁移后AppDelegate中Firebase推送通知启动崩溃问题求助

看起来你遇到了Swift 6迁移后Firebase推送相关的启动崩溃问题,我来帮你梳理下可能的原因和解决办法:

问题根源分析

你提到注释掉UNUserNotificationCenter.current().delegate = self和权限请求代码后崩溃消失,结合Swift 6的严格并发规则以及你的代码细节,主要问题可能出在两个地方:

  1. @AppStorage在非View类中的错误使用@AppStorage是专门为SwiftUI View设计的属性包装器,在AppDelegate这种UIKit类中直接使用会因为初始化顺序、线程隔离问题导致崩溃,尤其是Swift 6对并发检查更严格。
  2. 代理方法的错误隔离修饰:你给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

火山引擎 最新活动