Core Bluetooth advertise and scan in the background - ios

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

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.

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.

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.

Related

Can iOS app act as beacon, even when it is running in the background?

I need my app to act as a BLE beacon, even when it is in the background.
I've already made the beacon part - works fine when I'm using the app.
The problem is I've never made anything run in the background before, and not sure what the limitations are - if I will be allowed to let the app act as a beacon in the background.
Was wondering if this is possible or not (both technically, as well according to App Store rules)
Is this technically possible ? Can my app act as a beacon, even when the app is not in the foreground.
Is it within App Store rules ?
Unfortunately, iOS apps cannot emit iBeacon advertisements unless they are in the foreground. If you program your app to advertise iBeacon frames, as soon as the screen turns off or the app is not visible, iOS changes the advertising format to a special generic background advertisement that does not match the iBeacon format.
This proprietary background advertisement is useless for identification of a device. It is designed for connection to BLE GATT services from other iOS devices. Unless you want to advertise a connectable BLE GATT service to other iOS devices this advertisement has little value. EDIT: There is a way to make use of this advertisement. See below.
Apple's documentation describes this limitation here in the section titled "The bluetooth-peripheral Background Execution Mode":
you should be aware that advertising while your app is in the background operates differently than when your app is in the foreground. In particular, when your app is advertising while in the background:
The CBAdvertisementDataLocalNameKey advertisement key is ignored, and the local name of peripheral is not advertised.
All service UUIDs contained in the value of the CBAdvertisementDataServiceUUIDsKey advertisement key are placed in a special “overflow” area; they can be discovered only by an iOS device that is explicitly scanning for them.
If all apps that are advertising are in the background, the frequency at which your peripheral device sends advertising packets may decrease.
It may not be obvious that the above causes iBeacon transmissions to break, but I assure you that is absolutely a consequence.
Read my blog post here for more information on how to put useful beacon data into this background "Overflow Area" advertisement.

IOS Developpement : Background BLE Precisions on Apple doc

I everyone,
I'm trying to develop two apps that act as a central and a peripheral, which automatically connect to each other when they enter in the bluetooth range while both are in background.
I posted something about my issue (because none of my tests were conclusive) on this post : IOS Developpement : Background BLE scanning.
The answer I always find on the internet directly come out of the Apple documentation. One post example (https://stackoverflow.com/a/20460113/5464805):
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.
But there is something I don't understand: In my central, I already specify the UUID [centralmgr scanForPeripheralsWithServices:[NSArray arrayWithObject:servicesUUID] options:scanOptions];. According to the functionning detailed above, it shouldn't connect when the peripheral is in background and the central in foreground, but it does !
Does somebody have an idea on what is happening in this specific case ? Or maybe a solution to make my two devices connect while in background ?
Thank you!
PS: Also the answer I found is pretty old (2013).
I think that when the central is in the foreground it can wake the peripheral up to retreive the UUID when the peripheral has been detected, but when the central is in the background it cannot. That is why it will not work to have both sides in background mode.

Is it possible to scan for iBeacon advertisement data using Corebluetooth in the "background"?

I want to be able to scan for iBeacons ad data using the CoreBluetooth framework in the background with known proximity UUIDs. I was able to scan for all the peripherals in the FOREGROUND using:
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
[self.centralManager scanForPeripheralsWithServices:nil options:options];
I tried doing this while the app entered background but it did not return any data. Is it even possible to scan for iBeacons using the Corebluetooth framework in the background?
Thanks
From the Core Bluetooth Programming Guide
When an app that implements the central role includes the
UIBackgroundModes key with the bluetooth-central value in its
Info.plist file, the Core Bluetooth framework allows your app to run
in the background to perform certain Bluetooth-related tasks. While
your app is in the background you can still discover and connect to
peripherals, and explore and interact with peripheral data
Although you can perform many Bluetooth-related tasks while your app
is in the background, keep in mind that scanning for peripherals while
your app is in the background operates differently than when your app
is in the foreground. In particular, when your app is scanning for
device while in the background:
The CBCentralManagerScanOptionAllowDuplicatesKey scan option key is
ignored, and multiple discoveries of an advertising peripheral are
coalesced into a single discovery event.
If all apps that are scanning
for peripherals are in the background, the interval at which your
central device scans for advertising packets increases. As a result,
it may take longer to discover an advertising peripheral.
You should confirm that you have specified the background mode key in your project settings and that your CBCentralManager instance is instantiated correctly.
I note, however, that your Bluetooth scanning code isn't particularly efficient even for foreground processing. Shouldn't include the duplicates key unless it is strictly necessary and you should specify the service UUID that you are interested in rather than nil.

How to detect with CoreBluetooth when a peripheral disappears?

I would like to have a list of ble devices to which my iOS can connect, which refreshes when ble devices appear and disappear.
In order to do that, I created an NSMutableDictionnary* peripheralsAvailable, and everytime - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; is called by the CBCentralManager, I add the peripheral to the peripheralsAvailable dictionnary (and then update a UITableView). All is OK here.
However I can't find how I can update the dictionnary if a Peripheral "disappear". It seems that I can only add peripherals in my dictionnary when they are detected, but I can't remove one when I shut it down for example.
Could you tell me if I'm missing something?
The OS will cache discovery of devices. I.e. you will only get one "discovery" event per device.
To continuously get discovery events while the peripheral is advertising, you must use the following option:
CBCentralManagerScanOptionAllowDuplicatesKey
A Boolean value that specifies whether the scan should run without
duplicate filtering.
The value for this key is an NSNumber object. If YES, filtering is
disabled and a discovery event is generated each time the central
receives an advertising packet from the peripheral. Disabling this
filtering can have an adverse effect on battery life and should be
used only if necessary. If NO, multiple discoveries of the same
peripheral are coalesced into a single discovery event. If the key is
not specified, the default value is NO.
Setting the above option to YES, you could keep track of all of the peripherals that are advertising and when it stops advertising, you could remove it from the list.
For a device that you've connected to, there is the didDisconnectPeripheral delegate event.
Bluetooth devices don't advertise that they're about to go away, nor do they advertise that you're about to go out of range. You get an advertisement while they're advertising and you're within range, and you get nothing when you're out of range or they stop advertising. There's no event to trigger on when they're gone. You have to remember the devices that are advertising, and when they stop advertising (you haven't received an advertisement in awhile), you can remove it from the list.
Great answer by Marcus. One additional note to add is that the scan option CBCentralManagerScanOptionAllowDuplicatesKey as mentioned above does not work in the background.
Apps that have specified the bluetooth-central background mode are allowed to scan while in the background. That said, they must explicitly scan for one or more services by specifying them in the serviceUUIDs parameter. The CBCentralManager scan option is ignored while scanning in the background.

iOS - Scan BLE devies in background

I have noticed that scanForPeripheralsWithServices is not working in background. I tried with following:
specified UUID and option nil
set UIBackgroundModes bluetooth-central and bluetooth-peripheral info.plist
I want a background service that should scan BLE devices in background continuously.
Thanks for your help!
Background mode works differently for scanning.
Every peripheral is only reported the first single time it is observed (you cannot track proximity through RSSI without connecting to it while in background mode).
You can initiate a connect request to a peripheral that's not within range, and the connection will complete when the peripheral becomes available. Don't have to actively scan for this (except for initial discovery, so you know which UUID to connect).
Maybe, you can solve the problem by sending a connect request instead of scanning, while in background. This way, iOS knows that you are really interested in a specific peripheral and I could imagine that this affects discovery times.

Resources