Unloading my plugin from another process - cocoa

I'm toying with adding an NSDockTilePlugIn to my application, but I've come across some strange behavior -- understandable behavior, but I'd like to see if there is a way around it.
When I run my app from the dmg or keep the (dmg version of the) app in the Dock, the Dock loads my .docktileplugin bundle.
When the application quits, the plugin receives a setDockTile: message with a nil NSDockTile * (as per the documentation). However, the plugin keeps running, and I cannot eject (unmount, detach) the dmg unless I kill the Dock (I suppose logging out would work too).
I've tried sending a Notification through the NSDistributedNotificaitonCenter (from a non-dmg version of the app) to tell it to unload, but the Console tells me that now I have two of the same docktileplugin loaded, and it is undefined which one will execute.
And even though [[NSBundle bundleWithPath:...] unload] returns YES, I still cannot eject the dmg.

Related

OS X: Launching app from dock is a no-op if app is already running headless (via NSApplicationActivationPolicyProhibited)

My app can run with a UI and run headless, chosen via a command-line argument. (I don't want to build two separate apps.) When running headless, I'm calling:
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
during app initialization, and don't show any windows.
This all works fine, and I can run the app headed (via the dock) and headless (via another app passing the command-line argument) at the same time. The little dot shows up next to the dock icon only for the headed app. Good. The problem happens if the headless app is launched first. In that state (no dot in the dock), any clicking on the app icon (or launching via Spotlight or the Finder) does nothing, or sometimes brings up "You can’t open the application “Foo” because it is not responding." Presumably the OS thinks the app is already running headed, and is trying to activate it. Is there a way to convince it to ignore that background app and launch a new instance?
Quitting the background app allows the app to launch normally again from the dock.
I don't want to set LSBackgroundOnly in the plist, because that would require two separate apps unless I changed the setActivationPolicy to NSApplicationActivationPolicyRegular in the case of the headed app. But I've read that doing that causes various other bugs, like the menu bar not always showing up.
Any ideas?
I'm on OS 10.10.5 (the oldest OS I need to support). Thanks for any help!
Update: I just noticed that when clicking on the dock and it appears to do nothing, if there are no app windows open I can see that it actually unhides my app's main window (which was created hidden) but doesn't bring it to the front (presumably it notices that NSApplicationActivationPolicyProhibited is set, though that wasn't enough to prevent it from showing the window!).
Update 2: At this point, I'd settle for a way to notify the user that the reason the app isn't launching is that they need to quit the other app that has sublaunched the headless process. Of course this code would need to be in the headless app, since the headed doesn't even launch.
It looks like you can call setActivationPolicy from applicationShouldHandleReopen and set the policy back to NSApplicationActivationPolicyRegular at that time. I made a test app with the following code and it seems to behave as you describe you would like it to above (although I've only run it on 10.11.5):
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(aNotification: NSNotification) {
if NSUserDefaults.standardUserDefaults().stringForKey("background") == "true" {
NSApp.setActivationPolicy(.Prohibited)
print("launched in background")
}
}
func applicationShouldHandleReopen(sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
NSApp.setActivationPolicy(.Regular)
return true
}
}

How does Steam prompt me to delete game data when I delete the app?

I recently tried delete Steam.app from Mac OS X 10.10.5. As soon as I hit Command-Backspace to remove the application, this dialog appeared:
The text reads:
Steam still has some game content stored at /Users/apaidnerd/Application Support/Steam/SteamApps. Do you want to delete the game content as well?
[Leave files] [Reveal in Finder] [Delete files]
How did the Steam application cause this to happen? If I'm developing an app for Mac OS X, where is this documented?
You were close. It looks to me like the launch agent "com.valvesoftware.steamclean" is responsible for this. Unloading the "com.valvesoftware.steam.ipctool" agent/service may effectively disable the other one.
The steamclean agent's description plist file is in ~/Library/LaunchAgents. It doesn't poll. It has a WatchPaths key, which asks launchd to run it when that path changes. launchd uses file system notification techniques to monitor that, which also don't rely on polling.
When that happens, the agent runs the program ~/Library/Application Support/Steam/SteamApps/steamclean, which is not in the Steam.app bundle. It couldn't be, because the app bundle has been moved or renamed, so launchd would not be able to find the program for the agent. steamclean is what shows the dialog. You can confirm this by reproducing the dialog and leaving it open. Then, use Activity Monitor to list the processes and filter for "steam".
Figured it out. Steam has a launchctl agent that runs (com.valvesoftware.steam.ipctool) out of the /Applications/Steam.app/ directory. If I stop the daemon and remove the Steam.app, no dialog appears.
It must poll to see if its executable path still exists and prompts the user if goes missing. Also, the inactive-looking title bar implies that this dialog is indeed coming from some sort of background process.

How do I know the way my OS X application was launched?

Ok. It is 2015. A lot of things have changed. And I would like to ask...
Does anyone know the way to detect how the application was launched on OSX ?
Because I still don't have the answer...
I am talking about these cases that are important to me:
Regular launch by user (via selecting app from Finder, Launchpad, etc.)
Launch at login (automatic launch by myHelperApp on startup)
Launch by user selecting item in services menu ("Do something in MyApp") assuming my app wasn't launched before.
Right now I am detecting the launch-at-login with outdated GetCurrentProcess function, obtaining current process id and then looking up for a parent process info. In case parent process info is obtained (!) and bundleId is not equal to some list of strings (myHelperApp bundleId, com.apple.loginwindow, com.apple.coreservices.uiagent) (!) - then this is not launch-at-login case.
Yes, it works for now, but c'mon people, this is a completely outdated, not stable way to solve the problem!
And what is important - there seems to be no way to tell that my app was launched via Services menu!
Has anyone found something new on this topic?

My windows phone application runs when ran though visual studio (onclick of device button) but when I disconnect and run it doesn't?

I have written file picker code in my project. When i run the project in my windows phone by clicking on device button in visual studio, the app runs fine(I mean it opens pictures library and i can select a photo and preview it).
But when I disconnect my usb and then open the app in the phone and when i open pictures library on click of a button , the pictures library opens briefly and then the app crashes immediately(My app closes).
Can anyone please help me with this??
As written in the blog post , the AndContinue method run in a different process and to do so, the current running app goes into the background or even gets closed sometimes, that is what you are experiencing in your app as far i can tell but not sure why different things happening during debug & deploy.
There must be a callback inside app.xaml.cs specifically to handle the condition when calling application(which was sent into background) comes in foreground, read this blog post carefully & you'll understand as what you need to change in your code :
using-the-andcontinue-methods-in-windows-phone
http://blogs.msdn.com/b/wsdevsol/archive/2014/05/08/using-the-andcontinue-methods-in-windows-phone-silverlight-8-1-apps.aspx
I just dealt with this issue and one of the reasons why there are differences between debug and deploy is because of the suspending event.
During debug, the application does not actually get suspended until you manually do it through Lifecycle Events. This means that when you pick a file on debug and the app is put to the background to load the file picker, it is not actually suspended, while when the app is deployed, it actually gets suspended.
Look into your app_resuming method and OnSuspending methods in your app.xaml that may be causing errors not occurring during debug.

How to make 2 applications shared the same dock tile

I have a launcher application in a bundle in /Applications that chooses a binary and fork/execs it. What I would like is for them to share a dock tile. I have tried a few approaches and none of them are satisfactory.
1. Keep the binary bundled with the app
If the binary is contained within the app bundle, this seems to work. However, it's generally considered bad practice for a bundle to modify itself, and I can't guarantee that a user has write access to /Applications. I tried putting a symlink to e.g. ~/Library/Application Support/MyBinaries/mybinary but this gives a separate dock tile.
2. Keep the binary outside the app bundle
If the binary is outside the app bundle, I get a separate dock tile. Why don't I set one of the processes to be an agent s.t. one of the tiles does not appear? This would have to be the launcher as the user should not be able to skip the launcher's logic as to which binary to run. Unfortunately this means that quit doesn't work properly, and you can't click the dock to restore the binary's window. It may be possible to get this to work by passing messages between the applications properly.
3. Massively hacky solution
There's a rather hacky way of doing this by adding an extra level of indirection, where the app at /Applications/MyLauncher.app launches another app in ~/Library/Application Support/Launcher2.app, which contains the binaries in its bundle. Only the second launcher has a dock icon.
This seems rather hacky, so I thought I would open this up to SO to see if anyone has a better solution?
EDIT
In option 2, I was keeping the binaries in bundles of their own. Don't do this. When I kept them in a regular folder everything worked correctly.
Answering my own question.
If you find yourself in this situation, go with option 2. Put the application at /Applications/MyApplication.app and keep the binaries in a folder named something like ~/Library/Application Support/MyApplication/Binaries/ and happily fork/exec them. Definitely do not put them in app bundles, as this was the source of my confusion. This solved all of my criteria:
Don't need root access to modify the binaries
Launcher and binaries share one dock tile
Pressing quit on the dock tile quits the binary.
App appears in Launchpad
Launching via Dock goes through the launcher app

Resources