如何在SwiftUI类中正确访问AppDelegate?
问题:SwiftUI重构ObservableObject类时无法正确访问AppDelegate
在将遵循ObservableObject协议的类重构为使用@Observable宏标记的过程中,遇到了AppDelegate访问崩溃的问题。
相关代码定义
- 自定义AppDelegate:
class AppDelegate: NSObject, UIApplicationDelegate { var currentAuthorizationFlow: OIDExternalUserAgentSession? }
- 主App结构体通过
@UIApplicationDelegateAdaptor管理AppDelegate:
@main struct MyApp: App { @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate var body: some Scene { ... } }
- 重构前的AuthModel(虽不符合规范但能运行):
class AuthModel: ObservableObject { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate func login() async { await withCheckedContinuation { continuation in appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in continuation.resume() } } } }
- 重构后的AuthModel(运行时崩溃):
class AuthModel: ObservableObject { func login() async { await withCheckedContinuation { continuation in let appDelegate = UIApplication.shared.delegate as! AppDelegate appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in continuation.resume() } } } }
崩溃信息
运行时在let appDelegate = UIApplication.shared.delegate as! AppDelegate行崩溃,错误提示:Could not cast value of type 'SwiftUI.AppDelegate' (0x1ee8bafe8) to 'MyApp.AppDelegate' (0x1052a1c80)
补充场景
AuthModel通过@EnvironmentObject在多个视图中使用,例如登录视图:
struct LoginView: View { @EnvironmentObject var authModel: AuthModel var body: some View { Button { Task { await authModel.login() } } label: { Text("Log in") } .buttonStyle(.borderedProminent) } }
解决方案
方法1:依赖注入(推荐)
通过在初始化AuthModel时传入AppDelegate实例,实现解耦且避免类型转换风险,这是最符合SwiftUI设计理念的方式。
- 修改AuthModel,添加带参数的初始化方法:
class AuthModel: ObservableObject { private let appDelegate: AppDelegate // 通过初始化注入AppDelegate init(appDelegate: AppDelegate) { self.appDelegate = appDelegate } func login() async { await withCheckedContinuation { continuation in appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in continuation.resume() } } } }
- 在主App结构体中初始化AuthModel并注入AppDelegate,再通过
environmentObject传递给视图:
@main struct MyApp: App { @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate private let authModel: AuthModel init() { // 注入AppDelegate实例 authModel = AuthModel(appDelegate: appDelegate) } var body: some Scene { WindowGroup { LoginView() .environmentObject(authModel) } } }
方法2:获取SwiftUI包装后的实际AppDelegate
当使用@UIApplicationDelegateAdaptor时,UIApplication.shared.delegate实际是SwiftUI内部的代理对象,你的自定义AppDelegate被它持有在delegate属性中,可以通过以下方式获取:
修改AuthModel中的登录方法:
func login() async { await withCheckedContinuation { continuation in // 获取SwiftUI的代理包装器,再取出实际的AppDelegate let swiftUIDelegate = UIApplication.shared.delegate as! UIApplicationDelegateAdaptor<AppDelegate> let appDelegate = swiftUIDelegate.delegate appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in continuation.resume() } } }
这种方式不需要修改AuthModel的初始化逻辑,但依赖于SwiftUI的内部实现,相比依赖注入灵活性稍差。
内容的提问来源于stack exchange,提问作者Pieter
相关产品推荐
相关产品推荐

