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 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

  • Beacons are hardware devices that intermittently send out a Bluetooth signal
  • Based on the type of hardware you get slightly different features, like longer ranges and faster frequencies of updates
  • Beacons identify themselves with a unique ID (UUID), and a major/minor versioning system
  • You can’t connect to any beacon just because you are awesome, you need to know to know the combo at the previous bullet. Beacons are found in packs like Dinosaurs, if they all have the same identification qualities (called Region) than they belong to the pack.
  • A beacon region can be created using just a UUID

What can you do with them?

  • You can place in every room of your house and when you app is close by make something magic happening.
  • You can do more proportionally to your creativity but that’s about it. Detect presence and communicate the distance between your phone and the placement of the beacon.

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

Comments

Popular posts from this blog

Build an independent watch app - PART IV

Build an independent watch app - Part II

It's all matter of angles, in life