Cocoa: Receiving dock icon click of already-running application - cocoa

I'm writing an application with a main window that's displayed when the app starts. When the window is closed, I'd like the app to remain running (with a menu-bar menu), and if the user clicks on the dock icon again, I'd like the main window to be presented again.
I'm about 90% of the way there: my app properly keeps running after the main window is closed with Cmd-W, and since "Release When Closed" is unchecked, the window could be [makeKeyAndOrderFront:]-ed to show it again when the dock icon is clicked.
The only missing piece of this puzzle is intercepting the actual dock-icon click.
The other threads about this topic recommend implementing either applicationShouldHandleReopen:hasVisibleWindows: or applicationShouldOpenUntitledFile: in the window controller. I've done both, and neither one ever gets called.
Any other ideas?

The other threads about this topic recommend implementing either applicationShouldHandleReopen:hasVisibleWindows: or applicationShouldOpenUntitledFile: in the window controller.
That's only true if the window controller is the application's delegate. That is the object to which the application sends those messages.
I would not make a window controller the application's delegate, though. I typically make them two separate objects. Make one object specifically to be the application's delegate, and when that object receives the relevant delegate messages, send a message to your window controller telling it to do whatever it needs to do.
Actually, what I usually do in single-window apps is make the application's delegate create and own the window controller. You can respond to window closure by throwing away the WC, and respond to reopen by checking whether you have a WC and creating one (and thereby reopening the window) if you don't.

Use [NSApp setDelegate:self]; in awakeFromNib.

Related

Overriding close action in OSX

How do I override the close button action to show a confirm dialog?
Would placing code in the app delegate's
applicationWillTerminate
be the correct place for such code?
I have an app using CloudKit.
It saves way too constantly, where it causes errors trying to save to iCloud.
So I'm trying to show a dialog to confirm if the user wants to sync before quitting.
If you want to block a window from closing, the NSWindow's delegate is where it's at. Be it either the AppDelegate (which is the default for most Xcode templates) or an NSWindowController if that's how you presented your window. The method you want to implement from NSWindowDelegate is -windowShouldClose: or -windowWillClose:. If you want to prevent the closing of the window, simply return NO from -windowShouldClose:.
If you want to stop the app from quitting, you should look into NSApplicationDelegate and override -applicationShouldTerminate: and return NSTerminateCancel to prevent the app from terminating.
In short, the "should" methods allow you to prevent the action from occurring, the "will" methods are just saying that it will happen and you can deal with it.

Open Spotify main window from another app when closed

