Creative Uses for Bluetooth on Mobile Devices

BTLE - Beacon AND Receiver

BTLE – Beacon AND Receiver

We’ve built a few iOS apps that use Bluetooth Low Energy to sense and also act as beacons that kick off other app services once a beacon is recognized. It’s a decent approach to sensing proximity that uses cheap hardware and iOS backgorund services.

Below is an example of how to setup an iOS device as a Bluetooth LE beacon (a peripheral), and have another iOS device (a central) scan for the beacon, and log a message whenever it comes within range. Since we’re using Bluetooth LE, you’ll need to use an iPhone 4S or newer, iPad third-generation or newer, or an iPad mini.

All code is written for iOS 6 or later, and relies on the CoreBluetooth framework.

Peripheral

Bluetooth Low Energy peripherals (like a Bluetooth beacon), all advertise which services they provide using UUIDs. You can create a new UUID for the beacon by running `uuidgen` in Terminal.

To start advertising the device as a peripheral using the generated UUID:

self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

CBUUID *UUID = [CBUUID UUIDWithString:@”Your UUID”];

NSDictionary *advertising = @{ CBAdvertisementDataServiceUUIDsKey: @[ UUID ] };

[self.peripheralManager startAdvertising:advertising];

To stop advertising the device as a peripheral:

[self.peripheralManager stopAdvertising];

Central

We want our app to act as a central (client) to our peripheral, even when it’s in the background. To have the app run in the background, we need to add “App communicates using CoreBluetooth” to the “Required background modes” array in our Info.plist file.

To start scanning for peripherals:

self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
NSDictionary *options = @{ CBCentralManagerScanOptionAllowDuplicatesKey: @NO };
CBUUID *UUID = [CBUUID UUIDWithString:@"Your UUID"];
[self.centralManager scanForPeripheralsWithServices:@[ UUID ] options:options];

You will want to use the same UUID string as the peripheral you’re scanning for. `CBCentralManagerScanOptionAllowDuplicatesKey` is set to `@NO`, since we want to be notified every time the peripheral is discovered. When it’s set to `@YES`, we are only notified once per discovery of each peripheral.

In order to be notified when a peripheral (our beacon) is discovered, we need to implement this `CBCentralManagerDelegate` method:

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(@"Found peripheral");
}

We also need to implement the `centralManagerDidUpdateState:` delegate method to handle state changes. The `CBCentralManager` should only be used when its state is `CBCentralManagerStatePoweredOn`. The code will be fairly app-specific, so I won’t show it here.

Conclusion

This is a very basic example, where the beacon is only advertising its presence. It would be relatively easy to have the beacon also publish more information, such as a location ID, a URL, etc.

Custom In App Purchase Approach

We have been contacted by a couple of developers looking for help transition away from Urban Airship’s in app purchase using the approach we built for our own clients. To make the work reusable, we cleaned up the code a little and made it available as a stand alone project. The items include:

– Store Manager
– Storefront UI
– Download manager/ unzipping etc.
– Server scripts to create secure access to Amazon S3 buckets
– Example project

Given the complexity and time of recreating the functionality (which was the whole point of using UA’s approach in the first place), this sample code seems to be a good time saver.

As you can see from the below diagram, the service works with a json product file and a simple PHP script on the app’s server and then relies on Amazon S3 for content storage and downloading.

InAppDiagram

While it’s a pretty straight forward implmementation of in app purchase support, it adds some nice functionality too that you cannot get with Apple’s hosting for in app purchase ,such as:

– easier testing (you can do it through Apple’s sandbox environment, but it is really slow)

– distribute free content through your app

– have server-based images in the store front

– update content when app is waiting for approval

– leverage Amazon S3’s security, file management and other features for content management

So far, this approach is working well in the Robert’s Evolutionary Biology text book and a couple of other apps.

Thanks!

Kinvey and Core Data

images (2)As mentioned in earlier posts, we have used Kinvey for a number of apps and like their approach and developer support.

For one of out client apps (Freedom Money by Hearts and Wallets) we needed a backend service that allowed users to share basically all their data at very specific times (e.g., plan reviews). We looked at a couple of different approaches to make this happen (bulk data forwarding, traditional API approaches etc.) and settled on storing the data 100% server side using Kinvey.

