Ben Dodson

Freelance iOS, Apple Watch, and Apple TV Developer

Steve Jobs introduces the iPad at Death Star briefing

I’ve been re-watching Star Wars1 this weekend in preparation for The Force Awakens and a special episode of The Divide we are recording after Christmas. When it got to the Death Star briefing sequence towards the end of A New Hope, I was reminded of an excellent video that came out after the original iPad announcement with a mashup of that sequence and Steve Jobs voiceover. I had a quick look for it and couldn’t find it as for some reason it had been removed from YouTube2. After much searching, I eventually found a copy online but I’ve extracted it and saved it here to avoid it disappearing again in the future:

I absolutely love this video and wish I knew who created it.

  1. In Machete Order, obviously (although I’m tempted to re-watch The Phantom Menace after this theory that Jar-Jar is a Sith master…) ↩︎

  2. It doesn’t look like a takedown notice so maybe the account was deleted for some other video and this disappeared along with it. ↩︎

Using the Siri Remote with the Apple TV Simulator

I’ve been working on a tvOS app for a client recently which has led to a lot of time spent building to both the Apple TV hardware and simulator. Whilst the simulator is very good for basic testing, the simulated remote can be a bit tricky to use and so I was very pleased to discover today that the physical Siri Remote can be used with the Apple TV Simulator on El Capitan. All you have to do is pair your Siri Remote to your Mac and it’ll work in the simulator just like it does with the real thing.

The full set of steps are as follows:

  1. Unpair your remote from your existing Apple TV (if applicable) by holding down the menu button and volume up button for around 5-10 seconds whilst the Apple TV is unplugged or otherwise out of Bluetooth range1.

  2. Open up the Bluetooth settings panel in OS X; you should see a device with no details other than a mac address. Pair it.

  3. Profit! Your Siri Remote is now paired and can be used in the simulator.

I have a 1080p external monitor plugged into my iMac 5k and so if I run the Apple TV simulator on that in fullscreen mode it feels almost exactly like using the real hardware. This could be a good stop gap for those on a budget as you could just buy a Siri Remote for testing rather than the whole Apple TV2. You should always test your apps on real hardware of course but this is a useful trick nonetheless.

  1. Otherwise it’ll just unpair and repair very quickly - a frustrating way to spend 15 minutes on a Wednesday morning! ↩︎

  2. Or for developers who simply don’t want a second Apple TV. I’ve been moving mine between my office and living room for the past week and considered getting a second “development only” Apple TV to avoid this minor irritation; buying just a second remote seems a wiser investment. ↩︎

Introducing 'The Divide'

I’m happy to announce that The Divide, a new podcast I’ve been working on recently, is now available on iTunes.

Every two weeks, myself, Mr Christopher Ford, and Dr John Wordsworth will discuss a particular topic that is of interest to us for around an hour. The topics of our first three shows (which are available now) are:

  • Episode 1, The Phantom Keeper: A round-table discussion of our favourite PC games with each of us allowed to bring two for consideration

  • Three Geeks and a Toddler: A debate about our current computer setup and whether we can see a tablet replacing our laptops

  • Fitting in with all of the stereotypes: We discuss media in the digital age including films, tv, music, magazines, books, and comics.

You can subscribe on iTunes, listed online with Overcast, or by directing your podcast app of choice1 to our XML Feed. Alternatively, you can download the MP3 files directly from The Divide website.

These first few episodes, whilst not perfect2, have been a lot of fun to record and hopefully you’ll enjoy them. If you have feedback, questions, or want to suggest a topic, you can chat to us on Twitter via @PodcastDivide. Our next episode should go live on Wednesday 16th December.

  1. I personally use Overcast↩︎

  2. There are a few minor mic issues which we’ve fixed now and our second episode has an unscheduled drop-in from a hungry toddler. ↩︎

HomeKit enabled Christmas tree lights with Siri control

Every year we seem to buy a bigger Christmas tree. This year is no exception and it is now proving difficult to maneuver around it to turn the lights on and off. To fix this, I’ve connected my lights up to a Belkin WeMo and I’m running the excellent Homebridge1 server so that I can control it all via Siri:

To get this up and running on your own machine, you’ll need to do the following in the Mac Terminal:

ruby -e "$(curl -fsSL"
brew install node
sudo npm install -g homebridge
sudo npm install -g homebridge-wemo

