Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4370

HomeKit Tutorial: Getting Started

$
0
0
Note: This tutorial is up to date for Swift 4.2, Xcode 10 and iOS 12.

HomeKit Tutorial: Getting Started

HomeKit is a library that allows users to use their Apple devices to configure, monitor and control smart-home devices.

The HomeKit framework allows your apps to set up and control your HomeKit accessories. It provides a central repository for configuring these accessories in your home. Configuration is synchronized across all of your apps and devices, and it’s integrated into iOS, watchOS and tvOS. HomeKit even provides you with a way to control accessories or automation using Siri voice commands.

Before you learn how to control accessories from your app, first take a look at how Apple identifies pieces inside HomeKit.

Getting Started

HomeKit has a well-defined structure to enable developer communications with compatible devices.

At the root, HomeKit defines Homes, with one home being designated as the “primary home.” Every home needs to have a unique name, so when you ask Siri to interact with your home, it can easily identify if you mean your primary residence in the city, or your secondary residence such as a beach house or mountain cabin. Unique names are a fixed requirement across each piece of HomeKit.

There are many aspects to a home that HomeKit considers.

Each home can have one or more rooms, and each room can have one or more accessories. An accessory could be anything from a ceiling fan to a toaster.

Accessories can be equipped with one or more services. Think of services as the capabilities of an accessory. For example, a ceiling fan with lights has two services: One for the fan and one for the light.

Finally, each service can have one or more of its own characteristics. A characteristic is a specific trait or value type associated with that service. Your ceiling fan might have a speed characteristic, for example. The light in the ceiling fan might have two characteristics: One for intensity and another for color.

HomeKit-compatible devices can expose these characteristics, and developers can have granular control over how users interact with them.

In this tutorial, you’ll go over each of these pieces in an app that’ll control the On/Off state of a lightbulb.

Use the Download Materials button at the top or bottom of this tutorial to download the starter project. Open the starter project in Xcode.

Note: You may or may not have a HomeKit device available to you. In this tutorial, however, you’ll make use of a simulator provided by Apple to create HomeKit accessories.

Getting the HomeKit Simulator

The easiest way to get the simulator is to follow these steps:

  1. Select the LightSwitch project in the Project navigator and click the Capabilities tab.
  2. In the list that appears, you should see an option for HomeKit with a toggle button set to Off. Enable it to reveal a button labeled Download HomeKit Simulator.
  3. Click the button to open a Browser window, which takes you to your Apple Developer download page.
  4. Search for Hardware IO Tools for Xcode 7.3. Once you’ve found it, download it and open it.
  5. Copy the HomeKit Accessory Simulator app to your Applications folder.

Now, launch HomeKit Accessory Simulator. In the left pane, you should see an empty list of accessories. Click the + at the bottom of the list and choose New Accessory….

HomeKit Simulator

Give the accessory the name Lightbulb; you can leave all the other fields blank. Then, click Finish. The first thing you’ll note is each device is given a unique Setup Code. This code is used to pair and register the device when it’s on your network.

At the moment, your accessory has no service. A sad existence for any accessory, no?

Click the Add Service button at the bottom of the window. In the dialog, click the drop-down selector next to Service. You can see there are a number of predefined categories of services that HomeKit lets you choose from, including a Custom category.

Take a minute to go through them all and, when ready, select Lightbulb. Feel free to give this service a name and click Finish.

HomeKit Simulator configuring a lightbulb

You can see the Lightbulb service already comes with some predefined characteristics. You should see On, Brightness, Hue and Saturation as characteristics of the service. For the purposes of this demo, you can remove the last three characteristics and just focus on the On state of the characteristic.

HomeKit Simulator displaying a configured lightbulb

Congratulations, you’ve just created your first HomeKit accessory!

Creating Homes, Sweet Homes

Head back to the starter project.

Because you’re working with a user’s private data, you’ll ask the user permission to read and/or write that data. Open Info.plist, and add a key named Privacy – HomeKit Usage Description and set its value to This app lets you control your lightbulb.

In order to start interacting with HomeKit, first open HomeViewController.swift. Next, add a reference to the HMHomeManager under the comment as shown below. This object will give you access to all things HomeKit.

// 1. Add the homeManager
let homeManager = HMHomeManager()

Once it’s been added, you can access all the homes known to HomeKit by accessing the homes property on the homeManager. Next, add the following inside viewDidLoad() below the comment.

// 2. Add homes from homeManager
addHomes(homeManager.homes)

