HomeKit Tutorial: Getting Started

Learn how to use the HomeKit framework to control smart home devices by making an app that controls a smart lightbulb. By Adam Rush.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 2 of this article. Click here to view the first page.

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.