Couldn't cast SwiftUI.ExtensionDelegate to ExtensionDelegate

Viewed 1498

Working working with a SwiftUI Architecture WatchOS app, if you want to use ExtensionDelegate you need to create your own. I have done this, but when I try to actually access the delegate in the code, I am getting the following error message Could not cast value of type SwiftUI.ExtensionDelegate' (0x7fff8441b480) to 'TestMe_WatchKit_Extension.ExtensionDelegate' (0x10c3b36d0).
I have defined the ExtensionDelegate as -

class ExtensionDelegate: NSObject, WKExtensionDelegate {
    var meetingStatistics:  MeetingStatistics = MeetingStatistics()
    override init(){
        super.init()
    }
    func applicationDidFinishLaunching() {
        // Perform any final initialization of your application.
        print("applicationDidFinishLaunching for watchOS")
    }
    func applicationDidBecomeActive() {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }
    func applicationWillResignActive() {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, etc.
    }
}

in my @main - I have the following:

@main
struct WatchApp: App {
@WKExtensionDelegateAdaptor(ExtensionDelegate.self) var delegate
// code

}

When I try to access the delegate as - let delegate = WKExtension.shared().delegate as! ExtensionDelegate I get the above error.

3 Answers

The WKExtension.shared().delegate is not your ExtensionDelegate but internal SwiftUI.ExtensionDelegate (as stated), which provides your adapter delegate. You must use (pass everywhere) only instance of adapter delegate provided for your via

@WKExtensionDelegateAdaptor(ExtensionDelegate.self) var delegate

Update:

To use your delegate you have to pass it as argument next to view, eg as environment (so you can use it in any subview as @Environment(\.appDelegate) var appDelegate

var body: some Scene {
    WindowGroup {
       ContentView()
          .environment(\.appDelegate, delegate)
    }
}

and

struct DelegateKey: EnvironmentKey {
    typealias Value = ExtensionDelegate?
    static let defaultValue: ExtensionDelegate? = nil
}

extension EnvironmentValues {
    var appDelegate: DelegateKey.Value {
        get {
            return self[DelegateKey.self]
        }
        set {
            self[DelegateKey.self] = newValue
        }
    }
}

Well, - in the meantime I collected more experience. For my application delegate it is important that it is a singleton (only one instance exists). I realised that I have two.

@NSApplicationDelegateAdaptor(AppDelegate.self) var applicationDelegate

in the SwiftUI App struct initialises the first. To this App struct I added a init() method.

 init() {
  
  AppDelegate.shared = self.applicationDelegate
}

That initialises my AppDelegate static variable "shared" very early in the programme.

I removed all @Environment based access to the delegate, the key, the default value etc.. Global access is insured by

AppDelegate.shared

The issue with the self-defined @Environment variables is, that they can only be set with an .environment(_ keyPath:, _ value:) modifier on a view. However, my delegate is accessed before the first view sets the environment variable. Hence, my AppDelegate shared variable is now an ordinary static variable which I set myself as early as possible: in the init() method of the App struct.

I have the same problem in macOS. After declaring a class as the application delegate using in the @main App struct:

@NSApplicationDelegateAdaptor(AppDelegate.self) var applicationDelegate

The AppDelegate is correctly initialised when the Application launches. I declare the environment key:

struct DelegateKey: EnvironmentKey {
    typealias Value = AppDelegate
    static let defaultValue: AppDelegate = AppDelegate()
}

extension EnvironmentValues {
    var appDelegate: DelegateKey.Value {
        get {
            return self[DelegateKey.self]
        }
        set {
            self[DelegateKey.self] = newValue
        }
    }
}

to access the variable via @Environment(\.appDelegate) var appDelegate

That is fine for all SwiftUI environments. The problem is that the general access to the application delegate via NSApp.delegate does not work any longer.

The solution is to declare a static variable in the AppDelegate class itself:

class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
   
   @Environment(\.appDelegate) static var shared
}

This makes the instantiated AppDelegate object accessible from everywhere via: AppDelegate.shared .

Related