Since changes to homes can happen on any device synchronized with your account, you’ll add a delegate to homeManager to get notifications of changes to any homes. First, add the following inside init(coder:) below the comment to assign yourself as the delegate object:

// 3. Add HomeViewController as delegate to homeManager
homeManager.delegate = self

Don’t panic about the error this line of code just generated. It’s because HomeViewController doesn’t conform to HMHomeManagerDelegate yet. You’ll fix that by implementing the optional delegate methods. Add the following extension at the bottom of HomeViewController.swift:

// 4. Implement HMHomeManagerDelegate as extension on HomeViewController
extension HomeViewController: HMHomeManagerDelegate {
  func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
    addHomes(manager.homes)
  }
}

Here’s what this does. First, you conform HomeViewController to HMHomeManagerDelegate. Then, you implement homeManagerDidUpdateHomes(_:) to add all of the homes from HMHomeManager into your homes array by using the helper addHomes(_:).

Not everyone will already have a Home defined in HomeKit, so while you’re here you’ll add the ability to create your own home and give it a Room. Locate newHome(sender:) and add the following code inside below the comment:

// 5. Add new Home + Room

// 1
self.homeManager.addHome(withName: homeName) { [weak self] home, error in
  guard let self = self else {
    return
  }

  // 2
  if let error = error {
    print("Failed to add home: \(error.localizedDescription)")
  }

  // 3
  if let discoveredHome = home {
    discoveredHome.addRoom(withName: roomName) { _, error  in

      // 4
      if let error = error {
        print("Failed to add room: \(error.localizedDescription)")
      } else {
        self.homes.append(discoveredHome)
        self.collectionView?.reloadData()
      }
    }
  }
}

Here’s what this code does:

  1. Call addHome(withName:completionHandler:) passing in the name of the home.
  2. Verify there was no error while adding the home. If there was an error, simply print it out.
  3. If you successfully created an HMHome, add a room to it using the name entered in the dialog.
  4. If you successfully added a room, add the newly created home to the homes array and refresh the collection view.

Build and run the app. After granting the necessary permissions, create a new Home and Room.

Adding Accessories

Adding accessories is a bit different from the way you added homes. Accessories need to be discovered before they can be added to a given Home and Room. So that’s what you’re going to do next: Handling accessory discovery.

First, open AccessoryViewController.swift and add the following properties to the class declaration at the top:

// 1. For discovering new accessories
let browser = HMAccessoryBrowser()
var discoveredAccessories: [HMAccessory] = []

Here, you added an instance of HMAccessoryBrowser as a property to the class to help discover accessories, along with an array of HMAccessory objects, which you’ll use to keep track of all of the discovered accessories.

The AccessoryViewController has a button on the top-right you can tap to start the discovery of new accessories. This button calls back to discoverAccessories(sender:) in your code. Add the following inside discoverAccessories(sender:):

// 2. Start discovery
discoveredAccessories.removeAll()
browser.delegate = self
browser.startSearchingForNewAccessories()
perform(#selector(stopDiscoveringAccessories), with: nil, afterDelay: 10)

Here, you reset the list of discovered accessories and assign AccessoryViewController as a delegate to the HMAccessoryBrowser. Next, you have the browser start the discovery and set a timer to stop searching after 10 seconds.

Now, remember, as a delegate of HMAccessoryBrowser, you’re required to conform to HMAccessoryBrowserDelegate so HomeKit can notify your app when it discovers a new accessory during the search. Fix this issue by adding the following extension to the bottom of AccessoryViewController.swift:

// 3. Have AccessoryViewController implement HMAccessoryBrowserDelegate
extension AccessoryViewController: HMAccessoryBrowserDelegate {
  func accessoryBrowser(_ browser: HMAccessoryBrowser, didFindNewAccessory accessory: HMAccessory) {
    discoveredAccessories.append(accessory)
  }
}

In this app, you’ll just record when a new accessory has been found, and you’ll analyze the results after the timer completes.

After 10 seconds, the timer will call stopDiscoveringAccessories(). This is your chance to see if any accessories were discovered. Add the following code inside stopDiscoveringAccessories() right before the closing bracket:

// 4. Stop discovering
if discoveredAccessories.isEmpty {
  let alert = UIAlertController(
    title: "No Accessories Found",
    message: "No Accessories were found. Make sure your accessory is nearby and on the same network.",
    preferredStyle: .alert)
  alert.addAction(UIAlertAction(title: "Dismiss", style: .default))
  present(alert, animated: true)
} else {
  let homeName = home?.name
  let message = """
                Found a total of \(discoveredAccessories.count) accessories. \
                Add them to your home \(homeName ?? "")?
                """

  let alert = UIAlertController(
    title: "Accessories Found",
    message: message,
    preferredStyle: .alert)
  alert.addAction(UIAlertAction(title: "Cancel", style: .default))
  alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
      self.addAccessories(self.discoveredAccessories)
  })
  present(alert, animated: true)
}

