How to Terminate/Reset an bundle XPC helper? - macos

Does anyone know how to terminate or reset an XPC helper?
According to the Apple documentation, launchd takes care of the XPC helper if it crashes. But there is no indication of what to do, if you need to interrupt or cancel a running helper.
For instance, if I have an XPC client that is rendering a 32-bit QuickTime movie, how do I get my 64-bit "parent" application to signal the XPC helper to cancel the job and clean up?
Also, What is the proper way for an XPC helper app to handle a parent that has "Quit"?
Currently, to terminate on the parent app's side, I am using (NSXPCConnection):
(void)suspend
(void)invalidate
These seem to close off the connection. But I am not seeing any evidence that the helper app is paying attention.
Thanks in advance!

Your question doesn’t seem to ask for the proper handling of any crashes of the helper. Instead, you seem to simply need a way to properly tell the helper to terminate itself. If this is correct then please read on for a solution. At least if you still need one three years after asking…
You didn’t specify a language to use so please be aware my examples are written in Swift.
As part of your `NSXPCConnection* you had to define a protocol to be used for information sharing between app and helper. In this protocol you could add a simple function like this:
terminateHelper(withReply reply: (String) -> Void) {
reply("Terminating Helper.")
exit(0)
}
This function reports a message string back to the main app using the supplied closure and then terminates itself using the exit system call.
From your main app, you would call this function like this:
if let helper = helperConnection.remoteObjectProxyWithErrorHandler({ (error) in
let e = error as NSError
print("Helper communication failed. Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
}) as? HelperProtocol {
helper.terminateHelper(withReply: { (replyString) in
print(replyString)
})
}
Please not that launchd will immediately restart the terminated helper app despite the fact that it hasn’t crashed but was gracefully terminated instead. This will, however, guarantee that the helper returns to an initialized state with all previous helper processing being stopped.
If you suspend or invalidate the way you put in your question then you only cancel the XPC connection. But neither suspending nor invalidating the connection will send a message of any kind to the helper. The helper itself will simply see that the connection is suspended or invalidated without knowing anything about the reason.
Hope this gives you at least an idea of how to proceed with your problem.

Related

Using saveEventually() instead of save() or saveInBackground() for ParseUser or ParseObject

Looking at Parse documentation, there is a saveEventually() method that can be used in place of save() and saveInBackground().
Can I use this function everywhere in my code in place of save and saveInBackground so I don't have to worry about the user switching between being offline and online and I won't have to handle saving in situations where there is no connectivity? Are there any drawbacks to using this approach?
According to Parse's iOS guide:
Most save functions execute immediately, and inform your app when the save is complete. If you don't need to know when the save has finished, you can use saveEventually instead. The advantage is that if the user currently doesn't have a network connection, saveEventually will store the update on the device until a network connection is re-established. If your app is closed before the connection is back, Parse will try again the next time the app is opened.
So you will use save if you need the callback when the save has finished. I think you can use both depending on the situation you are handling.

Handling CGEventTaps in an NPAPI plugin

I'm trying to create an NPAPI plugin to listen to the Media Keys on a macbook and pass that to javascript to control things like pandora or soundcloud. I'm using Spotify's SPMediaKeyTap library, which just wraps CGEventTap running on a separate thread.
My problem is that I use npn_invoke to call back to javascript. This works normally, but when it's triggered from the CGEventTap callback, it crashes the plugin. I realize this needs to be run from the plugin thread, and I've tried to pass it back to the main thread both by using [NSObject performSelectorOnMainThread] and [NSObject performSelector:onThread] with the thread i've stored away in the main plugin threads create method. Both of these solutions still crash on any npn call. Is there anything else that goes on when handling a CGEventTap event that causes state to be invalid for NPN browser interaction calls?
Don't try to second guess the threading model by saving the thread like you are; just use performSelectorOnMainThread to call NPN methods. I do this all the time and it works fine, so I'm guessing that something with your method of cross-thread marshalling isn't working the way it needs to.

MACOSX - When calling -finishSyncing on an ISyncSessionDriver the app makes repeated calls to the delegate

I'm using Sync Services in my application. I'm using the normal way of getting the contacts from the address book (using sync services).
I want to prematurely end a sync session if the user decides to do that, therefore, when the user presses the "cancel" button, I make a call to [driver finishSyncing]
Attached to the ISyncSessionDriver is a delegate which deals with delegate methods typical of a sync session. One of those methods is - (BOOL)sessionDriver:(ISyncSessionDriver *)sender willFinishSessionAndReturnError:(NSError **)outError
The problem is that when calling finishSyncing, the sessionDriver:willFinishSessionAndReturnError: gets repeatedly called, not just once, but hunderds of times. Eventually it will throw an error.
So, how could I fix this, or what better debugging can I do to figure out what the problem is?
Thanks
Use instead - (void)cancelSyncing.
Make sure to release the receiver soon afterward because you cannot continue using a canceled session.

How can a Mac app determine the method used to launch it?

I have a Mac OS X application that is also a protocol handler (just as, for example, Safari is a protocol handler for the HTTP and HTTPS protocols). So when a user clicks a link of the form myscheme://some-kind-of-info in any application at all, my application launches to handle the link.
Now I need to be able to determine if the application was launched by such a link click, or if it was launched by any other method. In other words, it was launched by any method besides a link click. (In those cases, I want the app to stay open, but if it was launched by a link it should quit and ignore the link. This way it only operates when already running.)
Is there some way within the app at startup to introspect and find out that it was launched by a standard method rather than by an AppleScript GetURL event? I'd like to find out through a documented method, rather than - for example - just have my app only open these links after it's been running for a half a second.
You can register a handler for each of the possible Apple Events you'll get on launch, and make note of which one you receive first.
If the application is launched without documents, you'll get kAEOpenApplication.
If it's launched with documents, you'll get kAEOpenDocuments (or
kAEPrintDocuments).
If it's launched with a URL, then (obviously) you'll get kAEGetURL.
There's also kAEOpenContents, but I wasn't able to trigger it easily in my test app; it's probably worth supporting no matter what.
How Cocoa Applications Handle Apple Events documents all of this stuff.
There is one error in there, though; it says that AppleScript's "launch" will send kAEOpenApplication. It won't, it'll send ascr/noop (kASAppleScriptSuite/kASLaunchEvent, defined in ASRegistry.h). I couldn't get the usual Cocoa event handler mechanism to trap this event, so you may need to do some more digging there.
One way you can check if the event is sent at launch is to register the event handlers in your application delegate's applicationWillFinishLaunching: method; they should deliver by the time applicationDidFinishLaunching: is invoked. With that method, you could potentially only check for kAEGetURL.

netServiceBrowserDidStopSearch not called

I'm now writing a Bonjour service listener class, according to the document here:
Currently, it seems working, I can receive "netServiceBrowserWillSearch:" and "didFindService:moreComing:" correctly. However, after a long wait, I cannot receive " netServiceBrowserDidStopSearch:" or "netServiceBrowser:didNotSearch:". Therefore I don't know that is the proper time for my delegate class to stop showing some UI.
Could anyone have an idea for this? Thanks.
NSNetServiceBrowser doesn't stop browsing (and call the -netServiceBrowserDidStopSearch: delegate method) until you explicitly tell it to by calling -stop. After it's found the initial services, it continues informing you as new matching services are added or old ones disappear.
How you handle this depends on how you want your app to behave. If you have a window that continuously shows the available services (e.g. like the Bonjour window in iChat), then it's best to let it continue, and contiuously update the list in response to delegate messages. If you've got more like a dialog that gets populated and then goes away once the user makes a selection (e.g like the system Add Printer... dialog), then you want to keep the browser running while it's displayed, then call -stop once the user dismisses it. If you're waiting to find just one specific service, then you can call -stop once you've found and resolved it.

Resources