I would like to use a custom subclass of NSApplication to create the application instance of my program. The object retrieved by NSApplication.shared or NSApp should be an instance of the Application class below:

class Application: NSApplication {
    // My own methods
}

I first tried to set Principal class in Info.plist from NSApplication to Application but I later learned I had to use $(PRODUCT_MODULE_NAME).Application instead. This worked, and instantiated my subclass as expected, when I tried in a fresh macOS Application template with no modifications other than changing the Principal class and adding the code to define my subclass.


The issue is that the above changes had no effect whatsoever in an existing application project with a different structure.

To reproduce this in the fresh project, I removed the .xib/.storyboard file, and replaced the AppDelegate.swift file with only a main.swift file containing the following:

import Cocoa
class Application: NSApplication, NSApplicationDelegate {
    func applicationShouldTerminateAfterLastWindowClosed(_ _: NSApplication) -> Bool {
        return true
    }
}
NSApplication.shared.delegate = (NSApp as! Application)
NSApp.run()

In this case, the as! downcast will fail, meaning that the application object created by NSApplication.shared is not an instance of my custom class, even though I set the Principal class to the same string that worked before. Removing Info.plist entirely seems to have no effect at all.

I suspect that the other project structure comprising a delegate annotated with @NSApplicationMain somehow registers the value of Principal class in Info.plist to be used to instantiate the application instance, but using NSApplication.shared to manually initialize the application does not include that step.

How can I use a custom class for my application object in this particular case where I use NSApplication.shared directly?

In case this matters my macOS version is 10.15.7 and my Swift version is 5.3.2.

1

There are 1 best solutions below

3
On

You need to make sure that the Principal class includes the correct module name for it to be loaded.

This works fine:

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        print("NSApp:", NSApplication.shared as! MyApplication)
    }
}

class MyApplication: NSApplication {
    
}

enter image description here

Which prints:

NSApp: <CustomClass.MyApplication: 0x12af053b0>