I'm attempting to create a macOS application without a storyboard in Xcode 8 (stable) on macOS Sierra. However, my AppDelegate is not even being initiated. Here's the code I have:
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
override init() {
super.init()
print("Init")
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
print("Finished launching")
// Create a window
window = NSWindow()
// Add the view controller
let viewController = ViewController()
window.contentView?.addSubview(viewController.view)
// Show the window
window.makeKeyAndOrderFront(nil)
}
}
Neither init or applicationDidFinishLaunching(_ aNotification: Notification) is being called. Any help would be much appreciated.
You need to do a few things here
NSMainStoryboardFile key/value from the plist NSApplication subclass and assign it to the Principal Class (NSPrincipalClass) key. 
The name must be fully qualified with your module name.
delegate property. Make sure you keep a strong reference to your delegate object. Ive just used a let here.
class GrookApplication: NSApplication {
let strongDelegate = AppDelegate()
override init() {
super.init()
self.delegate = strongDelegate
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
e.g a simple delegate.
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
override init() {
super.init()
print("wassup")
//conceptual proof of life init override
//wait until applicationDidFinishLaunching , specially for UI
}
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
print("yo! I'm alive")
window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 200, height: 200), styleMask: .titled, backing: .buffered, defer: false)
window.makeKeyAndOrderFront(nil)
}
}
EDIT 2018 Verified High Sierra
Do NOT try and do window or view controller initialisation inside init this leads to double app initialisation issues and crashing. The app has not finished launching at this stage. Wait until applicationDidFinishLaunching to fire any significant operations.
Warren Burton's accepted answer, utilising a strong reference to a @NSApplicationMain-annotated AppDelegate instance no longer works. I've confirmed it myself on OS X High Sierra, and Alex Sieroshtan commented that it didn't work back in OS X Yosemite, either. The failure point, as Tyler Durden noted, was this message:
Assertion failure in -[X.XApplication init], /Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1504.82.104/AppKit.subproj/NSApplication.m:1778
2017-04-08 13:25:35.761585+0100 X
[9073:1059806][General] An uncaught exception was raised 2017-04-08 13:25:35.761601+0100 X
[9073:1059806][General] Creating more than one Application
I struggled with this myself for a good while, but came up with two up-to-date solutions by no small amount of experimentation.
@NSApplicationMain via a workaroundI found that you can alter the code of the accepted answer to work around the bug. The way to do this is by not calling the super.init() method in your class named AppDelegate.
Really. I think there is a too-eager assertion counting number of inits done by AppDelegate (or some logic along these lines), and thus the call to super.init() gets counted as well as the completion of the override init() block. You have two options for workarounds here:
Don't call super.init(): This is actually possible and completely healthy for NSObject, at least in macOS. You lose the ability to reference self in the override init() block, however.
Don't override init() at all: Consider doing your init process during a lifecycle method like applicationWillFinishLaunching(:).
I don't recommend either of these, of course.
@NSApplicationMain method altogether@NSApplicationMain is just a macro which we can approximate ourselves. By some luck, I came across James H Fisher's blog post explaining how. I'll quote what matters in a moment.
If you have written @NSApplicationMain anywhere, please delete it before proceeding with these instructions.
Info.plist fileThe key:value pair for NSPrincipalClass should keep its default value of:
<key>NSPrincipalClass</key>
<string>NSApplication</string>
main.swift instead of subclassing NSApplication
The file MUST be called main.swift; it's a special exception to Swift's "Expressions are not allowed at the top level" rule.
import AppKit
let app = NSApplication.shared
let delegate = AppDelegate()
app.delegate = delegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
James H Fisher explains, referencing the NSApplication documentation:
[Docs]
Every app must have exactly one instance of
NSApplication(or a subclass ofNSApplication). Your program’smain()function should create this instance by invoking theshared()class method.[James]
First,
main.swiftrunsNSApplication.shared, and assigns thisNSApplicationobject tomyApp. Notice the documentation refers to amain()function, even though in Swift there is none! The equivalent is themain.swiftfile.Next,
main.swiftinstantiates yourAppDelegateclass, and assigns it as the.delegateofmyApp. You can now see why the default project chooses to call the classAppDelegate: it is set as the.delegateon anNSApplication.Finally,
main.swiftcalls the functionNSApplicationMain(...)... The functionNSApplicationMain(...)is the entry point for Cocoa applications.NSApplicationMain(...)never returns; instead, it sets up the UI event loop, and eventually exits using the Cexit(...)function.
Additionally this StackOverflow post goes into some detail about why using sharedApplication remedies the "Creating more than one Application" bug.
... That's all you need! Hope this serves to help somebody else.
In case someone is looking for a Swift version (based on @WarrenBurtons answer).
AppDelegate
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow?
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
window = NSWindow(contentViewController: RootViewController())
window?.makeKeyAndOrderFront(self)
}
}
class RootViewController: NSViewController {
override func loadView() {
self.view = NSView()
self.view.frame = NSRect(x: 0, y: 0, width: 600, height: 400)
}
}
NSApplication subclass
import Cocoa
class Application: NSApplication {
let strongDelegate = AppDelegate()
override init() {
super.init()
self.delegate = strongDelegate
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Info.plist entry
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
...
<key>NSPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).Application</string>
...
</dict>
</plist>
I've also created a gist for this, that I will keep up to date for new Xcode / Swift versions. https://gist.github.com/florieger/7ac5e7155f6faf18666f92f7d82f6cbc
Edit: Make sure to delete the Main.storyboard / MainMenu.xib, otherwise you might end up with two Windows in the UI Debugger.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With