While Kinvey’s service fit the bill nicely, we still wanted to use Core Data to manage data in the app, so we used AFIncrementalStore as the persistent store for our Core Data stack. Using Kinvey’s REST API and a subclass of AFRESTClient, we found that Core Data can be integrated with Kinvey fairly easily.

With this approach, we got the following benefits:

  • data sharing
  • data persistence
  • easier server side programming
  • easier app side programming for fetch requests
  • easier to add new objects to view controllers
  • efficient memory management from Core Data

High Level Approach

The following methods of AFRESTClient need to be overridden:

- (NSString *)pathForEntity:(NSEntityDescription *)entity;

This should return the name of the resource on the server corresponding to entity. For example, for the entity named User, this should return users.

- (NSString *)resourceIdentifierForRepresentation:(NSDictionary *)representation ofEntity:(NSEntityDescription *)entity fromResponse:(NSHTTPURLResponse *)response;

This method needs to return a unique identifier that will be used by AFIncrementalStore. Since Kinvey uses _id as the key for unique identifiers for objects in their collections, we return the value for that key from representation:

return representation[@"_id"];

- (NSDictionary *)attributesForRepresentation:(NSDictionary *)representation ofEntity:(NSEntityDescription *)entity fromResponse:(NSHTTPURLResponse *)response;

This method should return a dictionary of attributes that will be used to setup an NSManagedObject. Depending on how the attributes of your Core Data entities map to the attributes on Kinvey, you may not have to override this method.

- (NSURLRequest *)requestForFetchRequest:(NSFetchRequest *)fetchRequest withContext:(NSManagedObjectContext *)context;

This method creates the NSURLRequest corresponding to an NSFetchRequest. In this method, we translate NSPredicates and NSSortDescriptors into URLs understood by Kinvey.

Depending on the complexity of your Core Data model, and the kinds of information you’re storing, more work might be required.

Once this setup is done, you can use Core Data in your app fairly normally, keeping in mind that fetch requests won’t always return all their results synchronously, so you need to set up an NSFetchedResultsController with a delegate that can update the interface as more information is downloaded from Kinvey.

Urban Airship Storefront Replacement

Urban Airship is cancelling their support for their In App purchase infrastructure. While this is a huge pain for us (since the service was working nicely and required no thought on our part), the replacement (described below) is giving us a few unexpected benefits, like:

  • better control over uploads and content versions
  • more easily distribute free content distribution to reviewers etc.
  • more flexible purchase options (e.g., bulk purchases)
  • much lower monthly costs

The basic architecture has the content sitting on an Amazon S3 server, a PHP class on another server to manage the authentication, a json file with in app purchase informarion (primarily for free assets), and some iOS code (below).

On the iOS side, there are two main classes: BLAStoreManager and BLAStoreFrontViewController.

BLAStoreManager

BLAStoreManager manages the in-app purchase process, including downloading and installing the purchases.

- (id)initWithDelegate:(id <BLAStoreManagerDelegate>)delegate

The delegate is responsible for providing a destination URL for downloaded products, knowing whether the product is installed, and knowing the current version of installed products. It must implement these methods:

- (NSURL *)destinationURLForProduct:(BLAStoreProduct *)storeProduct storeManager:(BLAStoreManager *)storeManager;

- (BOOL)productIsInstalled:(BLAStoreProduct *)storeProduct storeManager:(BLAStoreManager *)storeManager;

- (NSInteger)versionOfInstalledProduct:(BLAStoreProduct *)storeProduct storeManager:(BLAStoreManager *)storeManager;

Products can be fetched from a server using this method:

- (void)getProductsWithCompletion:(StoreManagerCompletionBlock)completion error:(void (^)(NSError *error))error;

This method:

  1. downloads products.json from a server, which is used to populate some BLAStoreProduct objects
  2. fetches the associated SKProducts from Apple, for non-free BLAStoreProducts

The completion block returns the BLAStoreProducts to you.

To buy a product, or download a free product, use:

- (void)buyProduct:(BLAStoreProduct *)product withCompletion:(StoreManagerBuyCompletionBlock)completion error:(StoreManagerErrorBlock)error progress:(StoreManagerBuyProgressBlock)progress;