In this code, you first check if the list of discovered accessories is empty. If it is, then you let the user know that no accessories were found. Otherwise, you notify the user how many accessories you want to add to their home and prompt for confirmation. Notice how you call addAccessories(_:) with the discovered accessories as part of the OK button action call.

Note: You’ll just add the accessory to the home. Normally you’d want to add it to a certain HMRoom for better management, but in the interest of time, you’ll just add it to the home.

So now that you’ve added accessories to the Home, you must add the ability to control the On/Off state of the lightbulb.

Start by displaying all known accessories. In loadAccessories(), add the following code before the call to reload the data:

// 5. Load accessories
guard let homeAccessories = home?.accessories else {
  return
}
    
for accessory in homeAccessories {
  if let characteristic = accessory.find(serviceType: HMServiceTypeLightbulb,
                                         characteristicType: HMCharacteristicMetadataFormatBool) {
    accessories.append(accessory)
    accessory.delegate = self
    characteristic.enableNotification(true) { error in
      if error != nil {
        print("Something went wrong when enabling notification for a characteristic.")
      }
    }
  }
}

Here’s what you’re doing in this code:

  • First you sort through all of a home’s accessories to try to find an accessory with a service that matches the HMServiceTypeLightbulb type and has a Boolean (on/off) characteristic format.
  • In the case you found one, you append it to the list of accessories, set AccessoryViewController as the delegate for the accessory and enable notification on that characteristic. That way, you’ll receive a callback when the value of the characteristic changes.

Next, add the following extension at the bottom of AccessoryViewController to implement the HMAccessoryDelegate protocol:

// 6. Have AccessoryViewController implement HMAccessoryDelegate to detect changes in accessory
extension AccessoryViewController: HMAccessoryDelegate {
  func accessory(_ accessory: HMAccessory, service: HMService,
                 didUpdateValueFor characteristic: HMCharacteristic) {
    collectionView?.reloadData()
  }
}

Here, you reload all of the data on the Collection view to reflect the new changes.

Finally, in collectionView(_:didSelectItemAt:), you want to toggle the value of the On/Off state in the lightbulb. The easiest way to do this is to use the asynchronous method writeValue(_:completionHandler:) on the characteristic whose value needs to be changed. Locate collectionView(_:didSelectItemAt:) and add the following code right before the closing bracket:

// 7. Handle touches which toggle the state of the lightbulb
let accessory = accessories[indexPath.row]
guard 
  let characteristic = accessory.find(serviceType: HMServiceTypeLightbulb,
                                      characteristicType: HMCharacteristicMetadataFormatBool) else {
    return
}

let toggleState = (characteristic.value as! Bool) ? false : true
characteristic.writeValue(NSNumber(value: toggleState)) { error in
  if error != nil {
    print("Something went wrong when attempting to update the service characteristic.")
  }
  collectionView.reloadData()
}

In this code, you make sure the item is a lightbulb and has a Bool data format. Then you toggle its state and reload all the data on the Collection view.

Build and run the app. Tap on the Home you added, then select the magnifying glass button. This will start searching for any accessories on the network.

It will find the accessory you created earlier; go ahead and complete the setup process for this.

Toggling the LightBulb accessory On/Off will also toggle the status inside the HomeKit simulator. Give it a try!

And that’s it!

Where to Go From Here?

You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial.

HomeKit is a great library to help you interact with all your HomeKit enabled devices. Although you didn’t cover it in this tutorial, HomeKit gives you the ability to set up scenes and automation, which are groups of actions that affect the accessories in your smart home.

HomeKit also allows you to create action triggers based on calendar values or geolocation. You can view the 2017 WWDC video, “What’s New in Homekit”, showing the very best of HomeKit.

I hope you enjoyed this HomeKit tutorial and feel empowered to start building HomeKit into your own apps! Please join the discussion below about how you can utilize the HomeKit framework.

The post HomeKit Tutorial: Getting Started appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4370

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>