I have been at this over 3 months and pulling my hair out. So please don't respond with beginner answers.
I consider terminated to be when user swiped the app in task manager or when the peripheral is turned on/off and the app was already dead
I need important health related BT peripheral data (recorded by BT device) maintained in the app so I need a consistent connection or the ability to wake the app back up and handle the data. I know this is asked a lot so I am trying to find the most current understanding or solutions to this problem. I have read sooo many articles and S.O. posts on this so I know Core Bluetooth is un-reliable at best. I know the general concept is flaky and people have been saying since 2010 its not possible. However, lots keeps changing in iOS so I was hoping something would have changed.
To be clear:
BT wake up would be great but it's really not been reliable, so... I will take ANY kind of reliable wake up (location, audio, BT, etc... NOT iBeacon though since I am connected/paired to BT device). If I have to "hack" the wake up to happen on location or audio and then quickly get the data from the peripheral somehow, I will take it!
(SKIP THIS IF YOU DON'T CARE OR IS NOT APPLICABLE)
Using full state restoration, that is to say, this code...
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:@{CBCentralManagerOptionShowPowerAlertKey: @(YES),
CBCentralManagerOptionRestoreIdentifierKey:@"MyDevice"}];
To register the identifier key and this code...
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(@"launch options found: %@", launchOptions);
NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];
NSLog(@"central managers found in launch options: %@", centralManagerIdentifiers);
[self triggerLocalNotification:[NSString stringWithFormat:@"central managers found in launch options: %@", centralManagerIdentifiers]];
if([centralManagerIdentifiers count] > 0) {
for(NSString *identifier in centralManagerIdentifiers) {
if([identifier isEqualToString:@"MyDevice"]) {
[self triggerLocalNotification:[NSString stringWithFormat:@"Identifier found: %@", identifier]];
self.bluetoothManager = [BluetoothMgr sharedInstance];
}
}
}
return YES;
}
- (void)centralManager:(CBCentralManager *)central
willRestoreState:(NSDictionary<NSString *,id> *)state {
NSLog(@"************** RESTORED STATE BT **************");
[self triggerCustomLocalNotification:@"************** RESTORED STATE BT **************"];
NSLog(@"central manager object: %@", central);
NSLog(@"state dictionary: %@", state);
[self triggerCustomLocalNotification:[NSString stringWithFormat:@"state dictionary: %@", state]];
NSArray *restoredPeripherals = [state objectForKey:@"CBCentralManagerRestoredStatePeripheralsKey"];
self.centralManager = central;
self.centralManager.delegate = self;
if([restoredPeripherals count] > 0) {
for(CBPeripheral *peripheral in restoredPeripherals) {
if([peripheral.name rangeOfString:@"mybox-"].location != NSNotFound) {
NSLog(@"Restoring mybox Box: %@", peripheral);
[self triggerCustomLocalNotification:[NSString stringWithFormat:@"Peripheral was found in WILL RESTORE STATE! it was: %@", peripheral]];
self.myPeripheral = peripheral;
self.myPeripheral.delegate = self;
[self connectToDevice];
return;
}
}
}
}
To restore the central manager state. This only works when the app is killed by iOS or the state is changed. Does not work when the user kills the app.
Subscribing to a notifying characteristic in the device (I made this custom characteristic and I have full control over the programming of the device) ... this works really well but does not always wake the app up. Works well in background though. Just not terminated.
Finally solved this problem! The solution was to use 2 Bluetooth chips in my solution. One chip to be a dedicated BT-Connected Paired/Auth/Bonded device and the other to be a dedicated iBeacon advertiser. With this solution I was able to both, wake up the app whenever I want (by power cycling the iBeacon chip at will) and connecting for BT encryption required characteristics.
Using the didEnterRegion
method of the CLLocationManager
class, in the background, I can start up the bluetooth manager... connect to the device in the background and then retrieve data successfully over a previously paired connection.
UPDATE: as a side note, it's good to mention that while the iBeacon is fairly reliable in waking up the application in the background, only the didEnterRegion
method happens immediately when the iBeacon is found or turned on. The didExitRegion
method takes me (on average) about 30 seconds to fire after I turn off the iBeacon or it is no longer in range.