Is there a main.swift which is equivalent to the @NSApplicationMain annotation?
The documentation assumes that there is a xib or storyboard which instantiates the AppDelegate
class via an object (blue cube) in Interface Builder. In this case both
main.swift
containingNSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
and
@NSApplicationMain
in theAppDelegate
class
behave exactly the same.
If there is no xib or storyboard you are responsible to initialize the AppDelegate
class, assign it to NSApplication.shared.delegate
and run the app. You have also to consider the order of appearance of the objects. For example you cannot initialize objects related to AppKit
before calling NSApplication.shared
to launch the app.
For example with this slightly changed syntax
let app = NSApplication.shared
let appDelegate = AppDelegate()
app.delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
you can initialize the status bar in AppDelegate
outside ofapplicationDidFinishLaunching
:
let statusItem = NSStatusBar.system().statusItem(withLength: -1)
because NSApplication.shared()
to launch the app is called before initializing the AppDelegate
class.
Here is what I did in order to run application without @NSApplicationMain
annotation and function NSApplicationMain(_, _)
while using Storyboard with initial NSWindowController
generated by Xcode application template (with slight modification related to Main Menu
described below).
File: AppConfig.swift (Swift 4)
struct AppConfig {
static var applicationClass: NSApplication.Type {
guard let principalClassName = Bundle.main.infoDictionary?["NSPrincipalClass"] as? String else {
fatalError("Seems like `NSPrincipalClass` is missed in `Info.plist` file.")
}
guard let principalClass = NSClassFromString(principalClassName) as? NSApplication.Type else {
fatalError("Unable to create `NSApplication` class for `\(principalClassName)`")
}
return principalClass
}
static var mainStoryboard: NSStoryboard {
guard let mainStoryboardName = Bundle.main.infoDictionary?["NSMainStoryboardFile"] as? String else {
fatalError("Seems like `NSMainStoryboardFile` is missed in `Info.plist` file.")
}
let storyboard = NSStoryboard(name: NSStoryboard.Name(mainStoryboardName), bundle: Bundle.main)
return storyboard
}
static var mainMenu: NSNib {
guard let nib = NSNib(nibNamed: NSNib.Name("MainMenu"), bundle: Bundle.main) else {
fatalError("Resource `MainMenu.xib` is not found in the bundle `\(Bundle.main.bundlePath)`")
}
return nib
}
static var mainWindowController: NSWindowController {
guard let wc = mainStoryboard.instantiateInitialController() as? NSWindowController else {
fatalError("Initial controller is not `NSWindowController` in storyboard `\(mainStoryboard)`")
}
return wc
}
}
File main.swift (Swift 4)
// Making NSApplication instance from `NSPrincipalClass` defined in `Info.plist`
let app = AppConfig.applicationClass.shared
// Configuring application as a regular (appearing in Dock and possibly having UI)
app.setActivationPolicy(.regular)
// Loading application menu from `MainMenu.xib` file.
// This will also assign property `NSApplication.mainMenu`.
AppConfig.mainMenu.instantiate(withOwner: app, topLevelObjects: nil)
// Loading initial window controller from `NSMainStoryboardFile` defined in `Info.plist`.
// Initial window accessible via property NSWindowController.window
let windowController = AppConfig.mainWindowController
windowController.window?.makeKeyAndOrderFront(nil)
app.activate(ignoringOtherApps: true)
app.run()
Note regarding MainMenu.xib
file:
Xcode application template creates storyboard with Application Scene
which contains Main Menu
. At the moment seems there is no way programmatically load Main Menu
from Application Scene
. But there is Xcode file template Main Menu
, which creates MainMenu.xib
file, which we can load programmatically.