31

I have been trying to setup an app to make the device both scan for peripherals and advertise as a peripheral. The goal is for two devices to be woken up in the background when they become near each other via bluetooth discovery. From the Apple Documentation, it seems that you should be able to run BLE in the background (with bluetooth-central and bluetooth-peripheral background modes enabled), and my application works when one device is in the foreground. First, I advertise data like so:

NSDictionary *advertisingData = @{CBAdvertisementDataLocalNameKey:@"my-peripheral",
                              CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:identifier]]};

// Start advertising over BLE
[peripheralManager startAdvertising:advertisingData]; 

I then set the device to scan for data:

NSArray *services = @[[CBUUID UUIDWithString:identifier]];

[centralManager scanForPeripheralsWithServices:services options:nil];

However, when both go into the background (device has to be locked), the bluetooth cannot discover and

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI

never gets called on either device. How can I fix this? Thanks

Kyle Rosenbluth
  • 1,672
  • 4
  • 22
  • 38

3 Answers3

51

I'm afraid what you are trying to do will not work. I have tried to achieve the same thing.

The problem is the difference in scanning in foreground and background. When you are scanning for devices in the foreground you can scan for anything. In the background you must specify the actual service UUID you are scanning for. Ok, this isn't actually a problem as you know the UUID you are looking for.

Peripheral: Broadcasting as a peripheral again works differently in foreground and background. In foreground it works like any normal BT peripheral. In the background it has a very limited amount of space to work with, so your peripherals UUID is hidden away and not broadcast. Only when a central device (an iPhone in foreground) requests the information from it will it wake your app and show it's UUID.

So the 2 cancel each other out. As your background scan can only scan for devices with a specific UUID and your background peripheral cannot advertise its UUID, they cannot see each other.

1 of your devices (either peripheral or central) must be in the foreground to work.

This has been discussed several times on the Apple Bluetooth mail list.

Darren
  • 10,182
  • 20
  • 95
  • 162
  • 3
    Thanks Darren. That's very unfortunate, and also a bit upsetting that Apple isn't more clear about that. Are there any workarounds that you know of to use BLE in the background on both devices? Also, do you know why the scanning works when my iphone is on the lock screen (and doing other things on the phone NOT in the app)? I thought this was considered background behavior and thus would not work? – Kyle Rosenbluth Dec 08 '13 at 23:01
  • 1
    You can read more about it here https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html#//apple_ref/doc/uid/TP40013257-CH7-SW1 it mentions a 'special overflow area' for the UUID's. – Darren Dec 08 '13 at 23:17
  • 1
    There are no workarounds for this as far as I know. I have 2 app ideas that need this and I spent a week trying to get it working with no luck. Its a bit hit and miss at which point you can and can't find it whilst backgrounded. I think it depends on what the device is up to. Also if you've previously connected to the peripheral it also affects things. – Darren Dec 08 '13 at 23:18
  • Just to add, if you continue with your project using foreground for one of the devices, be sure to use the Restoration mode in CoreBluetooth to make the central/peripheral truly be available all the time. – Darren Dec 08 '13 at 23:20
  • 1
    Why has this been down voted? It might not be the answer people want, but it is the correct one! – Darren Dec 09 '13 at 19:28
  • Darren, I'm sorry that this answer has been downvoted. I don't know why because it seems like it is indeed the correct answer. – Kyle Rosenbluth Dec 09 '13 at 19:54
  • There have been some updates in the latest iOS. Sometimes I remember (I was surprised too) that I got this scenario working. I'll need to check it again to be sure, though. – allprog Dec 09 '13 at 21:14
  • allprog, I would be extremely grateful if you figured out how to do it and are willing to share. Thanks in advance! – Kyle Rosenbluth Dec 09 '13 at 21:35
  • Ok, I just tested again but it seems to be even worse than I remembered. As long as the central was backgrounded no discovery happened. (I left the central lie for a little bit before started testing.) Then in a next run even the connection happened. This needs more testing, I'm sure I messed something up. Will try again tomorrow. But seems like Darren's statements still hold, unfortunately. – allprog Dec 09 '13 at 23:35
  • @Darren no offence, but everyone will argue that their answer is "the correct one", your view on the matter is biased. Apparently there was 1 person who disagreed ;) – Tim Mar 14 '16 at 15:37
8

You should elaborate on how you're testing this, because theoretically it looks like it should work. There's two primary issues you may be facing:

1.) Scanning is throttled down when iOS devices are in the background.

  • While scanning in the foreground will likely immediately discover a device advertising next to it, discovery in the background can take up to ~60 times longer. The iOS system makes no assumptions that the user would prefer one app to have better Bluetooth functionality than another (or that only one app wants to use it). And since it is shared functionality, they want users to have a uniform experience across apps. You should check out the technical specifications regarding Advertising and Scanning intervals to get a better idea of what Apple has to do under the covers.

2.) Your devices may have already discovered each other before entering the background.

  • We must remember that Apple disables the CBCentralManagerScanOptionAllowDuplicatesKey scanning flag when we enter the background. Since you're not even specifying this flag, it defaults to NO anyways. So if they've even seen each other once, you will not get another callback when they are in the background.
Tommy Devoy
  • 13,441
  • 3
  • 48
  • 75
  • 1
    Thanks for the response. The way I test the app is to run it on my iPad mini, lock the iPad, then walk about 200 feet. I then load it onto my iPhone. With the app in the foreground on the iPhone (plugged into my computer with a breakpoint at `didDiscoverPeripheral`), I start to walk forward until I hit the breakpoint. I then repeat this process but with the app in the background and the iPhone locked. XCode hits no breakpoints. Surprisingly, if I go into the lock screen, the breakpoint immediately gets hit. In regard to your 2 points, I can't see a problem with my method. Any ideas? – Kyle Rosenbluth Dec 08 '13 at 21:52
  • 2
    @KyleRosenbluth I'm testing a similar thing by just switching the BT on and off. – vaughan Nov 12 '14 at 10:38
5

I personally needed such a feature and I developed an open source component: https://github.com/omergul123/Discovery

It might be very helpful if you want to exchange an ID even if the peer apps are running at background.

Ömer Faruk Gül
  • 965
  • 1
  • 10
  • 22