Yes, I know this goes against the whole MVC principle!
However, I\'m just trying to whip up a pretty trivial application - and I\'ve pretty much implemented it. Howe
The sample swift code for the autoreleasepool snippet provided above does not work in modern Xcode. Instead, you need to get rid of the @NSApplicationMain in your App Delegate source file, if there is one (Xcode now adds these for new projects), and add a main.swift file that contains the following:
The top level code sample above no longer works in recent versions of Xcode. Instead use this:
import Cocoa
let delegate = ExampleApplicationController() //alloc main app's delegate class
NSApplication.shared().delegate = delegate //set as app's delegate
let ret = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
Don't use NSApplication and NSApp ...
You just have to specify the class which implements the UIApplicationDelegate protocol :
UIApplicationMain(argc, argv, nil, @"Name of your class");
This is the method I use in my applications. Sorry for the formatting, I hope you can make it out. I don’t know how to turn off the auto-formatting here.
Of course there will be no functioning main menu out of this example, that’s far too much code for me to write on a post like this :P - Sorry, out do some research on that ;)
This should get you started:
AppDelegate.h
@interface MyApplicationDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate> {
NSWindow * window;
}
@end
AppDelegate.m
@implementation MyApplicationDelegate : NSObject
- (id)init {
if (self = [super init]) {
// allocate and initialize window and stuff here ..
}
return self;
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
[window makeKeyAndOrderFront:self];
}
- (void)dealloc {
[window release];
[super dealloc];
}
@end
main.m
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSApplication * application = [NSApplication sharedApplication];
MyApplicationDelegate * appDelegate = [[[[MyApplicationDelegate]alloc] init] autorelease];
[application setDelegate:appDelegate];
[application run];
[pool drain];
return EXIT_SUCCESS;
}
7 years too late to the party, but a bit simpler single file code
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate> {
NSWindow* window;
}
@end
@implementation AppDelegate : NSObject
- (id)init {
if (self = [super init]) {
window = [NSWindow.alloc initWithContentRect: NSMakeRect(0, 0, 200, 200)
styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskClosable
backing: NSBackingStoreBuffered
defer: NO];
}
return self;
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
window.title = NSProcessInfo.processInfo.processName;
[window cascadeTopLeftFromPoint: NSMakePoint(20,20)];
[window makeKeyAndOrderFront: self];
}
@end
int main(int argc, const char * argv[]) {
NSApplication* app = NSApplication.sharedApplication;
app.ActivationPolicy = NSApplicationActivationPolicyRegular;
NSMenuItem* item = NSMenuItem.new;
NSApp.mainMenu = NSMenu.new;
item.submenu = NSMenu.new;
[app.mainMenu addItem: item];
[item.submenu addItem: [[NSMenuItem alloc] initWithTitle: [@"Quit " stringByAppendingString: NSProcessInfo.processInfo.processName] action:@selector(terminate:) keyEquivalent:@"q"]];
AppDelegate* appDelegate = AppDelegate.new; // cannot collapse this and next line because .dlegate is weak
app.delegate = appDelegate;
(void)app.run;
return 0;
}
Swift 4 version with NSToolbar
and NSMenu
(with event handlers instead of delegates):
File main.swift:
autoreleasepool {
// Even if we loading application manually we need to setup `Info.plist` key:
// <key>NSPrincipalClass</key>
// <string>NSApplication</string>
// Otherwise Application will be loaded in `low resolution` mode.
let app = Application.shared
app.setActivationPolicy(.regular)
app.run()
}
File: Application.swift
class Application: NSApplication {
private lazy var mainWindowController = MainWindowController()
private lazy var mainAppMenu = MainMenu()
override init() {
super.init()
setupUI()
setupHandlers()
}
required init?(coder: NSCoder) {
super.init(coder: coder) // This will never called.
}
}
extension Application: NSApplicationDelegate {
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
mainWindowController.showWindow(nil)
}
}
extension Application {
private func setupUI() {
mainMenu = mainAppMenu
}
private func setupHandlers() {
delegate = self
mainAppMenu.eventHandler = { [weak self] in
switch $0 {
case .quit:
self?.terminate(nil)
}
}
}
}
File MainWindowController.swift
class MainWindowController: NSWindowController {
private (set) lazy var viewController = MainViewController()
private (set) lazy var mainToolbar = MainToolbar(identifier: NSToolbar.Identifier("ua.com.wavelabs.Decoder:mainToolbar"))
init() {
let window = NSWindow(contentRect: CGRect(x: 400, y: 200, width: 800, height: 600),
styleMask: [.titled, .closable, .resizable, .miniaturizable],
backing: .buffered,
defer: true)
super.init(window: window)
let frameSize = window.contentRect(forFrameRect: window.frame).size
viewController.view.setFrameSize(frameSize)
window.contentViewController = viewController
window.titleVisibility = .hidden
window.toolbar = mainToolbar
setupHandlers()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
extension MainWindowController {
private func setupHandlers() {
mainToolbar.eventHandler = {
print($0)
}
}
}
File MainViewController.swift
class MainViewController: NSViewController {
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = NSView()
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.magenta.cgColor
}
}
File MainToolbar.swift
class MainToolbar: NSToolbar {
enum Event: Int {
case toggleSidePanel
}
let toolbarDelegate = GenericDelegate()
var eventHandler: ((MainToolbar.Event) -> Void)?
override init(identifier: NSToolbar.Identifier) {
super.init(identifier: identifier)
setupUI()
setupHandlers()
}
}
extension MainToolbar {
private func setupUI() {
allowsUserCustomization = true
autosavesConfiguration = true
displayMode = .iconOnly
toolbarDelegate.allowedItemIdentifiers = [.space, .flexibleSpace]
toolbarDelegate.selectableItemIdentifiers = [.space, .flexibleSpace]
toolbarDelegate.defaultItemIdentifiers = Event.toolbarIDs + [.flexibleSpace]
}
private func setupHandlers() {
delegate = toolbarDelegate
toolbarDelegate.makeItemCallback = { [unowned self] id, _ in
guard let event = Event(id: id) else {
return nil
}
return self.makeToolbarItem(event: event)
}
}
private func makeToolbarItem(event: Event) -> NSToolbarItem {
let item = NSToolbarItem(itemIdentifier: event.itemIdentifier)
item.setHandler { [weak self] in
guard let event = Event(id: event.itemIdentifier) else {
return
}
self?.eventHandler?(event)
}
item.label = event.label
item.paletteLabel = event.paletteLabel
if event.image != nil {
item.image = event.image
} else if event.view != nil {
item.view = event.view
}
return item
}
}
extension MainToolbar.Event {
init?(id: NSToolbarItem.Identifier) {
guard let event = (MainToolbar.Event.allValues.filter { $0.itemIdentifier == id }).first else {
return nil
}
self = event
}
static var allValues: [MainToolbar.Event] {
return [toggleSidePanel]
}
static var toolbarIDs: [NSToolbarItem.Identifier] {
return [toggleSidePanel].map { $0.itemIdentifier }
}
var itemIdentifier: NSToolbarItem.Identifier {
switch self {
case .toggleSidePanel: return NSToolbarItem.Identifier("ua.com.wavalabs.toolbar.toggleSidePanel")
}
}
var label: String {
switch self {
case .toggleSidePanel: return "Toggle Side Panel"
}
}
var view: NSView? {
return nil
}
var image: NSImage? {
switch self {
case .toggleSidePanel: return NSImage(named: NSImage.Name.folder)
}
}
var paletteLabel: String {
return label
}
}
File MainMenu.swift
class MainMenu: NSMenu {
enum Event {
case quit
}
var eventHandler: ((Event) -> Void)?
private lazy var applicationName = ProcessInfo.processInfo.processName
init() {
super.init(title: "")
setupUI()
}
required init(coder decoder: NSCoder) {
super.init(coder: decoder)
}
}
extension MainMenu {
private func setupUI() {
let appMenuItem = NSMenuItem()
appMenuItem.submenu = appMenu
addItem(appMenuItem)
}
private var appMenu: NSMenu {
let menu = NSMenu(title: "")
menu.addItem(title: "Quit \(applicationName)", keyEquivalent: "q") { [unowned self] in
self.eventHandler?(.quit)
}
return menu
}
}
Convenience extensions.
File NSMenu.swift
extension NSMenu {
@discardableResult
public func addItem(title: String, keyEquivalent: String, handler: NSMenuItem.Handler?) -> NSMenuItem {
let item = addItem(withTitle: title, action: nil, keyEquivalent: keyEquivalent)
item.setHandler(handler)
return item
}
}
File NSMenuItem.swift
extension NSMenuItem {
public typealias Handler = (() -> Void)
convenience init(title: String, keyEquivalent: String, handler: Handler?) {
self.init(title: title, action: nil, keyEquivalent: keyEquivalent)
setHandler(handler)
}
public func setHandler(_ handler: Handler?) {
target = self
action = #selector(wavelabsActionHandler(_:))
if let handler = handler {
ObjCAssociation.setCopyNonAtomic(value: handler, to: self, forKey: &OBJCAssociationKeys.actionHandler)
}
}
}
extension NSMenuItem {
private struct OBJCAssociationKeys {
static var actionHandler = "com.wavelabs.actionHandler"
}
@objc private func wavelabsActionHandler(_ sender: NSControl) {
guard sender == self else {
return
}
if let handler: Handler = ObjCAssociation.value(from: self, forKey: &OBJCAssociationKeys.actionHandler) {
handler()
}
}
}
File NSToolbar.swift
extension NSToolbar {
class GenericDelegate: NSObject, NSToolbarDelegate {
var selectableItemIdentifiers: [NSToolbarItem.Identifier] = []
var defaultItemIdentifiers: [NSToolbarItem.Identifier] = []
var allowedItemIdentifiers: [NSToolbarItem.Identifier] = []
var eventHandler: ((Event) -> Void)?
var makeItemCallback: ((_ itemIdentifier: NSToolbarItem.Identifier, _ willBeInserted: Bool) -> NSToolbarItem?)?
}
}
extension NSToolbar.GenericDelegate {
enum Event {
case willAddItem(item: NSToolbarItem, index: Int)
case didRemoveItem(item: NSToolbarItem)
}
}
extension NSToolbar.GenericDelegate {
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
return makeItemCallback?(itemIdentifier, flag)
}
func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
return defaultItemIdentifiers
}
func toolbarAllowedItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
return allowedItemIdentifiers
}
func toolbarSelectableItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
return selectableItemIdentifiers
}
// MARK: Notifications
func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem,
let index = notification.userInfo?["newIndex"] as? Int {
eventHandler?(.willAddItem(item: toolbarItem, index: index))
}
}
func toolbarDidRemoveItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem {
eventHandler?(.didRemoveItem(item: toolbarItem))
}
}
}
File NSToolbarItem.swift
extension NSToolbarItem {
public typealias Handler = (() -> Void)
public func setHandler(_ handler: Handler?) {
target = self
action = #selector(wavelabsActionHandler(_:))
if let handler = handler {
ObjCAssociation.setCopyNonAtomic(value: handler, to: self, forKey: &OBJCAssociationKeys.actionHandler)
}
}
}
extension NSToolbarItem {
private struct OBJCAssociationKeys {
static var actionHandler = "com.wavelabs.actionHandler"
}
@objc private func wavelabsActionHandler(_ sender: NSControl) {
guard sender == self else {
return
}
if let handler: Handler = ObjCAssociation.value(from: self, forKey: &OBJCAssociationKeys.actionHandler) {
handler()
}
}
}
File ObjCAssociation.swift
public struct ObjCAssociation {
public static func value<T>(from object: AnyObject, forKey key: UnsafeRawPointer) -> T? {
return objc_getAssociatedObject(object, key) as? T
}
public static func setAssign<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_ASSIGN)
}
public static func setRetainNonAtomic<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
public static func setCopyNonAtomic<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
public static func setRetain<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN)
}
public static func setCopy<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_COPY)
}
}
Here is Casper's solution, updated for ARC as per Marco's suggestion:
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
}
return EXIT_SUCCESS;
}