Search code examples
objective-cmacoscocoaipcdistributed-objects

How Do I Do Distributed Objects on OSX with Objective C?


As of 2016, the Apple docs on this are stale and don't work. For instance, they mention "retain", but in XCode 7.1 the default is to use ARC and it doesn't support "retain". I tried various examples on the web and none worked. How do I code the IPC mechanism called Distributed Objects on OSX, where a client application can call class methods on a server application (like one especially composed in a LaunchDaemon, but not required)?


Solution

  • Here's a code sample to get you going. The server.mm project is probably best that you load it into a LaunchDaemon. I ran some tests with the daemon running as root user, and sure enough the client application, which was running as "mike", ran the code in the daemon as "root". So, it enables privilege elevation. Note that this IPC doesn't provide any protocol encryption or authentication challenges -- so, it's up to you to add that yourself. You can probably get away with a key/list, XML, or JSON message that's encrypted with AES256 + Base64 encoding with a long, tough password phrase both on sending and receiving. Remember, with privilege elevation, it's very important that you put some protection mechanisms in place.

    Launch the server first and it will sit there, waiting on connections. Launch the client next and it will establish a connection, pass data to a sample class method, wait and receive a message back, and then display it and shut down. The server will also show the connection was made and what was received on the server before a response back was sent.

    Note that this is a synchronous example, meaning you call the class method and it waits for a response. If you want it to be asynchronous, then you should read the Apple documentation on the oneway keyword. You put it in both the client and server in the class method declaration. Just note that the oneway keyword is really only best used with a class method that returns void because you can't get a response back on an asynchronous class method. So, you'd do an async call to start a task, and then use a synchronous call to get a status update on that task you started. So, here's an example of a class method declaration that would have the oneway keyword added:

    - (oneway void)runTaskAsync:(NSString *)sParam;
    

    And now, the code...

    server.m

    #import <Foundation/Foundation.h>
    
    #define cat stringByAppendingString
    
    @interface MyService : NSObject {
        NSConnection *connection;
    }
    @end
    
    @implementation MyService 
    
    - (NSString *)testResponse:(NSString *)s {
        NSLog(@"...connection:%@", s);
        s = [s cat:@"-response"];
        return s;
    }
    
    - (void)runService {
        connection = [[NSConnection alloc] init];
        [connection setRootObject:self];
        [connection registerName:@"com.acme.myservice"];
        [[NSRunLoop currentRunLoop] run];
    }
    
    @end
    
    int main (int argc, const char *argv[]) {
        @autoreleasepool {
            NSLog(@"ACME MyService 1.0\n");
            MyService *svc = [[MyService alloc] init];
            [svc runService];
        }
        return 0;
    }
    

    client.m

    #import <Foundation/Foundation.h>
    
    int main (int argc, const char *argv[]) {
        @autoreleasepool {
            NSLog(@"building proxy object");
            id proxy = [NSConnection rootProxyForConnectionWithRegisteredName:@"com.acme.myservice" host:nil];
            NSLog(@"calling test response thru proxy object");
            NSString *sResult = [proxy testResponse:@"sent"];
            NSLog(@"RESULT=%@", sResult);
        }
        return 0;
    }