Search code examples
iosswiftcocoa-touchcore-bluetoothbtle

CoreBluetooth not discovering hear-trate monitor if other app is connected first


I want my users to be able to track their heart-rate with my app. So I use CBCentralManager for that. Everything works fine if no other app is connected to the heart-rate sensor yet. The problem I have is if I start f.e. Strava or Endomondo first. Then I just can't find any devices any more. The other way round everything works fine, so I guess I am missing an options somewhere?

What I currently do:

I instantiate my CBCentralManager like so

centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)

which will cause the delegate method for connection to be triggered

 func centralManagerDidUpdateState(_ central: CBCentralManager) {

    let heartRateServiceUUID = CBUUID(string: "180D")
    let services = [heartRateServiceUUID]

    switch central.state {

    case .poweredOn:
        centralManager.scanForPeripherals(withServices: services, options: nil)

and from there on no peripheral are found.

But again, when I force quit other apps like Endomondo or Strava and then start my app, everything works fine.


Solution

  • See Best Practices For Interacting With A Remote Peripheral Device for full details on how to connect to devices you already know about.

    The main issue you're encountering is that most BLE devices stop advertising once they have a connection. Since they aren't advertising, you can't see them in a scan. In this particular case, the BLE device is connected to the iPhone you're running on, but that doesn't change anything. It's still not advertising.

    To deal with this, you want to ask the iPhone for connected devices that have the service you want, using retrieveConnectedPeripherals(withServices:). This is a very fast, synchronous call, and you generally should do it before calling scan​For​Peripherals(with​Services:​options:​).

    There are several other steps that you generally should do. The precise order and logic depends a little on your situation, but the linked flowchart above walks you through one approach. Basically it will look something like this:

    • Call retrieve​Peripherals(with​Identifiers:​) to find a peripheral you already know the identifier for. Note that this just tells you the system knows about the peripheral; it doesn't mean it's currently nearby. Calling connect on it may never succeed.

    • Call retrieveConnectedPeripherals(withServices:) to find a peripheral that is already connected to this iPhone and advertises your service. You still need to call connect on it for your process, but it should succeed.

    • If all the rest fails, then call scan​For​Peripherals(with​Services:​options:​).