← Back to blog

Using Bluetooth LE iBeacons on iOS & iPhone

beaconsbeer
Using Bluetooth LE iBeacons on iOS & iPhone

The problem

Pint Please user often has a following problem.

How to get right beer recommendations at the right time?

2016-10-20-to-veikko-siemailee-vanhassa-kaupungissa

Let’s say that we have a user called Matt. He has been using Pint Please for some time and therefore, we know his beer taste pretty well. He enjoys having a good pint of imperial stout with coffee and chocolate aromas.  On the other hand, we also know that he doesn’t like lagers or fruity pale ales.

We have a great pleasure of having pubs and other retailers (such as grocery stores) also as Pint Please customers. And we know the beers they are selling.

Now, we know Matt’s beer taste and we also know pubs’ and retailers’ beer selection. But how can we know when Matt is actually in a grocery store? We would like to give a personal beer recommendation of the retailer’s selection precisely the time Matt is shopping.

oluthylly

The solution

Traditionally all location-aware apps have been made using GPS. But there are a few drawbacks when using this technology:

  • It doesn’t work very well indoors
  • It eats your device’s battery
  • It doesn’t support background monitoring (You could use geofencing but it’s inaccurate indoors and there is a limitation on the regions you can monitor)

Bluetooth LE iBeacons

Bluetooth beacons are small battery powered devices, which broadcast simple radio signal periodically, for example once in a second. In our case, we installed beacons to a pub and to a beer shelf in a grocery store. Because of the beacons, our app becomes location-aware at  the possible points of purchase.

beaconit-ja-pintti-luurissa

We managed to get our hands to few Bluetooth beacons from two different vendors. First one is Estimote and second one is Proxama. Both of these vendors provide their own SDK (software development kit) with the beacons. However, it’s not mandatory to use it. In our prototype we used Proxama’s SDK when using Proxama beacons. Estimote beacon was used with native iOS location framework (without Estimote’s SDK).

Proxama’s SDK is not public, therefore I can not discuss much about its details. But I can say that it’s a pretty easy to use and doesn’t require much work.

Beacon transmits three values:

  • UUID
  • Major
  • Minor

UUID can be used to define the beacon’s owner. In our case, all of our beacons will have a same UUID. iOS app can register to monitor only 20 different UUIDs. But this doesn’t limit the actual amount of beacons in the field, because multiple beacons can share the same UUID. And beacons are recognized based on the major and minor values. For example, the major value could define a country and a city where beacon is installed, and a minor value could define an actual pub in the city. Of course, we need to have our own database for all locations of the beacons. In Proxama case, all this is handled by Proxama’s own backend, which is accessible via the SDK.

Prototype

In our prototype we did following things:

  1. We configured and installed beacons to our customers’ premises
  2. We implemented support for beacon monitoring in our Pint Please app
  3. We modified our backend that can provide content based on the user’s location

The use case for Matt could be the following:

  1. Matt has been using Pint Please. App registers to “listen” certain beacon UIDs
  2. Matt walks to the store
  3. iOS (iPhone) device detects a broadcasting beacon
  4. Device wakes up the Pint Please app on the background (without UI)
  5. Operating system (iOS) gives Pint Please 10 second time frame to do something. During this time Pint Please will:
    1. Get the data from the beacon
    2. Connect to Pint Please backend and send needed information about the beacon and the Matt’s user account
    3. Receive a beer recommendation relevant to the Matt’s beer profile
    4. Show a local notification to the user
    5. Offer more information about the optimal beer and a possible promotion available to Matt

Implementation on iOS

We integrated Estimote’s beacons via iOS’s core location framework, which is not too complicated either. From this framework, we used mostly CLLocationManager, which is the main entry point for all location functionalities. CLBeaconRegion is used to define what are the beacons we are interested in. In this prototype, we do not use the proximity data (the distance between the device and the beacon) at all. Therefore, we don’t need to use the CLBeacon class. We are only interested in when the device enters the beacon area.

On iOS, when you want to use beacon monitoring on the background (when phone screen is locked and your application process in not necessarily running), you have to declare that your application would always like to use a location data. First you need to define a textual reason why your app would like to use location framework, even when it is not running. You do it by adding a new string value to your project’s .info file. Name of the key is  NSLocationAlwaysUsageDescription.

permission

Screen Shot 2016-09-01 at 1.19.23 PM

Location authorization dialog with our custom text.

Next we have to write a code that will ask user’s authorization for the location. First we check the current status. If user has denied using location services, we will abort the process, because we do not want to distract the user by asking again.

If a user has not denied the access to location services and it’s unknown, we will ask the authorization by sending a message requestAlwaysAuthorization to the CLLocationManager object.

/******************************************************************************/

// Get the current authorization status from the location manager.
CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus];
if ( authStatus == kCLAuthorizationStatusRestricted ||
     authStatus == kCLAuthorizationStatusDenied )
{
    // Abort... User has denied the
    // use of location services.
    return;
}

// Create the location manager instance and store
// it's reference to property.
self.locationManager = [[CLLocationManager alloc]init];

// Ask for always authorization if the status is unknown.
// This will pop up a dialog for the user.
if ( [self.locationManager
     respondsToSelector:@selector(requestAlwaysAuthorization)] &&
     authStatus == kCLAuthorizationStatusNotDetermined )
{
    [self.locationManager requestAlwaysAuthorization];
}

All these steps are done when Pint Please is launching. We do it with a separate method called in AppDelegate’s overridden method didFinishLaunchingWithOptions.

Then we start to monitor the beacon region. You need to define the UUID your beacons have and also a self-generated identifier string for your beacons. If you need to create your own UUID, you can use a handy terminal tool called uuidgen.

/******************************************************************************/
NSString* const kBeaconUUID = @"41C08DFD-B719-4CCB-9D3B-5AA7C3860F89";
NSString* const kBeaconIdentifier = @"PINT_PLEASE_BEACON";

// Create the beacon region to be monitored.
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc]
                initWithProximityUUID:[[NSUUID alloc]
                                       initWithUUIDString:kBeaconUUID]
                identifier:kBeaconIdentifier];

// Register the beacon region with the location manager.
[self.locationManager startMonitoringForRegion:beaconRegion];

We also need to set our object to implement the CLLocationManagerDelegate protocol,  it’s didEnterRegion method, and register our object as a delegate to the CLLocationManager object.

self.locationManager.delegate = self;

Finally the following code is executed when a user enters to the beacon region.

/***************************************************************************/
- (void)locationManager:(CLLocationManager *)manager
         didEnterRegion:(CLRegion *)region {
    // We are only interested in beacon regions.
    if ( [region isKindOfClass:[CLBeaconRegion class]] )
    {
        CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;

        // We also check that the UUID is equal with our UUID.
        if ( [beaconRegion.proximityUUID isEqual:
             [[NSUUID alloc]initWithUUIDString:kBeaconIdentifier]] )
        {

            // Do something with the beacon data.
            // Show local notification or send data to your backend.
            NSLog(@"Beacon monitored!");

        }
    }
}

OK. This is it. On one hand, beacon monitoring is not very complex to implement in iOS app. But on the other hand, providing meaningful content for the user might be more complex. Users are very sensitive for stupid spamming. Be sure that you are not distributing non-relevant content or something relevant too often to your users.

If you have any comments or need more information, let us know!

download_pintplease_text

googleplay_store_button

appstore_button

newsletter_icon3


← Back to blog