Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mac OS X XPC as IPC between two applications

I have a windowed application and windowless helper, sitting inside the app bundle, and working as login item. App can start and stop the helper, everything woks there. The problem is that I need to create some bidirectional communication channel between them. And it should work in both sandboxed and not sandboxed versions, desirably in OS X 10.7+.
I've investigated the topic and find that XPC can provide peer-to-per connection. I've read related Apple docs, as well as few topics below:

Is possible to use Mac OS X XPC like IPC to exchange messages between processes? How?
Communicate with another app using XPC
http://afewguyscoding.com/2012/07/ipc-easy-introducing-xpc-nsxpcconnection/
https://www.objc.io/issues/14-mac/xpc/

But I can't find any description of how should I organize my XCode project. I have two targets: "Main App" and "Helper App". Now I need to add the third one, taking XPC Service, as a template. OK, but what to do next? Where this XPC bundle should be located to be available for both applications? Note, that helper sits in the main app bundle, as it's a login item. So, I need some clear instruction or just a XCode project sample.

Thanks, Alex

like image 912
DeadlineX Avatar asked Jan 21 '17 14:01

DeadlineX


People also ask

What is XPC services on Mac?

macOS uses XPC services for basic inter-process communication between various processes, such as between the XPC Service daemon and third-party application privileged helper tools.

How do I share applications between Macs?

In the Finder or an app on your Mac, select one or more items, then click the Share button in the toolbar. If the item is on the desktop, Control-click it, then choose Share from the shortcut menu. In the Share menu, choose how to share the item.

What is IPC Mac?

(2) (InterProcess Communication) The exchange of data between one program and another either within the same computer or over a network. An IPC protocol generally expects a response to a request. Examples are Dynamic Data Exchange in Windows and Interapplication Communications in the Mac (see DDE and IAC).


1 Answers

Alright for anyone that has been struggling with this, I was finally able to 100% get communication working between two application processes, using NSXPCConnection

The key to note is that you can only create an NSXPCConnection to three things.

  1. An XPCService. You can connect to an XPCService strictly through a name
  2. A Mach Service. You can also connect to a Mach Service strictly through a name
  3. An NSXPCEndpoint. This is what we're looking for, to communicate between two application processes.

The problem being that we can't directly transfer an NSXPCEndpoint from one application to another.

It involved creating a machservice Launch Agent (See this example for how to do that) that held an NSXPCEndpoint property. One application can connect to the machservice, and set that property to it's own [NSXPCListener anonymousListener].endpoint

Then the other application can connect to the machservice, and ask for that endpoint.

Then using that endpoint, an NSXPCConnection can be created, which successfully established a bridge between the two applications. I have tested sending objects back and forth, and it all works as expected.

Note that if your application is sandboxed, you will have to create an XPCService, as a middle man between your Application and the Machservice

I'm pretty pumped that I got this working-- I'm fairly active in SO, so if anybody is interested in source code, just add a comment and I can go through the effort to post more details

Some hurdles I came across:

You have to launch your machservice, these are the lines:

   OSStatus                    err;
   AuthorizationExternalForm   extForm;

   err = AuthorizationCreate(NULL, NULL, 0, &self->_authRef);
   if (err == errAuthorizationSuccess) {
      NSLog(@"SUCCESS AUTHORIZING DAEMON");
   }
   assert(err == errAuthorizationSuccess);

   Boolean             success;
   CFErrorRef          error;

   success = SMJobBless(
                        kSMDomainSystemLaunchd,
                        CFSTR("DAEMON IDENTIFIER HERE"),
                        self->_authRef,
                        &error
                        );

Also, every time you rebuild your daemon, you have to unload the previous launch agent, with these bash commands:

sudo launchctl unload /Library/LaunchDaemons/com.example.apple-samplecode.EBAS.HelperTool.plist
sudo rm /Library/LaunchDaemons/com.example.apple-samplecode.EBAS.HelperTool.plist
sudo rm /Library/PrivilegedHelperTools/com.example.apple-samplecode.EBAS.HelperTool

(With your corresponding identifiers, of course)

like image 107
A O Avatar answered Sep 23 '22 00:09

A O