This method:

  1. starts a transaction for the associated SKProduct, if product is non-free
  2. when the transaction completes, send the receipt to the server for validation
  3. if the receipt is valid, the server returns a temporary URL to the content to be downloaded
  4. the content is downloaded to a temporary location, and then unzipped to the URL provided by the delegate in destinationURLForProduct:storeManager:.

The completion, progress and error blocks will be called as appropriate. progress provides a double which gives the current progress of the content download and installation.

BLAStore-Server

The server component of BLAStore verifies receipts with Apple, and returns temporary download URLs to the app. When verify.php is called with Base64 encoded receipt data from the app, it POSTs that data to https://buy.itunes.apple.com/verifyReceipt, and, if it is a valid receipt, gets a temporary time-limited URL from Amazon S3 which it returns to the app.

BLAStoreFrontViewController

BLAStoreFrontViewController provides a storefront UI, and also deals with BLAStoreManager. It has two public methods:

- (id)initWithStoreManager:(BLAStoreManager *)storeManager;

- (id)initWithStoreManager:(BLAStoreManager *)storeManager productID:(NSString *)identifier;

initWithStoreManager: creates an instance of BLAStoreFrontViewController which will load and display a list of all available BLAStoreProducts. initWithStoreManager:productID: creates an instance which will present the product detail view for the product identifier given.

  • BLAStoreFrontViewController shows a UITableView displaying BLAStoreProduct objects
  • BLAStoreProductDetailViewController shows detail and a “Buy” button for a specific BLAStoreProduct

Urban Airship Hiccup

Urban Airship has suspended its In App Purchase service and we are dissapointed and annoyed.

Here is a quote from their website about why developers love them so much:

“Developers love the platform because it’s so easy to integrate into their apps. It was built that way from the beginning, to make things easier for our fellow engineers. From our flexible APIs to our what-can-we-do-to-help support, we can help monetize your app.”

For sure, this sounds good and it was true for in app purchase support. We liked it because it simplified our development work and gave us lots of flexibility compared to Apple’s in app content hosting service. Unfortunately, it is only good until Urban Airship stops loving the service they provided and marketed to developers.

Here is the announcement (which came out late on a Friday – the classic bad news dumping time!):

“As we head into 2013, we are making strategic changes to our product vision that we would like to share with you. Urban Airship is becoming laser-focused on building an industry-leading mobile marketing solution, and as part of that process we will sunset our In-App Purchase (IAP) product on July 1, 2013. It will no longer be supported as of that date.”

I think this brings up the dangers of relying on a third party to provide application infrastructure and makes us think twice about what we are going to use going forward – especially from a company where we do not have a relationship with the decision makers.

In the meantime, we are developing a replacement approach and will share that here soon.

Our Friends at Kinvey

We use Kinvey in a couple of our apps because they have a great product, are here in Boston and take the time to work with us on new features.

The recently featured Ballast Lane Applications in a developer spotlight (here) which we appreciate.

We use Kinvey for the tagging feature in the Peale Sermon app and for the whole backend data storage (through iOS Core Data) in the Hearts & Wallets Freedom$ app.

Going forward, we are anticipating more features to migrate code between environments (test, dev, production) better javascript dev options in their pre and post logic features.

Thanks!

CS50 Seminar – iOS Study List

I had the opportunity to present a seminar entitled “Intro to iOS” to the students of Harvard’s CS50 last week. This is certainly a a fun topic, only problem is there was only 60 minutes on the agenda for my talk. While the students are quite motivated and good a catching on quickly (they had just been through three languages in three months), this short amount of time did not seem sufficient to get them going on their projects. So, instead of trying to go through a whole example from beginning to end, the agenda was focused around a list of “Study Topics” for the development environment, coding, and view basics to give the students a road map to get started on their final projects.

The items on the list are scoped by the knowledge required to build Mapping Revolutionary Boston. You can see the list here in the seminar slides. To support the list and since I cannot share the code to that app, there is a small project that shows how many of the concepts work. Most items on the list are in the sample project with exception of core data (got too late when I was working on the sample) and Google navigation and maps (which would of been way to much info). You can see the project here. Hopefully, the comments in the code are clear enough to understand what is going on.

Finally, you can watch the actual seminar here and see for yourself a whirlwind introduction to iOS.

Hope this helps!