2 // XPCNotificationDispatcher.m
5 // Created by Mitch Adler on 11/1/16.
9 #import "XPCNotificationDispatcher.h"
10 #include <dispatch/dispatch.h>
12 #include <utilities/debugging.h>
17 // PointerArray helpers
19 @interface NSPointerArray (Removal)
20 - (void) removePointer: (nullable void *)pointer;
23 @implementation NSPointerArray (Removal)
24 - (void) removePointer: (nullable void *)pointer {
26 while(pos < [self count]) {
27 if (pointer == [self pointerAtIndex:pos]) {
28 [self removePointerAtIndex:pos];
39 static const char *kXPCNotificationStreamName = "com.apple.notifyd.matching";
40 static const char *kXPCNotificationNameKey = "Notification";
42 @interface XPCNotificationDispatcher ()
43 @property dispatch_queue_t queue;
44 @property NSPointerArray* listeners;
46 - (void) notification: (const char *) value;
51 @implementation XPCNotificationDispatcher
53 + (instancetype) dispatcher {
54 static dispatch_once_t onceToken;
55 static XPCNotificationDispatcher* sDispactcher;
56 dispatch_once(&onceToken, ^{
57 sDispactcher = [[XPCNotificationDispatcher alloc] init];
63 - (instancetype) init {
64 if ((self = [super init])) {
65 self.queue = dispatch_queue_create("XPC Notification Dispatch", DISPATCH_QUEUE_SERIAL);
66 self.listeners = [NSPointerArray weakObjectsPointerArray];
67 __weak typeof(self) weakSelf = self;
69 xpc_set_event_stream_handler(kXPCNotificationStreamName, self.queue, ^(xpc_object_t event){
70 const char *notificationName = xpc_dictionary_get_string(event, kXPCNotificationNameKey);
71 if (notificationName) {
72 [weakSelf notification:notificationName];
80 - (void) notification:(const char *)name {
81 [self.listeners compact];
82 [[self.listeners allObjects] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
83 [obj handleNotification: name];
88 - (void) addListener: (NSObject<XPCNotificationListener>*) newHandler {
89 dispatch_sync(self.queue, ^{
90 [self.listeners compact];
91 [self.listeners addPointer:(__bridge void * _Nullable)(newHandler)];
95 - (void) removeListener: (NSObject<XPCNotificationListener>*) existingHandler {
96 dispatch_sync(self.queue, ^{
97 [self.listeners removePointer:(__bridge void * _Nullable)existingHandler];