20

There is a lot of confusion regarding the restrictions that are applied by the iOS on apps that want to scan BLE beacons\peripherals. After reading several blogs and Stack Overflow answers, I want to see if I understand all the issues correctly. Please correct me if there is anything I misunderstood or missed. I refer only to iOS 7 and above, and focus on detection and not connection (Can you connect to a CLBeacon using the iBeacon Monitoring & Ranging API?).

The options for the beacons are clear - Use a general purpose BLE peripheral or use a BLE peripheral that advertises in the iBeacon format (Also, a non-standard peripheral can advertise in the iBeacon format in the adv-packet and a different format in the scan-response packet).

General Restrictions

  • iBeacon Ranging will let you know which beacons are around you. You must specify the ProximityUUID that the beacons advertise beforehand (no "general" scanning). didRangeBeacons will be called every second with an array of CLBeacon objects that were found recently. The distance from the beacon and its accuracy are calculated by the iOS using some confidential algorithm that only Apple's developers really know (The algorithm is based on the rssi values and the rssi-at-1-meter calibration byte that the beacon advertises). You can also use iBeacon Monitoring to call a delegate every time you enter or exit a region - again you must specify the ProximityUUID that you are looking for (you can also specify a major & minor). "Exiting a region" is defined by some time of not receiving any advertisement, and therefore cannot be immediate. The number of regions that can be ranged\monitored simultaneously per device is limited to 20 - This means that if other apps do monitoring\ranging at the same time, your app may not be able to monitor\range (right?).
  • CoreBluetooth - You can also detect other ad-structures in the beacon's advertisement. If the beacon advertises in iBeacon format too, you cannot see the iBeacon fields (ProximityUUID, major, minor...), despite the fact that they are sent under a standard "Manufacturer Specific" ad-structure that you can see in other cases.

Running in the Foreground - The less restricted use-case:

  • iBeacon Ranging and Monitoring - no further restrictions.
  • CoreBluetooth - Passing nil in the serviceUUIDs of scanForPeripheralsWithServices will scan for all peripherals. Passing CBCentralManagerScanOptionAllowDuplicatesKey as YES in the options will make the didDiscoverPeripheral to be called multiple times for the same peripheral\beacon (I assume that using a timer you detect the advertisement was not received for some time and assume that the user exited the "region").

Running in the Background - The more restricted use-case:

  • iBeacon Ranging will not work directly. iBeacon Monitoring will call didEnterRegion and give the app runtime of 6 seconds - in which you can start Ranging (for example, to detect major & minor). The detection may not be immediate since iOS turns scanning on and off to preserve the battery power. If you enter a region of multiple beacons with the same ProximityUUID, and you monitor this UUID without a specific major and\or minor, didEnterRegion will be called when you start receiving the signal from the first beacon - however, if you did not exit the region of the first beacon and you also entered the region of a second beacon the app will not be woken up again (didEnterRegion will not be called again) so you cannot start ranging to detect the second beacon's major & minor. The app cannot simply pop up to the foreground, but can create local notifications and other background operations.
  • CoreBluetooth - According to Core Bluetooth Background Processing scanForPeripheralsWithServices can run in the background using, but you must specify at least one serviceUUID. didDiscoverPeripheral will be given a runtime of 10 seconds. Using CBCentralManagerScanOptionAllowDuplicatesKey will not work - didDiscoverPeripheral will be called once for every peripheral. Therefore, you cannot detect "exit" from the region and "re-entry". I suppose you can use a non-standard BLE peripheral that changes its MAC address to overcome this issue. The app cannot simply pop up to the foreground, but can create local notifications and other background operations. The detection may not be immediate since iOS turns scanning on and off to preserve the battery power.

Running after the app is killed

  • iBeacon Monitoring - Works! Even if the user killed the app or the device was restarted.
  • CoreBluetooth - The app will be woken up if it was killed by the iOS (due to inactivity or memory constraints). However, if the user explicitly killed the app it won't be woken up (which makes the first case hard to test). I don't know what happens after a device restart...

Does anyone have more experience with these restrictions? Can scanForPeripheralsWithServices be used as a better alternative to iBeacon Monitoring in some use-cases?

Thanks!

cmlloyd
  • 975
  • 9
  • 14
Oren
  • 2,767
  • 3
  • 25
  • 37

1 Answers1

5

You are mostly right with your description. Just two clarifications:

  • The 20 region limit is not per device, it is app-specific. No matter what other apps are doing on the mobile device, your app is still allowed to monitor up to 20 regions by iOS. That said, there are likely hardware limits that are device-specific on how many regions can be monitored in the background with hardware assistance. These limits are undocumented. If you surpass these undocumented limits, it will probably take a lot longer to detected beacons in the background. (Although that said, there is no OS guarantee of when the detections come, anyway.)

  • You cannot connect to a CLBeacon using Monitoring and Ranging APIs. These APIs only work with BLE advertising packets, which are connectionless.

Yes, it is possible to use scanForPeripheralsWithServices as an alternative. This is what Gimbal beacons do in order to implement a proprietary system. There are real disadvantages, however, in terms of background detection time and reliability.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • Thanks, I will look at the Gimbal beacons. Do you know if there are other disadvantages besides the case that the user explicitly kills the app or restarts the device? I am trying to build a beacon that will be detectable even if the user left the app in the background for a long time. – Oren Jan 31 '15 at 22:15
  • ... and advertise more information than the 4 bytes major+minor. – Oren Jan 31 '15 at 22:21
  • @davidyoung Can you elaborate a little bit more on that disadvantages of using scanForPeripheralsWithServices as an alternative? What are they? (besides inability to start app in background) – fspirit Feb 18 '15 at 14:22
  • You also cannot detect as quickly. On iPhone 5 and higher, there is hardware detection assistance for iBeacon that will trigger an app looking for them within a few seconds. The same is not available for listening for GATT advertisements in the background, so it can take many minutes to dectect devices. – davidgyoung Feb 19 '15 at 23:30
  • On iPhone 4S, 8.1.3, I see a following behaviour, if I call scanForPeripheralsWithServices AND also range for iBeacons, I get infinite background time! That is, I keep getting didDiscoverPeripheral and didRangeBeacons with no time limit. Is it a known behaviour? It doesnt correspond to description in the question. – fspirit Mar 04 '15 at 12:31
  • @fspirit are you actually (still) seeing this behavior? – Chris Apr 16 '15 at 02:08
  • @davidgyoung, Can you please suggest How much beacon i can connect at a time using CoreBluetooth? – David Gray Jun 02 '15 at 12:31
  • Please open a new question, rather than asking in comments. You can link to the new question in a comment if you wish. – davidgyoung Jun 02 '15 at 16:32
  • @Chris no more on ios 9.1 – fspirit Oct 16 '15 at 13:33