This will install brew, install node, install Homebridge, install the WeMo plugin for Homebridge, and then attempt to run Homebridge (which will fail as you have no config file). Next, you’ll want to go to ~/.homebridge and create a config.json file that looks like this:

    "bridge": {
        "name": "Homebridge",
        "username": "CC:22:3D:E3:CE:30",
        "port": 51826,
        "pin": "031-45-154"
    "description": "This is an example configuration file with all supported devices. You can use this as a template for creating your own configuration file containing devices you actually own.",

    "accessories": [
            "accessory": "WeMo",
            "name": "Christmas Tree",
            "description": "This shim supports Belkin WeMo devices on the same network as this server. You can create duplicate entries for this device and change the 'name' attribute to reflect what device is plugged into the WeMo, for instance 'Air Conditioner' or 'Coffee Maker'. This name will be used by Siri. Make sure to update the 'wemo_name' attribute with the EXACT name of the device in the WeMo app itself. This can be the same value as 'name' but it doesn't have to be.",
            "wemo_name": "Christmas Tree"

The important part is to make sure that the wemo_name matches the name of your device within the WeMo app; the name attribute is what you want your device to be called on HomeKit. Run the homebridge command again and you should see that your device is found and Homebridge is running.

Next, you’ll need to get a HomeKit enabled app to run all of this. My recommendation is to use the one that Apple provides to developers, HomeKit Catalog, although that will require you to know how to sideload apps from Xcode. Alternatively you can download the Elgato Eve app. Once you have an app, you’ll need to add the Homebridge using the pin that is in your config file. You can play around with the app and create rooms, scenes, zones, etc, but for now the basic commands should work. If you can turn your device on and off via the HomeKit enabled app, you will be able to activate Siri and say “turn on the Christmas tree”, “turn off the Christmas tree”, and “is the Christmas tree on?”.

Added bonus: if you have an Apple TV, you are signed into iCloud, and you use iCloud keychain, then you’ll be able to use all of these voice commands when you’re outside of your house! It’s a pretty useful way to activate these devices and I have several of them dotted around my home2. Homebridge also supports many other services such as Philips Hue (without the need for their HomeKit bridge) and you can write your own plugins very easily3.

My next task is to dig out some code I wrote last year to turn the tree lights on and off automatically depending on if I’m in the house using an iBeacon…

  1. Hat tip to Luke Redpath for introducing me to Homebridge. ↩︎

  2. After my wife asked me for the 62nd time if she’d left her straighteners plugged in whilst we were driving in the car, I set up the plug near her dresser with a WeMo so she can use Siri to ask “are my straighteners turned on?” and “turn off the straighteners” if need be! ↩︎

  3. I’ve written a very basic plugin for the Prismatik Light Pack↩︎

Brapp v1.1

A big update I worked on for Brapp went live today on the App Store. It’s the first major update for the app and adds the following:

  • You can now adjust audio levels of yourself and the beat after recording. Sweet.
  • You can now rotate videos after recording so you’re not sideways when you don’t want to be.
  • We’ve added 5 new video filters! Ice, Whitewash, Planet Rock, Purple Haze and Blackout.
  • We’ve added controls for brightness and video filter intensity so there are infinite variations.
  • The new video player is faster, smoother, lighter to control and does not overlay on top of videos.
  • Producer’s names now show on videos made on their beats.
  • The new beat player shows real waveforms because real waveforms rule.
  • You can now loop beats by pressing the ∞ button.
  • Spins now show on beats! Spins are all the plays of a beat INCLUDING plays of videos made on them.
  • You can now filter your Saved Beats by title, producer name, #hashtag and bpm.
  • You can now edit info on your beats & videos. Phew.
  • When you make a video you can see it’s progress as it uploads, you no longer have to wait hopingly!
  • When you tap the beat icon on any video it shows you the beat, now it also shows you all the other - videos made on that beat on the same page.
  • When commenting on a beat/video it won’t stop playing what you are listening to.
  • You can tap profile pictures to make them big.
  • It is now easier to find your Facebook and Twitter friends, and connect with popular users on Brapp.
  • And there’s a heap of other handsome UI, UX and stability updates. P.s. We’re just getting started!

If you haven’t checked it out yet, you can get Brapp for free on the App Store. You can also read about the initial build and release.

Chipp'd, an app for sharing personal messages

I’m happy to announce that an iPhone app I’ve been working on since September this year has gone live in the App Store; Chipp’d v2.0.

Chipp’d is an app that works with real world products (available at John Lewis) to let you share secrets or private messages with your friends. You simply scan the QR code on the sticker, create your secret (with video, audio, photos, and text), and send the sticker or card; your friend will then be able to see that secret when they scan it with the app or any other QR scanner.

It is also possible to share secrets for free using the app via auto-generated URLs that can be sent via email or SMS.

I was the sole iOS developer on the project and worked closely with the CTO of Chipp’d to integrate their existing API into the product on an incredibly agressive timescale1. Everything was written in Swift 2.0 and used AutoLayout for flexible layouts on all iPhone screen sizes. I’m particularly proud of the QR scanner which I custom built when it transpired that the products that had been printed and shipped to distributors were too light for a conventional QR scanner to detect; I used GPUImage to manipulate the live video feed with multiple filters per second in order to quickly scan at different brightnesses and solve the issue before it caused major problems.

You can check out Chipp’d on the App Store (it’s free) and you can purchase the stickers and cards to use with it at John Lewis.

  1. The stickers and cards were printed, sitting in a warehouse, and about to be available for sale from John Lewis for Christmas 2015! ↩︎

A simple Audio Recorder controller in Swift (for iPhone)

iOS provides a number of useful modal views within it’s system frameworks with everything from sending email to taking photos or video. However, there is still not a standard way to perform what is a fairly basic task; recording a voice memo.

I had a need for such a feature on a project recently and so I put together AudioRecorderViewController. It’s a basic modal view controller that allows you to record, playback, re-record, and save audio locally in mp4 format.

Usage is insanely simple and modelled on such classes as UIImagePickerController:

@IBAction func presentAudioRecorder(sender: AnyObject) {
    let controller = AudioRecorderViewController()
    controller.audioRecorderDelegate = self
    presentViewController(controller, animated: true, completion: nil)

func audioRecorderViewControllerDismissed(withFileURL fileURL: NSURL?) {
    // do something with fileURL
    dismissViewControllerAnimated(true, completion: nil)

Check out AudioRecorderViewController-Swift on GitHub.

HKHealthStore Extension, deleteSamplesOfType

Following on from my post yesterday about the broken implementation of the HKHealthStore method deleteObjectsOfType:predicate:withCompletion:, I’ve put together a gist which provides a fixed method that works with HKSampleType objects; deleteSamplesOfType:predicate:withCompletion:

import Foundation
import HealthKit

extension HKHealthStore {
    func deleteSamplesOfType(sampleType: HKSampleType, predicate: NSPredicate, withCompletion completion: (success: Bool, count: Int, error: NSError?) -> Void) {
        let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: 0, sortDescriptors: nil) { (query, results, error) -> Void in
            if let _ = error {
                completion(success: false, count: 0, error: error)
            if let objects = results {
                if objects.count == 0 {
                    completion(success: true, count: 0, error: nil)
                } else {
                    self.deleteObjects(objects, withCompletion: { (success, error) -> Void in
                        completion(success: error == nil, count: objects.count, error: error)
            } else {
                completion(success: true, count: 0, error: nil)


It simply uses the NSPredicate within a HKSampleQuery in order to find any objects that match the HKSampleType and then deletes them with the HKHealthStore method deleteObjects:withCompletion: - this correctly ensures that you are only deleting objects that match both the predicate and type.

By way of example, this simply extends HKHealthStore so you can do something like this:

let date = NSDate()

let predicate = HKQuery.predicateForSamplesWithStartDate(date.beginningOfDay(), endDate: NSDate().endOfDay(), options: .None)

let stepType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!
healthStore.deleteSamplesOfType(stepType, predicate: predicate, withCompletion: { (success, count, error) -> Void in
    NSLog("deleted \(count) step objects: \(error)")
    let stepQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: 9800)
    let stepSample = HKQuantitySample(type: stepType, quantity: stepQuantity, startDate: date, endDate: date)
    self.healthStore.saveObject(stepSample) { (success, error) -> Void in

let flightsType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierFlightsClimbed)!
healthStore.deleteSamplesOfType(flightsType, predicate: predicate, withCompletion: { (success, count, error) -> Void in
    NSLog("deleted \(count) flight objects: \(error)")
    let flightsQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: 15)
    let flightsSample = HKQuantitySample(type: flightsType, quantity: flightsQuantity, startDate: date, endDate: date)
    self.healthStore.saveObject(flightsSample) { (success, error) -> Void in

As a reminder, you can file a duplicate of (rdar://22977320) if you’d like to see the original bug fixed but in the meantime the above should allow you to carry on with your HealthKit app without worrying about deleting other data.

HealthKit Radar 22977320

Working with any code, it is inevitable that you’ll stumble across bugs. When this happens with iOS development, Apple ask developers to submit a “radar” so that they can track and hopefully fix any issues that arise. The issue is that the Apple Bug Reporter is locked down and non-searchable so you have no idea if anyone else has submitted the same issue. Apple supposedly collate all of the bugs and mark any issues as duplicates and then use the number submitted to gauge how important an issue is and prioritise it’s fix. This has led to sites like Open Radar that let developers copy and paste their bug reports so other developers can search and then simply submit new bugs as a duplicate.

In any case, today I found an interesting bug in HealthKit. iOS 9 added a new method named deleteObjectsOfType:predicate:withCompletion which is supposed to let you delete any objects you have written to HealthKit nice and easily. For example, I might have an app which constantly checks your step count and flights climbed throughout the day and so it will want to delete any steps and flights from 00.00 to 23.59 before it adds the updated count. Unfortunately, the method doesn’t seem to respect the type parameter and so it will delete everything that matches your query regardless of what HKObjectType it is.

For example, consider the following code:

let date = NSDate()

let predicate = HKQuery.predicateForSamplesWithStartDate(date.beginningOfDay(), endDate: NSDate().endOfDay(), options: .None)

let stepType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!
healthStore.deleteObjectsOfType(stepType, predicate: predicate, withCompletion: { (success, count, error) -> Void in
    NSLog("deleted \(count) step objects: \(error)")
let stepQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: 9800)
let stepSample = HKQuantitySample(type: stepType, quantity: stepQuantity, startDate: date, endDate: date)
healthStore.saveObject(stepSample) { (success, error) -> Void in

let flightsType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierFlightsClimbed)!
healthStore.deleteObjectsOfType(flightsType, predicate: predicate, withCompletion: { (success, count, error) -> Void in
    NSLog("deleted \(count) flight objects: \(error)")
let flightsQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: 15)
let flightsSample = HKQuantitySample(type: flightsType, quantity: flightsQuantity, startDate: date, endDate: date)
healthStore.saveObject(flightsSample) { (success, error) -> Void in

The expected result is that you’ll end up with an entry for Step Count (9800 steps) and an entry for Flights Climbed (15). However, you’ll only get the Flights Climbed entry as the line healthStore.deleteObjectsOfType(flightsType, predicate: predicate, withCompletion:... will delete all entries that match the predicate, regardless of the HKObjectType. In addition, if you look at the headers for this method within the HealthKit framework, they don’t add up with what is expected:

This suggests that this method was refactored at some point prior to release and has this rather serious bug. I’ve filed this as a radar (rdar://22977320) but in the mean time you’ll need to do a manual search for objects with HKSampleQuery and delete them with a separate call if you want to avoid this.

Update: I’ve put together a fix for this issue with a new HKHealthStore method, deleteSamplesOfType:predicate:withCompletion:

Update: Whilst iOS 9.1 hasn’t fixed this issue, my bug report has today (23rd Oct 2015) been marked as a duplicate of issue 23097982 so looks like they may be getting around to fixing it. iOS 9.2 perhaps?

The Times They Are A-Changin'

Over three and a half years ago I announced the launch of WallaBee, “the ultimate collectibles game for iOS”. Whilst it has been a labour of love for me for over the past four years, it has also been incredibly stressful and draining at times. Today I was very happy to announce that I’d sold WallaBee to Munzee. I won’t go into the details of how that will work for players (you can find out all about it on the WallaBee Blog) but I did want to talk a bit about how a relatively simple game became a huge business.

I created WallaBee as it was a game I wanted to play1. I’d built several iOS apps in the past (including several that were featured by Apple) and my day job previously had been as a PHP developer so it was fairly trivial for me to throw the basic building blocks together. Over a few months of testing and iteration the game went live; it was free but monetised by using In-App Purchases for in game currency as well as a pro subscription for $5 a month which gave additional benefits to players. The feedback was great and I iterated quickly to a v1.1 product which launched at Nonick 012 where WallaBee was voted the ‘Best European Gaming Startup of 2012’. Over the coming years I’d add extra bits of complexity and hidden secrets2 and generally expand things out.

However, my big plan was to launch a version 2 product. I’d always considered v1.* to be a proof of concept and I had a ton of ideas for things I wanted to improve on. I pitched the idea to my co-founders in November 2012 and we showed off some screens and concepts to our players on April 1st 20133. It was around that time that the number of players was expanding rapidly and so I spent most of my time dealing with server issues and scalability. Over time, the fast iteration I’d started with slowed to a halt as I dealt with server scaling and that lead to issues when the innovation of iOS overtook us; for example, we didn’t implement AutoLayout and so weren’t equipped to deal with things like the iPhone 6 resolution change. This eventually led to nearly every issue needing the same solution; a complete rewrite of both the app and API. Unfortunately this was difficult to manage as all of my time as the sole developer was being taken up either trying to hold the server issues together enough for another release or dealing with player issues that crop up when you have thousands of people able to communicate with each other.

After a while, it just wasn’t fun for me anymore so I made the decision that I would shut it down. A few minutes after that announcement, Rob Vardeman from Munzee got in touch and asked if we could speak. I’d met him before when he was in London for a Munzee event and after a discussion, some emails, and a lot of thinking, I decided to let Munzee acquire the company so that they could continue to grow it in the way I had wanted to. I am confident that they will do an excellent job at maintaining the quirky side of WallaBee4 whilst also being able to add some amazing new features; I’m so sure of it that I have remained as a shareholder of the company.

Based on my experiences, I have a few pieces of wisdom to share:

  • It’s fine to get something launched but don’t forget to put in the groundwork for scalability. If you don’t know what you’re doing, find someone who does.

  • Monetise your game from the start. We never had any issues but I see startups every day that get a big of investment and think that if they build it they can monetise it later; don’t do that. We launched with 30-day and 90-day subscriptions and most players bought a 90-day one straight off. It is much easier to alter your existing payment structure (as we did when we went from subscriptions to single payments for goods) than to try and add one when everything was free previously.

  • Never announce something before it is built. This is a lesson that few people have mastered as it is insanely difficult trying to keep something awesome from the people who are going to love it most; it is also easy to announce something as then you feel like it forces you to launch it by that date but it doesn’t work. Take a leaf out of Apple’s book and only announce something when it is done and going to ship within the next 2 weeks (and when it comes to software, announcing something that is available “Today” should be the rule).

  • Do not underestimate how difficult it is to manage a community of players or how crucial it is that you do. I frequently had to deal with issues such as bullying, stalking, and just generally petty squabbles. We had births, deaths, celebrations, and everything in between5. There was also the inevitable bugs and server issues that required an apology. However, because of the way we handled those issues publicly and fairly, it cut us a lot of slack when things went wrong. A good community is the key thing for any social app and must be nurtured.

So what now? Without WallaBee to manage over the past few weeks, I’ve found myself at a bit of a loose end with so much more free time! I’ve written three apps so far including a content blocker, an app based on HealthKit, and an app to let me fly my Bebop Drone with an iOS MFI controller6; all of them will be available on the App Store at some point. I’m also collaborating on an Apple TV game with another developer which will hopefully be live in November. Beyond that I haven’t decided what the future has in store but I’m enjoying working on small, offline apps for the time being.

The final thing to mention is a slight change to the way I do my freelance work. Due to WallaBee becoming my main business for a while, I changed my company name from Ben Dodson Apps Ltd to WallaBee Ltd. As Munzee are based in the US, they have no need for a UK Limited company and so they have simply purchased the rights and assets of the WallaBee app and brand. I’ve therefore changed the company name to Dodo Apps Ltd7 and that is what I’ll be using for all of my freelance business from now on.

  1. And the nice thing now is that I get to play it and enjoy it! ↩︎

  2. One of our players, ‘SkipOkane’ put together a list of some of the secrets. My favourite is probably the ‘Mirage’ item which would always appear as a #1 when you purchased it (although the ‘Boomerang’ item that comes back if you try and recycle it is a close second as is the jousting minigame…) ↩︎

  3. My reasoning was that if they hated it, we could pretend it was a joke. ↩︎

  4. They’ve done well so far; the first set they created is made entirely of dancing cats. I think it’s in safe hands! ↩︎

  5. I would never have believed that somebody would want to speak with me in the final weeks leading up to their death as they wanted to make arrangements for how their game items could be distributed. It’s humbling and hard to deal with. ↩︎

  6. I would love to do a Steve Jobs “these are not 3 separate devices” but they are. ↩︎

  7. I first used the name “Dodo Apps” when I was doing my A-Level IT course. I built a choral robe management system in Microsoft Access and had to choose a name for the about page; I chose “Dodo Apps” as I was a big fan of the dodo from Alice in Wonderland and because it used letters from my surname. ↩︎

« Older Entries Newer Entries »