In my app I have an AppDelegate
with a field that I need to access. When I try to access the AppDelegate
from an @Observable
annotated class I get an error at runtime:
Could not cast value of type 'SwiftUI.AppDelegate' to 'MyApp.AppDelegate'.
All documentation and answers I can find online seem to suggest that what I'm doing should work. What am I doing wrong?
My AppDelegate
.
class CustomAppDelegate: NSObject, UIApplicationDelegate {
var currentAuthorizationFlow: OIDExternalUserAgentSession? // The field I need to access
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
print("AppDelegate: App has finished launching") // This does get printed at startup
return true
}
}
The main
of my app.
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
I've also tried using @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
in the main
. The print from the AppDelegate
fires then but I still get the same crash.
How I try to access the AppDelegate
.
@MainActor
@Observable
class MyModel {
func login() async {
// The app crashes here at runtime
let appDelegate = UIApplication.shared.delegate as! AppDelegate
await withCheckedContinuation { continuation in
appDelegate.currentAuthorizationFlow = OIDAuthState.authState()
}
}
}
Even with a UIApplicationDelegateAdaptor
, UIApplication.shared.delegate
is still going to be a delegate implementation internal to SwiftUI. UIApplicationDelegateAdaptor
simply forwards delegate calls to your own delegate.
You can access your own delegate via an @EnvironmentObject
in a view, if you conform your app delegate to ObservableObject
. You can have login
take the delegate as a parameter, and pass it from a view.
class CustomAppDelegate: NSObject, UIApplicationDelegate, ObservableObject { ... }
struct ContentView: View {
@EnvironmentObject var delegate: CustomAppDelegate
@State private var model = MyModel()
var body: some View {
// ...
await model.login(delegate: delegate)
// ...
}
}
@MainActor
@Observable
class MyModel {
func login(delegate: CustomAppDelegate) async {
await withCheckedContinuation { continuation in
delegate.currentAuthorizationFlow = OIDAuthState.authState()
// ...
}
}
}
That said, I think it's easier to just declare currentAuthorizationFlow
as a static property of some top level type, or a global property. Then it can be accessed from anywhere.
enum Auth {
@MainActor
static var currentAuthorizationFlow: OIDExternalUserAgentSession? = nil
}