SwiftUI中在@Observable类访问自定义AppDelegate时遭遇类型转换崩溃的问题求助
兄弟,我一眼就看出你踩了SwiftUI里@UIApplicationDelegateAdaptor的一个经典小坑!咱们先把问题根源捋清楚,再给你俩靠谱的解决办法。
你遇到的崩溃提示Could not cast value of type 'SwiftUI.AppDelegate' to 'MyApp.AppDelegate',本质原因是SwiftUI并没有把你自定义的AppDelegate直接设置为UIApplication的全局delegate。当你用@UIApplicationDelegateAdaptor的时候,SwiftUI会偷偷创建一个自己的内部AppDelegate代理类(就是错误里的SwiftUI.AppDelegate),然后把你的自定义Delegate作为子代理来调用生命周期方法——这就是为什么你能看到AppDelegate的打印语句,但强转的时候会崩溃:UIApplication.shared.delegate指向的是SwiftUI的内部类,不是你自己写的那个!
另外还要先纠正你代码里的一个小细节:你自定义的Delegate类叫CustomAppDelegate,但在@UIApplicationDelegateAdaptor里写的是AppDelegate.self,这会导致SwiftUI找不到你的自定义类,要么报错,要么自动生成一个默认的AppDelegate,这也会引发类型不匹配的问题。一定要确保@UIApplicationDelegateAdaptor里指定的类名和你自定义的Delegate类名完全一致!
接下来给你两个可行的解决办法:
方法一:通过依赖传递访问(符合SwiftUI设计理念)
这种方法是最贴合SwiftUI数据流转逻辑的,不用绕全局的UIApplication,直接把自定义AppDelegate传递给需要用到它的模型:
- 先修正
MyApp里的Adaptor类名,把自定义Delegate传进ContentView:
@main struct MyApp: App { // 这里要和你自定义的Delegate类名一致,比如你写的是CustomAppDelegate就用这个 @UIApplicationDelegateAdaptor(CustomAppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView(appDelegate: appDelegate) } } }
- 在
ContentView里把AppDelegate传给你的MyModel:
struct ContentView: View { let appDelegate: CustomAppDelegate @State private var model: MyModel init(appDelegate: CustomAppDelegate) { self.appDelegate = appDelegate self._model = State(initialValue: MyModel(appDelegate: appDelegate)) } var body: some View { // 你的视图内容 } }
- 修改
MyModel,通过构造方法接收AppDelegate:
@MainActor @Observable class MyModel { private let appDelegate: CustomAppDelegate init(appDelegate: CustomAppDelegate) { self.appDelegate = appDelegate } func login() async { await withCheckedContinuation { continuation in appDelegate.currentAuthorizationFlow = OIDAuthState.authState() // 这里一定要调用resume()!你原来的代码漏了,不然异步任务会永远挂起 continuation.resume() } } }
方法二:给自定义AppDelegate加静态共享实例(全局访问)
如果你实在需要全局调用的方式,可以给自定义AppDelegate加一个静态共享实例,这样不用强转就能直接拿到:
- 修改你的
CustomAppDelegate,添加静态shared属性并在启动时初始化:
class CustomAppDelegate: NSObject, UIApplicationDelegate { static var shared: CustomAppDelegate! var currentAuthorizationFlow: OIDExternalUserAgentSession? func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { print("AppDelegate: App has finished launching") // 把当前实例赋值给静态属性,方便全局调用 CustomAppDelegate.shared = self return true } }
- 然后在
MyModel里直接用静态实例,完全不用碰UIApplication的delegate:
@MainActor @Observable class MyModel { func login() async { await withCheckedContinuation { continuation in CustomAppDelegate.shared.currentAuthorizationFlow = OIDAuthState.authState() // 同样别忘了调用resume() continuation.resume() } } }
最后再提个醒:你原来的代码里withCheckedContinuation没有调用continuation.resume(),这会导致你的异步任务永远挂起,这个一定要补上,不然登录逻辑会直接卡死。
内容来源于stack exchange

