I have an app that needs to detect a nearby (in range for Bluetooth LE) devices running the same application and iOS 7.1. I've considered two alternatives for the detection:
- Having the devices act as iBeacons and detect iBeacons in range
- Using CoreBluetooth (like in Vicinity implementation here) to create a BLE peripheral, advertise that and scan the peripherals
It seems that the option 1 is out of the question because:
- It may take at least 15 minutes for iOS to detect entering a beacon region when the application is running background (iOS 7.1)
Option 2 seems the way to go, but there are some difficulties regarding the implementation:
- iOS seems to change the peripheral UUID in advertisement packets after a certain period of time (around 15 minutes?). This means that it's not directly possible to identify the advertising device from the advertisement broadcast signal.
Regarding this, I have the following questions:
- Are there any other methods of implementing the nearby device detection I haven't considered?
- Is it possible to identify the device through advertising (or by some other means) so that option 2 would work?
I found a way to make this work Core Bluetooth (option 2), the procedure is roughly the following:
CBAdvertisementDataLocalNameKey
(when the broadcasting application runs foreground) and a characteristic that provides the device unique identifier through a Bluetooth LE service (when the broadcasting application runs background)The advertising works as follows:
[UAUtils deviceID]
, because it's the device identifier in other parts of the program, also - but you might as well use any unique ID implementation).When the application is running foreground, I can pass the device unique ID directly in the advertisement packet by using
CBAdvertisementDataLocalNameKey
. The standard UUID representation is too long, so I use a shortened form of the UUID as follows:When the application is running background, the advertisement packet gets stripped and
CBAdvertisementDataLocalNameKey
is not passed along anymore. For this, the application needs to publish a characteristic that provides the unique device identifier:The scanning works as follows:
You start to scan peripherals with the certain service UUID as follows (notice that you need to specify the service UUID, because otherwise background scan fails to find the device):
When a device is discovered at
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
you check that ifadvertisementData[CBAdvertisementDataLocalNameKey]
exists and try to convert it back to UUID form like this:If the conversion fails you know the broadcasting device is in background, and you need to connect to the device to read the characteristic that provides the unique identifier. For this you need to use
[self.central connectPeripheral:peripheral options:nil];
(withperipheral.delegate = self;
and implement a chain of delegate methods as follows: