public

iBeacons, WatchKit and Fun

As you remember a while back we built a Watch app for a typical caveman. In this two series article we’re going to learn how to build an iPhone

2 years ago

Latest Post Simply put. by Mario Esposito public

As you remember a while back we built a Watch app for a typical caveman. In this two series article we’re going to learn how to build an iPhone app that can detect iBeacons (PART I) and then communicate with the Apple Watch to render some caveman staff on the screen (PART II).

Background Knowledge

What is an iBeacon

What can you do with them?

What can they do for you?

Advertise, Ranging and Monitoring
Ranging is like making sounds birds recognize so you can shot, pull feathers and cook. Every 1 second the phone pings the region and they surrounding beacons respond back with a few small bluetooth packet. They contain an array of beacons that are within that range. You’ll receive a CLBeacon instance when you’re ranging, more later.
Monitoring is like registering a beacon to the device for later usage. You can only monitor up to 20 regions contrary to regions that you can have as many as like.

Where do I get them?

Amazon, where else :-) -- I got mine from Amazon and Google shopping sites. If you are into Raspberry Pi you can even make your own.

Let’s Build An App

Our caveman overnight sneakily has attached sensors to the body of local bully T-Rex. He wants to be warned by his app when the all teeth and nothing man eating dino is approaching. The closer they get to his region of hunting the worst for him. Since last time our caveman had an Apple watch we're going to make a phone dependent watch app. That is not because we're suddenly stupider than the average cave man, that is because Apple decided (long story) not port the framework/apis necessary to do all beacon work on the watch. Which would have been way more idea.

On that basis, our project will look like this. And if you get lost you can find the code on Github.

We start with the phone target. ViewController.swift is your friend.

import CoreLocation

it is the step zero. Then we add the delegate in order to receive callbacks from the location manager

CLLocationManagerDelegate

under the viewDidLoad we add code to initialize and ask user's permission for using bluetooth.

 locationManager = CLLocationManager()
 locationManager.delegate = self
 locationManager.requestAlwaysAuthorization()

When it comes to asking for permissions you can't escape from adding to the plist something majorly depressing like Privacy settings. Specifically

At this point we're ready to start scanning for beacons and let the app alert us when hungry dino gets closer.

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == .authorizedAlways {
            if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) {
                if CLLocationManager.isRangingAvailable() {
                    startScanning()
                }
            }
        }
    }
    
    func startScanning() {
        let beaconID = UUID(uuidString: "E2CCCDB5-DFFB-48D2-B060-D0F5B79096E0")!
        let constraint = CLBeaconIdentityConstraint(uuid: beaconID, major: 0, minor: 0)
        let beaconRegion = CLBeaconRegion(beaconIdentityConstraint: constraint, identifier: "Bedroom Beacon")
        locationManager.startMonitoring(for: beaconRegion)
        locationManager.startRangingBeacons(satisfying: constraint)
    }
    
    func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
        if beacons.count > 0 {
            updateDistance(beacons[0].proximity)
        } else {
            updateDistance(.unknown)
        }
    }




// when the beacon is on the screen shows different level of warning
    func updateDistance(_ distance: CLProximity) {
        
        var imageToSet = "question-mark"
        var messageToSet = "I am confused"
        
        // Dispatching on the main thread is critical for UI updates. Learn more about dispatching on this awesome site.
        DispatchQueue.main.async {
            UIView.animate(withDuration: 0.8) {
                switch distance {
                case .unknown:
                    imageToSet = "mouth-happy"
                    messageToSet = "Looks safe, go hunt."
                case .far:
                    imageToSet = "mouth-teeth"
                    messageToSet = "Something is fishy..."

That's it. That's all code necessary to detect beacons and act on them. When all is put together this is what it looks like. Play the clip full screen to see it in action.

The icons come from the awesome icons8.com repository. Plus some basic pixel magic with Pixelmator and finally massaged by the "I am so happy that I bought it" app: ImageResizeLite for macOS. To discover the UUID of my beacons I used this macOS app

You can download both ImageResize and Beacon Scan from the macOS app store.

Watch out for Rex!

References

Mario Esposito

Published 2 years ago