yfrancis
yfrancis

Reputation: 2616

How to receive notifications posted via CPDistributedNotificationCenter

The private AppSupport framework on iOS has a class called CPDistributedNotificationCenter which appears to support a subset of the functionality provided by NSDistributedNotificationCenter on OS X.

I'm attempting to use this class to post notifications from a background daemon such that multiple clients in other processes can receive these notifications and act on them. I realize there are other options, including CPDistributedMessagingCenter or CFMessagePort, low level mach ports or even darwin's notify_post. I'd prefer it if the daemon had no knowledge of the clients however, and I'd like to be able to pass data along with the notification, and notify_post does not allow this.

Currently, this is what I am doing in the daemon:

CPDistributedNotificationCenter* center;
center = [CPDistributedNotificationCenter centerNamed:@"com.yfrancis.notiftest"];
[center runServer];
[center postNotificationName:@"hello"];

And in the client:

CPDistributedNotificationCenter* center;
center = [CPDistributedNotificationCenter centerNamed:@"com.yfrancis.notiftest"];
[center startDeliveringNotificationsToMainThread];

NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:[Listener new] 
       selector:@selector(gotNotification:)
           name:@"hello"
         object:nil];

where Listener is a simple class that implements a single method gotNotification:

Unfortunately, the client never receives the 'hello' notification. If I replace the name argument in the addObserver call with nil I can see every notification delivered to the client's notification center, but 'hello' is not one of them.

I got the inspiration for my code by looking at a disassembly of SpringBoard and CPDistributedNotificationCenter. Notifications appear to be delivered via CPDistributedNotificationCenter's deliverNotification:userInfo: which acts as a shim for NSNotificationCenter's postNotificationName:object:userInfo:.

What am I missing here?

Upvotes: 4

Views: 1685

Answers (1)

yfrancis
yfrancis

Reputation: 2616

Figured it out. Your daemon has to wait for a notification indicating that a client has started listening, before sending out your notification. There is no backlog, even if the daemon server runs before the client, there is a registration delay. You can't simply start your server and immediately post notifications to listeners. The following works for me:

In the server init:

self.center = [CPDistributedNotificationCenter centerNamed:@"com.yfrancis.notiftest"];
[self.center runServer];

// requires a runloop
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
       selector:@selector(clientEvent:)
           name:@"CPDistributedNotificationCenterClientDidStartListeningNotification"
         object:self.center];

And make sure to implement the following method in the server:

- (void)clientEvent:(NSNotification*)notification
{
    // you can now send notifications to the client that caused this event
    // and any other clients that were registered previously
    [self.center postNotificationName:@"hello!"];
{

I've documented this API on the iPhoneDevWiki

Upvotes: 5

Related Questions