Search code examples
iosswiftbeaconcbperipheral

CLLocationManager's delegate function 'didRangeBeacon' doesn't find any beacons?


This question has been asked here, but it's not solving my problem, I will explain why it doesn't solve the problem at the end of the question.

What I'm doing takes 2 steps:

  1. Let CBCentralManager discover peripherals

  2. Let CLLocationManager listen to beacons discovered in step 1

I found out the UUID of my beacon with the following delegate method:

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
    print(peripheral.identifier)
}

Then step 2 takes place:

if let uuid = UUID.init(uuidString: "<identifier found in step 1>") {
    locationManager.startRangingBeacons(in: .init(proximityUUID: uuid, identifier: UUID().uuidString))
}

My locationManager's delegate method simply prints all beacons in range:

func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
    print(beacons)
}

But all I'm getting is an empty array. Why? How do I fix this?

The question listed above suggests that when creating a CLBeaconRegion, we should give a unique identifier, which I am already doing.


Solution

  • The problem is with this statement:

    I found out the UUID of my beacon with the following delegate method:

     func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
         print(peripheral.identifier)
     }
    

    The value returned by peripheral.identifier is not your beacon's proximityUUID. Because of this, the didRangeBeacons returns an empty array, because there are no beacons in the vicinity with a proxmityUUID matching the CLBeaconRegion you set up.

    Here's a deeper explanation:

    The peripheral.identifier field comes from the CBPeripheral object, which inherits it from CBPeer. Apple's documentation here describes it this way:

    The value of this property represents the unique identifier of the peer. The first time a local manager encounters a peer, the system assigns the peer a UUID, represented by a new NSUUID object. Peers are identified by NSUUID UUIDs instead of by the CBUUID objects that identify a peripheral’s services, characteristics, and characteristic descriptors.

    Basically, this field is just a temporary identifier assigned to bluetooth device by iOS. While it is represented by a NSUUID (as it's called in Objective C or just UUID in Swift), and the CLBeacon/CLBeaconRegion have a proximityUUID field that is also an instance of NSUUID, they are not the same value. This is a common source of confusion.

    Unfortunately, it is impossible to use CoreBluetooth APIs to get the ProximityUUID of your beacon. Apple intentionally goes to some lengths to prevent you from doing this. Sorry.