Gary Bennett
Gary Bennett

Reputation: 31

Core Bluetooth - startScanningForPeripherals not working in background after app terminated and started in background

I have a custom BLE peripheral that turns a LED green when a person enters a building and turns the LED off when they leave the building. The iOS App uses Geofence regions to send the notifications to the app when the person enters and leaves the building. The locationManger didDetermineState gets called on a region change and the BadgeHandler class gets called to update the badge. Everything works great in the foreground and background, until iOS terminates the app.

When the app is terminated by iOS in the background, and later a geofence region notification comes in, the startScanningForPeripherals doesn't work.

The badge (peripheral) runs off 2 watch batteries and to minimize battery drain, we connect to the peripheral (badge) only to update the LED and then disconnects from the peripheral.

This works just fine in the foreground and the background until iOS kills the app while in the background. When the app starts back up in the background on a geofence region change startScanningForPeripherals doesn't result in any delegate notifications after it is called.

I do have a delegate method: willRestoreState This doesn't get call. The reason it doesn't get called is it wasn't scanning for the device or connected to the device when the app was terminated by iOS in the background. Below is the sequence of events and how I am calling these methods:

  1. App is running and placed in the background
  2. App receives a geofence notifications and connects to the badge (peripheral) updates the LED and disconnects from the badge (peripheral). Works as required.
  3. After some time goes by, iOS terminates the app
  4. A Geofence region notification comes in
  5. The application is automatically restarted, (note: the app is still in the background) didFinishLaunchingWithOptions is called and I start the the centralManager:

let cmQueue = DispatchQueue( label: "com.serial-queue") centralManager = CBCentralManager(delegate:self, queue: cmQueue, options: [CBCentralManagerOptionRestoreIdentifierKey:"com.TrueAccess.BLEConnect.CentralManager",CBCentralManagerOptionShowPowerAlertKey:true,CBCentralManagerScanOptionAllowDuplicatesKey:true])

  1. The delegate method: centralManagerDidUpdateState gets called:

else if central.state == .poweredOn{ startScanningForPeripherals(central) }

  1. Start scanning is then called with the the serviceID that it is looking for so scanning can occur in the background.

if central.state == .poweredOn { let serviceUUID:[CBUUID] = [CBUUID(string: "ID Number here")] central.scanForPeripherals(withServices: serviceUUID, options: [CBCentralManagerScanOptionAllowDuplicatesKey : true])

No delegate methods are received after this, i.e.: didDiscover didConnect didFailToConnect

Any help is greatly appreciated.

UPDATE TO QUESTION. I filed my first Technical Solutions Question to Apple in 8 years of iOS programming. This is their response. I will keep this updated.

Although there are many ways that an app can go wrong and not be able to scan or connect in the background, it is important to check first, if the lights are on.

That is, is the peripheral advertising properly at the time your app starts scanning in the background?

Following the specifications for the advertising interval and the advertising data becomes crucial when an app is scanning in the background. What might work in the foreground, even if out of spec, would start having problems when the app is in the background, or in terminated state.

The advertising interval of your peripheral affects the time to discovery and connect performance. To have a high probability of being discovered by an Apple product you should first use the recommended advertising interval of 20 ms for at least 30 seconds. If it is not discovered within the initial 30 seconds, you can switch to using one of the following longer intervals to increase chances of discovery: 152.5 ms, 211.25 ms, 318.75 ms, 417.5 ms, 546.25 ms, 760 ms, 852.5 ms, 1022.5 ms, 1285 ms

Also, it is important that the service UUID you are scanning for is contained in the first advertising packet (ADV_IND) to ensure successful discovery of the peripheral under all conditions.

So, please check these advertising requirements, and if those are OK, then we can see if there is something wrong in the app.

Upvotes: 1

Views: 1390

Answers (2)

Gary Bennett
Gary Bennett

Reputation: 31

As Apple tech support mentioned, we determined the service UUID we were scanning for was NOT contained in the first advertising packet of the peripheral.

Once we updated the peripheral's firmware with this fix, scanning in the background after the app has been terminated by iOS works great.

Upvotes: 2

agscastaneda
agscastaneda

Reputation: 184

did you add the correct permissions in info.plist?

<key>NSBluetoothPeripheralUsageDescription</key>
<string>BLE required</string>

<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-central</string>
    <string>bluetooth-peripheral</string>
    <string>location</string>
</array>

Regards

Upvotes: -1

Related Questions