Subclass NSApplication in Swift
Try:
@objc(MyApplication)
class MyApplication: NSApplication {
OR, you have to set Info.plist like this.
<key>NSPrincipalClass</key>
<string>YourAppName.MyApplication</string>
^^^^^^^^^^^^
There is no exact document about this, but this description is corresponding:
In order to preserve namespacing when a Swift class is used in Objective-C code, Swift classes are exposed to the Objective-C runtime with their fully qualified names. Therefore, when you work with APIs that operate on the string representation of a Swift class, you must include the fully qualified name of the class. For example, when you create a document–based Mac app, you provide the name of your NSDocument subclass in your app’s Info.plist file. In Swift, you must use the full name of your document subclass, including the module name derived from the name of your app or framework.
and this:
When you use the
@objc(<#name#>)
attribute on a Swift class, the class is made available in Objective-C without any namespacing.
Sending replyToApplicationShouldTerminate to NSApplication in Swift
I believe you should change
NSApplication.replyToApplicationShouldTerminate(true)
to
NSApplication.sharedApplication().replyToApplicationShouldTerminate(true)
since replyToApplicationShouldTerminate
is a instance method rather then a class method.
NSWindow not closing when created with custom NSApplication
I seem to have solved the problem, In Objective-C I can just use this, and my app will respond to the quit item in the dock menu.
NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:YES];
In Swift if you try to get the next event like that you would do this:
let event = nextEventMatchingMask(Int(NSEventMask.AnyEventMask.rawValue),
untilDate: NSDate.distantPast(),
inMode: NSDefaultRunLoopMode,
dequeue: true)
However, you get an overflow error when converting from UInt64 to Int. This seems to be unintended. At first, I tried to resolve this problem by replacing it with 0xfffffffffffffff
. This worked fine, and the app would respond to events. But this was not enough, actually. The app needs to also respond to events matching a mask 0x1
. I have no idea why, but this allows me to quit and hide my app from the dock menu. (0x0
only allows me to quit.)
So then, the entire run()
implementation of a Swift NSApplication subclass is this:
override func run() {
finishLaunching()
setValue(true, forKey: "running")
while true {
let event = nextEventMatchingMask(0xfffffffffffffff, untilDate: NSDate.distantPast(), inMode: NSDefaultRunLoopMode, dequeue: true)
let dockEvent = nextEventMatchingMask(0x1, untilDate: NSDate.distantPast(), inMode: NSDefaultRunLoopMode, dequeue: true)
if dockEvent != nil { sendEvent(dockEvent!) }
if event != nil { sendEvent(event!) }
if !running { break }
updateWindows()
}
}
NSApplicationDelegate not working without Storyboard
You need to do a few things here
- Delete
NSMainStoryboardFile
key/value from the plist - Create a
NSApplication
subclass and assign it to thePrincipal Class (NSPrincipalClass)
key.
The name must be fully qualified with your module name.
- Manually instantiate your delegate in your NSApplication subclass and assign it to the
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.
How to get Main Window (App Delegate) from other class (subclass of NSViewController)?
For the mainWindow method the docs say:
This method might return nil if the application’s nib file hasn’t finished loading, if the receiver is not active, or if the application is hidden.
I just created a quick test application and I placed the following code:
NSLog(@"%@", [[NSApplication sharedApplication] mainWindow]);
into my applicationDidFinishLaunching:aNotification
method, and into an action method which I connected to a button in the main window of my application.
On startup, the mainWindow
was nil, but when I click the button (after everything is up and running and displayed), the mainWindow
was no longer nil.
NSApplication
provides other methods which you may be useful to you:
- windows
- an array of all the windows;– keyWindow
- gives the window that is receiving keyboard input (or nil);– windowWithWindowNumber:
- returns a window corresponding to the window number - if you know the number of the window whose contents you wish to replace you could use this;– makeWindowsPerform:inOrder:
- sends a message to each window - you could use this to test each window to see if it's the one you are interested in.
With regard to calling methods on the delegate
, what you say gives a warning works fine for me. For example, this works with no warnings:
NSLog(@"%@", [[[NSApplication sharedApplication]delegate] description]);
What exactly is the warning you receive? Are you trying to call a method that doesn't exist?
How do I use a subclass of NSDocumentController in XCode 4?
In your application delegate:
// LukeAppDelegate.h
#import "LukeAppDelegate.h"
#import "VRDocumentController"
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
VRDocumentController *dc = [[VRDocumentController alloc] init];
}
This will make sure that an instance of VRDocumentController
is created and registered as the shared document controller, preventing Cocoa from using the default NSDocumentController
.
As to why you haven’t been able to use a custom object in your nib file, make sure that that you select Object (blue cube) instead of Object Controller (blue cube inside a green sphere) when dragging a new object into the nib file.
Edit: If you’re targeting an OS X version that supports restoration, -applicationWillFinishLaunching:
may be too late to register a custom document controller. If the application delegate is placed inside MainMenu.xib, it should be instantiated by the nib loading process before any documents are restored, hence you can move the NSDocumentController
subclass initialisation to the application delegate’s init method:
// LukeAppDelegate.h
#import "LukeAppDelegate.h"
#import "VRDocumentController"
- (id)init {
self = [super init];
VRDocumentController *dc = [[VRDocumentController alloc] init];
return self;
}
NSApplication init failure: Creating more than one Application
Looking at my own code for doing without NSApplicationMain()
, you don't alloc init
the NSApplication
instance.
You should do this-- use the sharedApplication
singleton generator method:
NSApplication *application = [NSApplication sharedApplication];
See, for instance, the answer here. I also talk about a few other things that break when NSApplicationMain()
isn't used.
@cacau got me squinting at that assertion. It's in the NSApplication init
method you call in the very first line of the app. Does it look like the exception happens there if you set an exception breakpoint?
Hope that helps, though I expect you'll run into more issues. I haven't done the no-NSApplicationMain thing for a document based app.
For what it's worth, the sharedApplication
reference says:
This method also makes a connection to the window server and completes other initialization. Your program should invoke this method as one of the first statements in main(); this invoking is done for you if you create your application with Xcode.
Related Topics
Why Can't I Divide Integers in Swift
Po Swift String "Unresolved Identifier"
Calculate Area of Mkpolygon in an Mkmapview
Access Properties via Subscripting in Swift
Cannot Read the Nfc Chip of the Epassport Using iOS13
Check for Value or Reference Type in Swift
How to Create a Static Class in Swift
Why Does an @Objc Enum Have a Different Description Than a Pure Swift Enum
Can Swift Playgrounds See Other Source Files in the Same Project
How to Use Objective-C Enum in Swift
How to Get User Input in Apple's Swift Language in a Command Line Tool
Uisplitviewcontroller Displaymodebuttonitem()
Swiftui Swizzling Disabled by Default, Phone Auth Not Working
Pass Optional Block or Closure to a Function in Swift
What Is an Example of Drawing Custom Nodes with Vertices in Swift Scenekit
How Does Typecasting/Polymorphism Work with This Nested, Closure Type in Swift