Reputation: 51
I know you can use this service to have devices like smart watches intercept notifications from iOS devices. But can you receive these iOS notifications on a Mac through OS X?
I want to be able to have my OS X program detect a specific notification type that is received in iOS. I tried browsing for the ANCS device on my Mac, but it didn't show up. I know you can't do this between iOS devices, so I was wondering if maybe the same was true between iOS and OS X or not? Thanks!
Upvotes: 5
Views: 1496
Reputation: 968
It's definitely possible!
Here's what you need:
CoreBluetooth
and uses CBPeripheralManager
to advertise a dummy service with some custom UUID (not the ANCS UUID, it won't work). This dummy service is required for your Mac to "see" the ANCS service.*An app on your Mac which imports IOBluetooth
, initiates a CBCentralManager
object, and starts a scan. You can do this as so:
[self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:YOUR_CUSTOM_SERVICE_UUID]] options:@{CBCentralManagerScanOptionSolicitedServiceUUIDsKey:@[[CBUUID UUIDWithString:ANCS_SERVICE_UUID]]];
Make sure you set yourself up as a delegate to CBCentralManager
to receive the delegate callbacks.
When your Mac discovers your device in didDiscoverPeripheral
, connect to it:
[self.centralManager connectPeripheral:peripheral options:nil];
1 very important thing to note here is you need to retain your peripheral to a property if you wish to connect to it, otherwise it will be dealloc'ed. See this answer for a more detailed discussion.
In didConnectPeripheral
, you need to set yourself up as a delegate to the CBPeripheral
you're connected to then start discovering services:
[peripheral discoverServices:nil];
(All the callbacks from this point onward are for CBPeripheral
)
In didDiscoverServices
, you should get a list of available services. Loop through them as so and discover each service's characteristics:
for (CBService *service in peripheral.services) {
if ([service.UUID isEqual:[CBUUID UUIDWithString:YOUR_CUSTOM_SERVICE_UUID]]) {
NSLog(@"Found your Custom Service");
}
if ([service.UUID isEqual:[CBUUID UUIDWithString:ANCS_UUID]]) {
NSLog(@"Found ANCS Service");
}
[peripheral discoverCharacteristics:nil forService:service];
}
In didDiscoverCharacteristicsForService
, you want to look for 3 characteristics:
9FBF120D-6301-42D9-8C58-25E699A21DBD
(notifiable)69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9
(writeable with response)22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB
(notifiable)For those notifiable characteristics, subscribe to them for updates:
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:ANCS_CHARACTERISTIC_UUID]]) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
If you want to check if those characteristics are have started notifying, do a if(characteristic.isNotifying)
in didUpdateNotificationStateForCharacteristic
.
You will get the actual NSData
updates in didUpdateValueForCharacteristic
with characteristic.value
. The important thing to note here is that you will get informed of notification events by the Notification Source characteristic, but these will not contain that much information. If you want your Mac to play a sound or flash some Hue lights or something like that for every iOS notification, this will suffice. However, for the actual notification details, it will need to come from the Data Source characteristic, but you need to request for them by making very specific calls to the Control Point characteristic. This is where it gets really complicated, and you'll be able to get more information in the official ANCS Specification document.
If you want a shortcut or a look at how others have done it, check out these Github repos:
Just be careful as you may find bugs in some of these implementations, mainly in the processing of data sent by the ANCS Data Source (I had to get creative with my own error handling).
*** Some may argue that you can use "Service Solicitation" to expose ANCS without having an app running on the iOS device and/or without advertising a dummy Service (see options
parameter in Step 2), but I haven't had that much success with it so perhaps there's something I'm missing.
Upvotes: 9