December 6, 2013

Apple Stores use iBeacons (and so can you!)

By

Today, Apple began using iBeacons in all of their retail stores across the United States. In this article, I’ll show you how C# developers can also take advantage of this exciting technology.

We’re going to take a look at iBeacons and Multipeer Connectivity, and show how they can be used in concert to create a unique in-store experience for customers with a coupon-serving iOS app.

InStoreCoupon

For this app, when a customer enters the store, the iPhone version of the application will find an iBeacon in the store, and enable the customer to get a coupon for that store. In the store itself, an iPad will serve as the iBeacon, as well as handle sending the coupon information to the customer.

To advertise itself as an iBeacon, the app will:

  1. Create a beacon region.
  2. Get peripheral data from the beacon region.
  3. Create a peripheral manager.
  4. Use the peripheral manager to advertise the peripheral data.

Creating a beacon region is done by passing an NSUuid to a CLBeaconRegion instance:

// Create a beacon region
var storeUUID = new NSUuid (uuid);
var beaconRegion = new CLBeaconRegion (storeUUID, storeId) {
  NotifyEntryStateOnDisplay = true,
  NotifyOnEntry = true,
  NotifyOnExit = true
};

Then, we can use the CLBeaconRegion to get an NSMutableDictionary containing the peripheral data for a specified beacon power:

// Get peripheral data from the beacon region
NSMutableDictionary peripheralData = beaconRegion.GetPeripheralData (new NSNumber (-59));

With the CLBeaconRegion and peripheral data in place, we can create a CBPeripheralManager to advertise the beacon:

// Create a peripheral manager and start advertising
peripheralMgr = new CBPeripheralManager (peripheralDelegate, DispatchQueue.DefaultGlobalQueue);
peripheralMgr.StartAdvertising (peripheralData);

That’s all you need to do to advertise an iBeacon. Now let’s see how we can discover it from the iPhone app.

To discover when we enter the beacon region, we use the Core Location framework. In Xamarin.iOS we can take advantage of location manager delegate methods being wrapped in C# events. We need to handle the RegionEntered event for when the region is entered, in addition to the DidDetermineState event, for the case where the beacon is first discovered when already in the beacon region (such as the case where the iPad is turned on with the customer carrying the iPhone already being in the store). Note, we set NotifyEntryStateOnDisplay above to get this event in all cases.

locationMgr = new CLLocationManager ();
locationMgr.RegionEntered += (object sender, CLRegionEventArgs e) => {
  if (e.Region.Identifier == storeId) {
    WelcomeBackCustomer ();
  }
};
locationMgr.DidDetermineState += (object sender, CLRegionStateDeterminedEventArgs e) => {
  if(e.State == CLRegionState.Inside){
    if(!DealofDay.Enabled){
      WelcomeBackCustomer ();
    }
  }
};

The WelcomeBackCustomer method simply presents a notification and enables a button to allow the user to connect to the iPad to get a coupon.

In this case, we use the iPad to browse for peers (customers) using Multipeer Connectivity framework’s MCBrowserViewController, as shown below:

peer = new MCPeerID (storeId);
session = new MCSession (peer) {
  Delegate = sessionDel
};
browser = new MCBrowserViewController (serviceType, session) {
  Delegate = browserDel,
  ModalPresentationStyle = UIModalPresentationStyle.None
};

On the iPhone, the app uses an MCAdvertiserAssistant to advertise itself to peers:

peer = new MCPeerID (customer.ToString ());
session = new MCSession (peer) {
  Delegate = sessionDel
};
assistant = new MCAdvertiserAssistant (serviceType, dict, session);
assistant.Start ();

Then, in the MCSessionDelegate, we send a coupon when the customer has connected:

public override void DidChangeState (MCSession session, MCPeerID peerID, MCSessionState state)
{
  switch (state) {
  case MCSessionState.Connected:
    if (peerID.DisplayName != storeId) {
      vc.SendCoupon ("buy 1 widget, get a second for 1/2 price", peerID);
    }
    break;
  ...
  }
}

Finally SendCoupon uses the MCSession to send the coupon information in a string for simplicity.

void SendCoupon (string message, MCPeerID peer)
{
  NSError error;
  session.SendData (NSData.FromString (peer.DisplayName + ":" + message), new MCPeerID[]{peer}, MCSessionSendDataMode.Reliable, out error);
}

The coupon image is generated on the receiver using the CIQRCodeGenerator filter from Core Image:

var qrCode = new CIQRCodeGenerator {
  Message = NSData.FromString (text),
  CorrectionLevel = "Q"
}.OutputImage;

The code for this post is available in my github repo.

 

TwitterFacebookGoogle+LinkedInEmail