Iam working on an application that tries to extend some Spotify functionality (not another client). I would like to show Spotify's main window when my icon is clicked on the dock - even if the main window in Spotify is closed.
This is my code now:
- (void) applicationDidBecomeActive:(NSNotification *)notification {
// Causes Spotify to hit the front when selecting it!
[[[NSRunningApplication
runningApplicationsWithBundleIdentifier:#"com.spotify.client"] lastObject]
activateWithOptions:NSApplicationActivateAllWindows];
}
It works when the window is open but not in focus (background), but not when I close the Spotify main window (which I people tend to do). Is there any way to re-open this window if it's closed from another application?
The Spotify icon can do this (obviously). Try to hit the close button (the red x) and press the icon (it will reappear). Is that possible from another app?
The Dock sends the reopen Apple Event to the application you clicked on, which then executes code as it sees fit. To simulate a Dock click, you need to send the reopen event to the application manually.
The Apple Documentation on the matter states that the reopen event has the ID of rapp code (kAEReopenApplication) and is part of the kCoreEventClass class.
From there, it's relatively simple to construct the Apple Event in code and send it off to the application. Note that you really should do some error checking from the AESendMessage call, as the event will likely fail if the application isn't launched, etc — I haven't really tested those cases.
Here is my solution. Note that you'll need to keep your original code to bring the application to the front — the Apple Event doesn't change application order.
NSAppleEventDescriptor *target = [[NSAppleEventDescriptor alloc]
initWithDescriptorType:typeApplicationBundleID
data:[#"com.spotify.client" dataUsingEncoding:NSUTF8StringEncoding]];
NSAppleEventDescriptor *event = [[NSAppleEventDescriptor alloc]
initWithEventClass:kCoreEventClass
eventID:kAEReopenApplication
targetDescriptor:target
returnID:kAutoGenerateReturnID
transactionID:kAnyTransactionID];
AESendMessage(event.aeDesc, NULL, kAEWaitReply | kAENeverInteract, kAEDefaultTimeout);

Transmit NSMenu commands to the current window?

Newbie OS X developer here, although fairly experienced with iOS.
I am missing something basic about the way the top-level NSMenu interacts with the application. I want the File->Save command to go to the current window. So far I have only been able to receive NSMenu actions in the app delegate. Am I supposed to keep track of the active window there and invoke methods from the app delegate?
Firstly, it sounds like you need to read up on Mac menu handling, because there are a lot of things you need to know about in order to deal with menus correctly.
To answer your specific question, if a menu item has a target of nil, such as the Save menu item, then the menu handling system walks up the responder chain, starting from the currently active control or view (first responder), looking for an object that implements the action selector for that menu item.
If you don't understand how the responder chain works, you should read about that too, because it's fundamental to understanding how Mac apps work.
If you want your window controller to handle the ‑save: action when its window is the main window, then all you need to do is implement the ‑save: action in your window controller. Because the window controller is in the responder chain before the application delegate, its implementation of the method will be used.

NSWindow delegate (windowShouldClose)

In one of my first Cocoa applications I have just a single window with 2 NSTextField instances - for user text input and of output of text processing.
If my user clicks on the red x on the top left, the window closes but the application is still running and icon stays in the dock - the normal Mac behavior.
When the user double-clicks on the icon in dock or on the desktop, this does not reopen the window - apparently also normal Mac behavior, but confusing to the user.
To get the app back into a running state, the user has to force Quit from the main menu or the context menu, and restart the app by clicking on one of the icons.
I searched Apple doc and forums, and it seemed that the following should prevent the closing of the window (my first preference : hide the widow so it can be reopened later) :
add a delegate to NSApp
delegate implements -applicationShouldHandleReopen which calls [mainWindow makeKeyAndOrderFront:self]; and returns TRUE
delegate implements -windowShouldClose which returns FALSE
However, although -windowShouldClose is called, the window closes.
What am I missing here?
As an alternative (my second preference), I added to the delegate
-applicationShouldTerminateAfterLastWindowClosed which returns YES
This works, i.e. the application closes when the used clicks on the red x,
and the user can restart the app later without further ado.
Clarifications and pointers to specific doc and working code examples would be appreciated.
Rudi
"When the user double-clicks on the icon in dock or on the desktop, this does not reopen the window - apparently also normal Mac behavior, but confusing to the user."
If you want the window to re-open in that case, implement applicationShouldHandleReopen:hasVisibleWindows:. There's nothing un-Mac-like about opening a window when the user clicks the dock icon after closing all the windows; lots of apps do it and the delegate exists specifically to support that behavior.
First of all, your "alternative" behavior of terminating the app on window close is probably the preferred approach for your situation. Users may be confused when they can't close the window.
If you really want to prevent the window from being closed, why not just disable the close control on the window in IB?

applicationShouldTerminateAfterLastWindowClosed: does not seem to work when the red x is used to close my Cocoa app

I have a small OSX Cocoa app that just bring up an IKPicutreTaker and saves the picture to a file if one is set. I use applicationShouldTerminateAfterLastWindowClosed: to close the application when the pictureTaker is closed. This all works fine when I either set(this is done when you have picked the picture you want) or when you hit cancel, but when I click on the red arrow in the top left of the windows, the application does not quit when the window is closed this way. Is this intended functionality or am I doing something wrong (not setting some flag?). Also, is there some way to disable this button?
Clicking the red button does not close an application, partly because that verb does not go with that noun. The red button closes the window it's on; it does not quit your application. (An application can and will often have multiple windows on Mac OS X.)
That's where applicationShouldTerminateAfterLastWindowClosed: comes in. Whenever the user closes the last window on the screen (whether by clicking the red button, by choosing Close from the File menu, or by some other means), the NSApplication object will send an applicationShouldTerminateAfterLastWindowClosed: message to its delegate, to ask it whether the application should terminate. If the delegate responds to the message by returning YES, then the application will terminate itself.
You don't say exactly how you're “using” applicationShouldTerminateAfterLastWindowClosed:. If you're just sending it to some object, that's not going to work, because you're asking a question (“should the application terminate after the last window is closed?”) and then ignoring the answer. If you implement the method in an object, but that object isn't the application's delegate, that won't work either—the application object only sends that message to its delegate.
You need to implement the method in your delegate, and return YES when it is appropriate for the application to terminate when the user closes its last window.

Resources