Search code examples
objective-ccocoaipcsandboxcore-foundation

Cocoa/App Group Interprocess Communication via CFMessagePort


I'm trying to pass a string (though my real goal is to pass an array) from one macOS app to the other. The receiver app is sandboxed while the sender app is not sandboxed.

I've added an App Group (DEV_TEAM_ID.com.if.APP_NAME.UNIQUE_NAME) to both apps.


In the receiver app, I have implemented creating the port and runloop:

CFMessagePortRef localPort =
    CFMessagePortCreateLocal(nil,
                             CFSTR("DEV_TEAM_ID.com.if.APP_NAME.UNIQUE_NAME"),
                             PortCallBack,
                             nil,
                             nil);

CFRunLoopSourceRef runLoopSource =
    CFMessagePortCreateRunLoopSource(nil, localPort, 0);

CFRunLoopAddSource(CFRunLoopGetCurrent(),
                   runLoopSource,
                   kCFRunLoopCommonModes);

and the callback, of course:

CFDataRef PortCallBack(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) {
     char *message = "Thanks for saying hello!";
     CFDataRef returnData = CFDataCreate(NULL, (const UInt8 *)message, strlen(message)+1);
     printf("here is our received data: %s\n", CFDataGetBytePtr(data));
     return returnData;
}




In the sender app, I have implemented:

CFMessagePortRef remotePort = CFMessagePortCreateRemote(nil, CFSTR("DEV_TEAM_ID.com.if.APP_NAME.UNIQUE_NAME"));
SInt32 messageIdentifier = 1;
CFDataRef messageData = (__bridge CFDataRef)[@"hello, friend." dataUsingEncoding:NSUTF8StringEncoding];
SInt32 status = CFMessagePortSendRequest(remotePort, messageIdentifier, messageData, 1000, 0, NULL, NULL);

if (status == kCFMessagePortSuccess)
{
    NSLog(@"success");
} 




The receiver app launches fine, but the sender app crashes on this line:

SInt32 status = CFMessagePortSendRequest(remotePort, messageIdentifier, messageData, 1000, 0, NULL, NULL);



EDIT: It seems that remotePort is null, and is causing the crash with the following error:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x8)

CoreFoundation`CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER: 0x7fff20707fe0 <+0>: testq %rdi, %rdi 0x7fff20707fe3 <+3>: je 0x7fff20707fed ; <+13> 0x7fff20707fe5 <+5>: pushq %rbp 0x7fff20707fe6 <+6>: movq %rsp, %rbp 0x7fff20707fe9 <+9>: callq *0x10(%rdi) -> 0x7fff20707fec <+12>: popq %rbp 0x7fff20707fed <+13>: retq
0x7fff20707fee <+14>: nop
0x7fff20707fef <+15>: nop
0x7fff20707ff0 <+16>: nop
0x7fff20707ff1 <+17>: nop
0x7fff20707ff2 <+18>: nop
0x7fff20707ff3 <+19>: nop
0x7fff20707ff4 <+20>: nop
0x7fff20707ff5 <+21>: nop
0x7fff20707ff6 <+22>: nop




I'm not sure why remotePort would have a null value. I'm using the same port name in both apps.

I've been trying to use code examples and other posts here on Stackoverflow to solve the issue, but I don't understand where I've messed it up. I would greatly appreciate any advice! Thanks!

Posts I've looked at:
https://nshipster.com/inter-process-communication/
CFMessagePort and sandboxing
Theos inter-app communication using mach ports
https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups


Solution

  • I was able to sort this out after reading Using App Groups for communication between macOS/iOS apps from the Same Vendor

    The problem was that as of Big Sur (or possibly Catalina), macOS apps using App Groups require a group container to be created in the developer portal. after doing that, you need to reconfigure the code-signing your app to include a provisioning profile. I'm not really sure when this all became a requirement since setting up App Groups isn't really well-documented by Apple. Everything works great after following the steps in the above linked article.

    I was able to get my array passed from one app to another:

    NSData* myData = [NSKeyedArchiver archivedDataWithRootObject:self.runningProcs];
                
    CFMessagePortRef remotePort = CFMessagePortCreateRemote(kCFAllocatorSystemDefault, CFSTR("group.DEV_TEAM_ID.com.if.APP_NAME.UNIQUE_NAME"));
    SInt32 messageIdentifier = 1;
    CFDataRef messageData = (__bridge CFDataRef)(myData);
        
    SInt32 status = CFMessagePortSendRequest(remotePort, messageIdentifier, messageData, 1000, 0, NULL, NULL);
    
    if (status == kCFMessagePortSuccess)
    {
          NSLog(@"success");
    }