I've started writing my first Apple Watch application (in Swift, of course!), which requires a real-time synchronization between two devices. iOS provide several alternatives to share data and send signals between the host application and the extension, but I've concluded that the combination of shared storage (using App Group) and the Darwin notification is the best answer to my application.
Since the Darwin notification is a C-based API and requires a callback function, it's quite hard to use from Swift. Therefore, I've created a simple bridge class in Objective-C, which allows Swift application to use the Darwin notification very easily (MMWormhole is a nice library, but comes with extra service).
When you want to send a signal (across app/extension boundary), you just need to create an instance of DarwinNotification, and call its postNotification method with a unique identifier.
let darwin = DarwinNotification() darwin.postNotification("com.foobar.myidentifier")
On the receiver side, you need to create an instance of DarwinNotification with this unique identifier as a property of your model or controller instance, and call addObserver function with a block, which will be called as the result of notifications.
let darwin = DarwinNotification(identifier: "com.foobar.myidentifier") ... darwin.addObserver { [unowned self] (n: NSNotification!) -> Void in ... }
Notice that you must specify [unowned self] at the beginning of the block to avoid the circular references.
You may use the same set of instances for two-way notifications, but you must use two difference identifiers.
Here is the implementation:
DarwinNotification.h
#import <Foundation/Foundation.h> @interface DarwinNotification : NSObject -(id) initWithIdentifier:(NSString*)identifier; -(void) postNotification:(NSString *)identifier; -(void) addObserver:(void (^)(NSNotification *note))callback; @end
DarwinNotification.m
#import "DarwinNotification.h" @interface DarwinNotification() { NSString* _identifier; } @end @implementation DarwinNotification -(id) initWithIdentifier:(NSString*)identifier { if (self = [super init]) { _identifier = identifier; [self _register]; } return self; } - (void) postNotification:(NSString *)identifier { CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter(); CFDictionaryRef const userInfo = NULL; BOOL const deliverImmediately = YES; CFStringRef str = (__bridge CFStringRef)identifier; CFNotificationCenterPostNotification(center, str, NULL, userInfo, deliverImmediately); } -(void) addObserver:(void (^)(NSNotification *note))callback { NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center addObserverForName:_identifier object:nil queue:nil usingBlock:callback]; } - (void) _register { CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter(); CFStringRef str = (__bridge CFStringRef)_identifier; CFNotificationCenterAddObserver(center, (__bridge const void *)(self), notificationCallback, str, NULL, CFNotificationSuspensionBehaviorDeliverImmediately); } - (void) _unregister { CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter(); CFStringRef str = (__bridge CFStringRef)_identifier; CFNotificationCenterRemoveObserver(center, (__bridge const void *)(self), str, NULL); } void notificationCallback(CFNotificationCenterRef center, void * observer, CFStringRef name, void const * object, CFDictionaryRef userInfo) { NSString *identifier = (__bridge NSString *)name; [[NSNotificationCenter defaultCenter] postNotificationName:identifier object:nil userInfo:nil]; } - (void)dealloc { if (_identifier) { [self _unregister]; } CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter(); CFNotificationCenterRemoveEveryObserver(center, (__bridge const void *)(self)); } @end
Thanks, I found this useful.
Steve Sheets
Posted by: Steve Sheets | 07/05/2015 at 09:00 PM