Bluetooth LE iOS unable to scan in background

1.5k Views Asked by At

Setup, uniqueness of the situation, and success scanning in foreground

I am trying to pick up bluetooth readings from a CBPeripheral device. What is interesting about this particular device is that it sends out UUIDs that are embedding information in them directly. (This seems strange, but I don't know enough about BLE to know whether or not that's typical. There is no actual connection occurring in the end - only advertisements are being sent out.)

In the foreground, I am able to identify which readings are mine because [peripheral name] remains constant. So I'm able to pick up the readings that are relevant by doing the following check inside of didDiscoverPeripheral:

if ([[peripheral name] isEqualToString:@"UNIQUE_IDENTIFIER"]) {
    NSLog(@"*** Got a reading ***");
}

This is working nicely in the foreground - I am running

[self.centralManager scanForPeripheralsWithServices:nil options:nil];

in a loop (details there seem irrelevant) and the code is able to print out all of the readings that I expect it to print.

Trouble scanning in background and attempts to solve

My app's Info.plist contains the following entry:

I have also check the (what I think is) the appropriate field in the Capabilities tab in XCode:

Neither of these things have turned out to be useful. didDiscoverPeripheral is called perfectly up until the point where I close my application, at which point it stops immediately.

One other thing that I read on the internet was that background processing still won't happen when scanForPeripheralsWithServices is called with nil, however if a non-empty array of services is passed instead, then it should succeed. I'm not sure if this is correct. The problem is, I have only been able to find examples of calling scanForPeripheralsWithServices where the services are identified by their UUIDs, for example

NSArray *services = @[[CBUUID UUIDWithString: @"2456f1b9-26e2-8f83-e744-f34f01e9d701"]];
[self.centralManager scanForPeripheralsWithServices:services options:nil];

but not their name property.

The question

So I guess my question, after all of this, would be: Would providing an array of name filters (instead of UUIDs) help the application to run in the background, and if so, how would I write that in code? If not, what am I missing such that my app still only works in the foreground?

EDIT: I had previously used the terminology "identifier" when I think I meant to say "name", so I went back and changed those. From the documentation, here is the name value that I would like to scan for in the background https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519029-name?language=objc

1

There are 1 best solutions below

4
Paulw11 On BEST ANSWER

In order to scan for new devices in the background you must specify the UUIDs of the service(s) that you are interested in. The documentation is quite clear on this.

The identifier for a device is a value locally determined by iOS, not the device itself and will vary for different iOS devices connecting to the same peripheral. If you know the identifier for a device that you have previously discovered then you can use the identifier to connect to the device but you can’t scan for an identifier.

It is possible for a peripheral to include data in its service advertisement and perhaps this is what your peripheral is doing. If so, then you will not be able to get readings in the background since duplicate device discovery events are not delivered when your app is in the background.

You can discover and connect to a new device in the background as long as you know the service that it is advertising but any subsequent data transfers will require the device to issue a notify on a changed characteristic. It cannot use the manufacturer data portion of the advertising frame.