Written by: John Murray (@jcmrim)
In the last blog that I wrote about Beacons I described a Cordova Plugin and sample application that I had written for BlackBerry 10. In this post I want to talk about how I extended this plugin to work on both Android and iOS. There are some learning points that are certainly worth reviewing when designing a Cordova plugin that crosses multiple platforms.
In fact, I had to make some changes to the API presented by the plugin to an application as a result of failing to fully appreciate the differences in how Cordova itself was implemented on BlackBerry 10, Android and iOS. As a result I decided to leave the original plugin unchanged and write a new one that crossed all three platforms so as not to impact anyone who had already made use of the older plugin.
So, the two main points I want to highlight are:
- Differences in how Cordova is implemented in BlackBerry 10, Android and iOS affect how you should design the plugin API presented to the application, and;
- Differences in Bluetooth APIs across BlackBerry 10, Android, and iOS that affect how the plugin interacts with an application.
Differences in Cordova implementation between BlackBerry10, Android and iOS
Cordova states that this call to exec() should be regarded as asynchronous. That is, you should not make any assumptions about when the success() and fail() call-back anonymous functions would be executed in relation to the call to exec() – in other words don’t apply any knowledge as to how you may know exec() has been implemented on any platform because it will come back and bite you.
In fact, just for reference, here’s how each of the platforms actually implement this:
- Android implements exec() asynchronously through a queuing mechanism;
- BlackBerry 10 implements exec() synchronously through direct calls into native shared libraries;
- iOS implements exec() synchronously through direct calls into a class that extends CDVPlugin that gets linked into a complete XCode project that Cordova builds for you.
If you develop the plugin initially on BlackBerry 10 it’s tempting to exploit the synchronous nature of exec(), in this case, to allow you to provide APIs to the user that appear synchronous. An example might be to emulate the behaviour in the plugin of a “Property that could be set or retrieved just by simple assignment. This will fail to work when you extend your plugin to the Android platform. Be warned!
Differences in Bluetooth implementation between BlackBerry10, Android and iOS
This is where you run into a difference in philosophy between how BlackBerry 10 and Android deal with iBeacons and how iOS deals with them.
BlackBerry10 and Android both take a similar approach where applications have access to the Bluetooth Advertising frame that contains the iBeacon data. In this case it’s very easy to parse the content of this frame and extract the iBeacon UUID, Major and Minor numbers, and the Transmitted Power reference (power in dBm at 1m from the device).
It’s then possible to compare the Transmitted Power value with the RSSI (Received Signal Strength Indication), also in dBm, allowing a Path Loss in dB to be calculated and hence the distance from the handset to the iBeacon.
iOS on the other hand takes a completely different approach! Whilst it’s still possible to access the Bluetooth Advertising frame, iOS actively blocks access to this data in the case that it recognises the Advertising Frame as containing iBeacon data. Rather, it requires you to use a different set of APIs that are part of the CoreLocation Framework. In this case iOS reports via this API a Proximity indication (near, far, etc.) and an Accuracy indication (a measurement in metres from the handset to the iBeacon). It means you don’t have to calculate the distance to the iBeacon yourself, but it also means that you can’t fine-tune the calculation using a calibration process to account for iBeacons that don’t accurately report a Transmitted Power reference. It also requires you to register interest in one or more CLBeaconRegions before scanning for iBeacons. A CLBeaconRegion is s set of iBeacon UUIDs, or Major or Minor numbers. So, on iOS, you just can’t scan for any iBeacons that are in the vicinity; rather you have to elect a set of iBeacons you’re interested in. This had to be accommodated into the plugin’s behaviour as well.
This behaviour on iOS has spawned a bit of a Cottage Industry in competitive beacon implementations including:
- AltBeacon from Radius Networks;
- An implementation from Samsung;
- Eddystone from Google.
The Eddystone variant was just release in mid-July 2015 and seems to have the backing of many, if not all, of the beacon manufacturers. Estimote, for example, has released an over the air firmware upgrade for its iBeacons to optionally broadcast the Eddystone format.
Rather than use the Manufacturer Field in the Bluetooth Advertising Frame it uses a registered Eddystone Service UUID and appends the beacon data to this service element. This means that Apple would have difficulty blocking access to this field as they do with iBeacon and everyone then has access to a more sophisticated beacon format that can be used in the same way on any platform.
I’ve released v1.1.0 of the SimpleXpBeaconPlugin plugin containing the features described in this article and so you can download, review and reuse the full source code – you can find the URL in the “Resources” section below. I hope this has been interesting and will help set you on your way developing real-world event driven Bluetooth applications for Blackberry 10, Android and iOS.
Please feel free to contact me, @jcmrim, via Twitter if you need any help on anything Bluetooth Smart.
John Murray – @jcmrim