iAchieved.it

Software Development Tips and Tricks

By

CoreLocation on iOS 8 with Swift

Editor’s Note: This is part two in a three part series. The goal in this series is to develop a fully functional (and useful) iOS application that is capable of pushing weather alerts based on your location to your iOS device. The series is broken down as follows:

  • Part One – Developing a Push Notification-capable application with Parse and Swift
  • Part Two – Using CoreLocation with Swift
  • Part Three – Integrating in a Weather Service API

In this second part of our three part series we’ll be expanding on our weather alerts application started in Part One. A warning! The code examples that follow build upon the code in Part 1.

CoreLocation

CoreLocation is the iOS framework which provides location-based information to your application. By location we mean either the phone’s location in a GPS-coordinate space, or we can mean in reference to beacon devices. In this tutorial we are interested in the GPS location of the device.

Before we get started using CoreLocation, it is important to note that Apple has raised the bar in terms of how resource-intensive your application is with regards to using location-based services. Our application, Lewis and Clark, made use of CoreLocation to send frequent and accurate GPS locations to determine whether a user was in a new county or state. Apple had approved the application through several iterations and then rejected the application on the grounds it was requesting “navigation-level” GPS coordinates and was not a navigation application. When writing an application that makes use of location updating strive to only request the bare minimum accuracy necessary for your application to work correctly.

Okay, let’s get started. In your weatheralerts Xcode project, create a new Swift file called CoreLocationController.swift. If you don’t have the project you can get the version from here. Make sure and read the Part One post to obtain API keys for Parse and add them to your project.

CoreLocation uses the delegation pattern, so our class needs to declare that is implements the CLLocationManagerDelegate protocol. If you are unfamiliar with using the delegation pattern in iOS, see the following excellent tutorials:

Back to the code! Here is our initial content for CoreLocationController.swift:

Next let’s declare a member variable locationManager and configure it in the init() method as follows (this code goes in your CoreLocationManager class!):

Note: To declare Swift classes as delegates you must first inherit from NSObject. See Stackoverflow for a good answer as to why.

Before we forget, go to the AppDelegate and add:

as a member variable after the pushNotificationController declaration, and in the didFinishLaunchingWithOptions function add:

after the self.pushNotificationController = PushNotificationController() line.

Delegate Methods

There are a number of CoreLocation delegate methods we need to implement in our CoreLocationController class, but the first we will implement is locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus). iOS 8 introduced changes to how apps are authorized to use CoreLocation, and we want to capture when our app moves from a given authorization state to the Authorized state. To capture that transition we implement the delegate method:

If we tried to compile and run this code as is you would notice that no authorization request is made. iOS 8 requires you to not only request authorization status (with the code requestAlwaysAuthorization function), but also provide a description as to why you are requesting a given authorization. The text description is given by the key NSLocationAlwaysUsageDescription (when requesting always authorization, which is used to receive location updates while the app is in the background) in your Info.plist. To add it, edit your Info.plist in Xcode. I prefer to right-click in the Info.plist editor window and Show Raw Keys/Values before inserting a new key.

In this example we’ve added our key NSLocationAlwaysUsageDescription and the reason we give is “Your location is used to send you timely weather alerts in your immediate area.”

tut_locationalways_usage
Show Raw Keys/Values

Run the application again and you should see:

tut_access_location

Press Allow and your application should switch to an .Authorized status. If you have the same println statements in your code as above you will see in the console log:

Notice that when our app changed to .Authorized we call self.locationManager.startUpdating(). startUpdating() is a method on CLLocationManager which will be responsible for obtaining GPS coordinates and delivering them to us. And since this is a delegate pattern, we need to implement the function that will be called when CLLocationManager has GPS coordinates: locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!).

Here’s our initial implementation of this delegate function:

Run the application again and you should begin receiving a stream of locations. Too many locations as a matter of fact, and they are all likely right on the same location. We want to fine tune how many location updates we receive, as well as our relax our accuracy requirements.

Consider our use-case, receiving weather alerts relative to our location. Does it matter if our GPS location is accurate down to a meter? No. Chances are a weather alert in your area is going to be the same as in your next-door neighbor’s area, i.e., you are in the same “area”. We can greatly reduce the demands of our application without sacrificing accuracy by relaxing our GPS accuracy to a kilometer. Moreover, we don’t need any additional updates to our location unless we move significantly, say 3 kilometers. This can be achieved by adding the following code after self.locationManager.delegate = self in our init() routine.

An illustration:
tut_accuracy_distance

The location reported by our phone is the green dot. Because our accuracy is set to one kilometer we could actually be anywhere in the blue circle. Again, we don’t really care. On the edge of the blue circle, right in the center, etc., its all the same for a weather alert. The red circle represents our distance filter, that is, the distance we have to move before we will receive another update. As long as our green dot stays within the red circle, we won’t receive an update from CoreLocation, which again is fine for our purposes.

Note: In our application we might even be better served by significant location change updates from iOS, but for our purposes we’ll use standard location updating.

Reverse Geocoding

We now have the basics for receiving location updates whenever our phone moves approximately 3 kilometers from its previous location, and our location is accurate within a kilometer. Our weather API will require us to pass city, state, and country information to provide weather alerts. We can obtain this information by using the reverse geocoding feature of the CLGeocoder class. Reverse geocoding relies on databases to take GPS coordinates and return the “address” of that location. iOS encapsulates the returned data in a CLPlacemark object which has various attributes we can extract as follows (this code goes in our didUpdateLocations delegate function):

We’ll note that the call reverseGeocodeLocation returns immediately, and it is only after the reverse geocoding results are available (a network request is made to Apple servers to look up the data) is the completionHandler block called. Heed the warning in Apple’s documentation when using reverse geocoding! Geocoding requests are rate-limited for each app, so making too many requests in a short period of time may cause some of the requests to fail. When the maximum rate is exceeded, the geocoder passes an error object with the value kCLErrorNetwork to your completion handler.

Simulating Movement

At some point we’re going to need to test our application and ensure that current weather alerts are recognized and displayed to the user. I live in North Texas, and while we do get severe weather from time to time (tornados anyone?), we don’t have severe weather every day. So how are we going to test our application? Simple. We will take a look at Wunderground’s severe weather alert map, pick a city within an alert area, look up its GPS coordinates, and give that information to Xcode to simulate for us.

Before we go into the details of getting the GPS address of a city of interest, let’s start with creating a GPX file in Xcode for GPS-location simulation. To create a GPX file use Xcode File – New – File and select iOS Resource – GPX File

tut_create_gpx_file

Name the file Waypoints. Edit the file in Xcode and delete the default waypoint (the lone <wpt/> element) and then add:

This coordinate is in Buffalo, New York, which, as of this writing, has a winter weather alert. Run the application and then use Xcode to simulate the location based upon the content of the Waypoints GPX file. This is done within Xcode, on the bar above the Debug Area notice the location arrow:

tut_simulate_waypoints

You should see after triggering the location simulator to use Waypoints:

Posting Our Location

I am a big fan of the notification model in iOS application development. Rather than add hooks across classes with references to objects, or create a new delegate protocol, simply post a notification to the notification center and let anyone who is interested in the data subscribe to it (sometimes referred to as pub-sub).

We will eventually write a class that handles taking our location and uses a weather service API to look up whether there are any active alerts. That class is going to listen for an event that the CoreLocationController will publish. For simplicity we’ll call it the LOCATION_AVAILABLE event. Here’s how we use the notification center to post it. After extracting the CLPlacemark data into a userInfo dictionary, add the following line:

Let’s go ahead and create the class that will receive and handle the notification. Create a new file called WeatherServiceController and implement the following:

Note: WeatherServiceController must inherit from NSObject to make use of the notification center. Failure to derive the class from NSObject will result in the following type of trap when the notification is posted by the CoreLocationController:

Before trying to run the again, remember, we have to create an instance of our WeatherServiceController. Head back to the AppDelegate and add:

to your member variable declarations and:

in your didFinishLaunchingWithOptions function. At this point your AppDelegate should look a bit something like this:

Running the code again and simulating to Buffalo, New York:

Receiving Events in the Background

If you’ve been working along and put the application in the background, you’ll notice no updates are received! That sort of defeats the purpose, so we have to explicitly add the capability to receive location updates in the background. Go to your application target and navigate to the Capabilities page and scroll down to Background Modes. Turn background modes on and select Location updates

tut_background_location_updates

Bitbucket Xcode Project

The work done thus far is available on Bitbucket on a branch named part2. To use the project you can directly download the zip file and open the enclosed Xcode project. There are a few steps you’ll need to complete to use the project:

  • Sign up for Parse and obtain your own API keys
  • Change the Bundle Identifier to reflect your organization (that is, change it.iachieved. to com.yourcompany)
  • Create and add your own ApiKeys.plist file and use your Parse API keys
  • Create and configure your own provisioning profile

Part 3

In part three we’ll be adding the final touches to the application which will be passing the reverse geocoded location to our weather service API. This API will in turn provide us with information about weather alerts for the area, and if any are available we will provide that information to Parse to receive a push notification.

By

Push Notifications with Parse and Swift

Update 1/29/2016: Parse announced yesterday in their blog that they will be fully shutting down operations on January 28, 2017. Consider using a service like Amazon.com SNS or Pushover.

Editor’s Note: This is part one in a three part series. The goal in this series is to develop a fully functional (and useful) iOS application that is capable of pushing weather alerts based on your location to your iOS device. The series is broken down as follows:

  • Part One – Developing a Push Notification-capable application with Parse and Swift
  • Part Two – Using CoreLocation with Swift
  • Part Three – Integrating in a Weather Service API

In this first part of our three part series we’ll be developing an application which is capable of receiving iOS push notifications. Rather than invent our own push notification application server, we’ll leverage Parse to provide this capability. Our first warning: this tutorial is rather lengthy and involved, and it requires some knowledge about using the Apple Developer Portal to create certificates, provisioning profiles, and register iOS devices for development. If you’ve never gone through this process before I highly recommend a quick read of this post.

Our Application

There are a number of iOS weather applications on the AppStore. If you think about it, a large number of mobile phone applications mirror the sections of the newspaper you were used to growing up as a kid (because as an adult I’m betting you don’t read the newspaper). The Business section had stocks, the Sports section had all the football scores, and of course the Weather page had the weather and the forecast. The basic information we desire to receive hasn’t changed much; what has changed is how we receive it and how timely and contextual it is. I personally use The Weather Channel application and have my Severe Weather set to Dallas, TX. Whenever there are severe weather alerts for North Texas I receive a push notification to my phone alerting me to the fact.

But what I really want is weather alerts based upon my current location. If I’m traveling across the country, whether by plane, train, or automobile, it would be nice to know if there are sudden weather alerts based upon where I am, not where I live. This is the type of application we’re going to develop in this tutorial, and we’ll do it using very basic iOS development techniques, and we’ll do it all in Swift. At the end of this tutorial you won’t have an application that you’ll be able to rush out and publish to the AppStore, because it won’t have a functional UI (it won’t have a UI at all), but it will give you a framework to build something upon if you choose to do so. What it will provide are all the details necessary to learn how to use Parse for push notifications, CoreLocation for location, and integrating with a REST API for receiving weather alerts posted by NOAA.

Getting Started

To get started let’s create a new project in Xcode (we are using Xcode 6.1). Select the Single View iOS application template, and choose Swift as your language. Name the Product Name weatheralerts. Make note of the bundle identifier, in our case its it.iachieved.weatheralerts, but yours will undoubtedly be different (like com.yourcompany.weatheralerts).

tut_new_project_options

Normally I wouldn’t harp on this, but when dealing with push notifications it is important to ensure that you make note of the bundle identifier. It is important when configuring push notifications in the developer portal that you use the same bundle identifier as what you used in your project.

Since we are going to be using API keys in our application and don’t want to commit them to Github, we’re going to use our documented technique for storing API keys in a property list file. Take a moment to follow our tutorial for creating your ApiKeys.plist and ApiKeys.swift files for this project. The remainder of this tutorial will assume you’ve used this technique.

Signing Up With Parse

We’ll be using Parse for sending push notifications to our application. Parse requires you to sign up to use it’s service, so head on over sign up.

tut_signup_for_parse

Once you’ve signed up browse over to the QuickStart page and create a basic application. Name the application weatheralerts and then select the big Push icon:

tut_hello_push

Select the big iOS icon (they make it so cute), and then select Existing Project.

tut_parse_existing_project

Here you should see a pleasant blue background outlining steps to obtain the Parse SDK, adding it to your application, etc. Follow the instructions for downloading, unzipping, and adding the Parse SDK to your project, but stop at the section titled Connect your app to Parse and instead follow these instructions for using Parse with Swift. To be clear, before proceeding with the next section you should have:

  • Downloaded and unzipped the Parse SDK
  • Dragged and dropped Parse.framework and Bolts.framework into your Xcode project, following the instructions to copy files
  • Added all of the required iOS frameworks as outlined in the Parse instructions (there are quite a few)

Using Parse

To use Parse with our Swift application we’re going to need to create a bridging header. This is due to the fact that Parse is (currently) an Objective-C framework, as is 99% of the established frameworks out there as of this writing. Swift simply hasn’t been around long enough to have a large number of native frameworks. To create a bridging header with Xcode go to File – New – File and in the iOS Source templates choose Header File. Name it bridgingHeader (the name doesn’t matter actually as long as your project is configured properly to use it) and Xcode will place it in your project.

In bridgingHeader.h add #import such that the file looks something like:

#ifndef weatheralerts_bridgingHeader_h
#define weatheralerts_bridgingHeader_h

#import <Parse/Parse.h>

#endif

Our bridging header is in place, but you have to configure it in your project. Click on your application target and select the Build Settings tab. In the search box type bridging to find the configuration entry quickly. For the bridging header path you can type in $(SRCROOT)/$(PROJECT_NAME)/bridgingHeader.h.

tut_configure_bridging_header

Now, because this is a multipart tutorial that will culminate in an application that can send you NOAA weather alerts, we’re going to go ahead and add things that aren’t necessary for a basic push notification application. The pattern is a useful one, however, and can be adapted for your own specific project needs.

We’re going to use a push notification controller to encapsulate our push notification needs. Go ahead and create a Swift file named PushNotificationController and add the following boilerplate code to it:

import Foundation

class PushNotificationController : NSObject {
  
  override init() {
    super.init()
  }
  
}

Simple enough. Now we can pick up where we left off with Parse.

The first step in our application is going to be to set Parse’s application ID and client key to those values provided to us. Whereas the Parse tutorial has us placing these values in our application delegate’s didFinishLaunchingWithOptions function, we’re going to place them in the constructor (in other words, the init function) of our PushNotificationController. First, however, we want our keys in our ApiKeys.plist file. We’ll refer to them as PARSE_APPLICATION_ID and PARSE_CLIENT_KEY. Again, we’re using a specific technique for managing API keys outlined here.

tut_parse_api_keys

Using our valueForAPIKey function provided in ApiKeys.swift we can configure Parse as follows:

override init() {
    super.init()
    
    let parseApplicationId = valueForAPIKey(keyname: "PARSE_APPLICATION_ID")
    let parseClientKey     = valueForAPIKey(keyname: "PARSE_CLIENT_KEY")
    
    Parse.setApplicationId(parseApplicationId, clientKey: parseClientKey)
    
  }

Now we must switch over to the AppDelegate class and register for push notifications. We want to create an instance of the PushNotificationController in our application delegate, and we can do so by adding:

var pushNotificationController:PushNotificationController?

right beneath the var window: UIWindow? declaration, and then in the application(application:didFinishLaunchingWithOptions:) function add:

self.pushNotificationController = PushNotificationController()

    // Register for Push Notitications, if running iOS 8
    if application.respondsToSelector("registerUserNotificationSettings:") {
      
      let types:UIUserNotificationType = (.Alert | .Badge | .Sound)
      let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)

      application.registerUserNotificationSettings(settings)
      application.registerForRemoteNotifications()
      
    } else {      
      // Register for Push Notifications before iOS 8
      application.registerForRemoteNotificationTypes(.Alert | .Badge | .Sound)
    }

Note that due to differences between push notification registration in iOS 8 and iOS 7 we have to determine if the new iOS 8 method is available (registerUserNotificationSettings:). If it is we use the iOS 8 method, otherwise iOS 7. See Stackoverflow for additional information.

Finally, in our application delegate (AppDelegate) we’re going to add 3 more functions:

  • didRegisterForRemoteNotificationsWithDeviceToken
  • didFailToRegisterForRemoteNotificationsWithError
  • didReceiveRemoteNotification

It’s important to go ahead and implement didFailToRegisterForRemoteNotificationsWithError because odds are there will be some misconfiguration with your first attempt at push notifications, and it’s helpful to know right away what that might be.

Here’s the implementation for these routines:

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
    println("didRegisterForRemoteNotificationsWithDeviceToken")

    let currentInstallation = PFInstallation.currentInstallation()
    
    currentInstallation.setDeviceTokenFromData(deviceToken)
    currentInstallation.saveInBackgroundWithBlock { (succeeded, e) -> Void in
      //code
    }
  }
  
  func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
    println("failed to register for remote notifications:  (error)")
  }
  
  func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
    println("didReceiveRemoteNotification")
    PFPush.handlePush(userInfo)
  }

We’ll look at each of these in turn momentarily. Go ahead and try to run the application as it is on your iPhone. If everything has been configured correctly thus far you should get a popup requesting push notification access.

tut_would_like_to_send

However, even after pressing on OK you will likely see in the console log:

An Aside: If you ever find yourself testing push notifications and would like to trigger the dialog requesting allowance to send push notifications a second time, you’re in for the real treat of changing the time on your iPhone. No kidding.

To actually allow for push notifications to be sent to the device we need to turn to the Apple Developer portal and follow a multi-step process (and in this order):

  • Create a new AppId for our application
  • Enable Push Notifications for our newly created application
  • Create our Push Notification security certificate
  • Create a Provisioning Profile for our application
  • Download and install the Provisioning Profile
  • Configure our project to use our new Provisioning Profile

Provisioning in the Developer Portal

Let’s start with our AppId. Go to the Apple Developer Portal Member Center and navigate to the Certificates, Identifiers & Profiles page. Select Identifiers and choose App Ids.

tut_new_app_id

You should see a plus icon to the right of the title iOS App IDs, select that plus sign to register a new AppId.

tut_create_app_id

There are some key things to note here:

  • The App ID Description can be a plain-text description, such as Weather Alert Application
  • The App ID Prefix is chosen for you based upon what iOS development team you are a member of
  • Select Explicit App ID for this tutorial and ensure your Bundle ID is the same as the bundle id of your application

The last item cannot be stressed enough, if the bundle identifiers do not match you are in for a headache. In our example we used it.iachieved.weatheralerts. Scroll down to the bottom (we are going to ignore App Services for the moment and configure it later) and click Continue. On the Confirm your App ID page click Submit.

Configuring the application for push notifications

Although after creating the application ID the portal tells us This App ID is now registered to your account and can be used in your provisioning profiles. we need to actually configure it for push notifications. Go back to the App IDs page and select your application. Scroll to the bottom of the Application Services list and click on the Edit button.

Scroll down again until you find the Push Notifications service and click on the checkbox to the left to enable it, and then click on Create Certificate in the section marked Development SSL Certificate.

tut_enable_push_notifications

If you have never created a Certificate Signing Request the process is not difficult, but it is cumbersome and requires a number of manual steps. Before continuing further I suggest you read in detail the text on the developer portal page that is presented to you. That is, the page that begins with To manually generate a Certificate Signing Request…. As the page instructs you need to open KeyChain Access and use the Request a Certificate from a Certificate Authority feature.

As depicted below enter your e-mail address, and choose a Common Name that is descriptive for the purposes that this certificate will serve. In our case I chose WeatherAlertsDevelopmentPush to remind me what I’m using this certificate for. Choose Saved to Disk.

tut_request_cert

Continue the prompts in Keychain Access and once you’ve saved your certificate request to disk, click Continue in the development portal and then Choose File… to upload the request to the portal. Once the file is uploaded click Generate. The developer portal will then generate your certificate and prompt you to Download, Install and Backup. Click Download.

tut_download_and_install

The file downloaded will be named something like aps_development.cer and placed in your Downloads folder. Navigate to the file and open it. Keychain Access will import the file and match it against the private key used by the certificate signing request. In Keychain Access click on My Certificates and you can use the search field to find your new certificate:

tut_push_certificate

If you thought you were finished with push notification configuration, you are mistaken! We told you there were a lot of steps involved. Continuing in the portal we need to create our application provisioning profile.

Creating a Provisioning Profile

Select Development under Provisioning Profiles and click the plus icon to the right of iOS Provisioning Profiles. When prompted choose a Development provisioning profile (the type used for installing development apps on test devices):

tut_what_type_of_pp

Click Continue to select the App ID of the application we will use with this provisioning profile. In particular, select the application that matches the one you just created for weatheralerts.

tut_select_app_id

Now choose the developer certificate(s) you want to be able to use with this provisioning profile. It is important to choose a Development certificate here, and one that you have already set up. Finally, choose all the devices that you want to be able to run the application on.

tut_name_pp

Name the profile (I chose WeatherAlerts Development) and click Generate.

tut_download_and_install_pp

Download and install the provisioning profile using the instructions on the screen.

Now that your provisioning profile is downloaded and installed go to your application target in Xcode, and choose Build Settings. In the Code Signing section, choose your code signing identity (which matches the certificate you chose to use for the provisioning profile),

tut_codesigning_identity

If everything is configured correctly this time, you should see didRegisterForRemoteNotificationsWithDeviceToken in your console log.

Configuring Your Application in Parse

To allow Parse to push notifications to your app, you first have to configure it with your application’s push notification certificate. This is the private key and certificate you created above in Keychain Access. Locate the certificate in Keychain Access by selecting My Certificates and the search field. Right-click the row that the certificate is on and choose Export “Apple Development IOS Push Services:…”. Name the file WeatherAlertsDevPushCert and ensure that the file format for the export is Personal Information Exchange. Save it to your Desktop, and do not enter a password to protect the exported items (this is important!). Keychain Access may prompt you for your login keychain password to export the certificate.

Go to your application’s Push Settings in your Parse console. The screen should look something like this:

tut_parse

In the section title Apple Push Certificates click on Select your certificate and choose the WeatherAlertsDevPushCert.p12 file you just created. You should now see something like this in your Parse portal.

tut_devpushcert_uploaded

Now, let’s send a test push notification. Again, the only way this will work is if all of the previous steps have been followed correctly. In your Parse portal, select the Push menu item for your application.

tut_send_test_push

Click Send a push and keep the Parse defaults with the exception of writing This is a test push notification in the field marked for writing your message. Scroll all the way to the bottom of the page and select Send now.

If your application was running in the foreground when you sent the test push, Parse did you the kind favor of creating an alert dialog box with given text. If your application was running in the background you should have received a standard iOS push notification dialog box. An example of our push notification received while the application is in the background:

tut_push_in_background

An example of receiving a push notification while the application is in the foreground:

tut_push_in_foreground

Bitbucket Xcode Project

The work done thus far is available on Bitbucket on a branch named part1. To use the project you can directly download the zip file and open the enclosed Xcode project. There are a few steps you’ll need to complete to use the project:

  • Sign up for Parse and obtain your own API keys
  • Change the Bundle Identifier to reflect your organization (that is, change it.iachieved. to com.yourcompany)
  • Create and add your own ApiKeys.plist file and use your Parse API keys
  • Create and configure your own provisioning profile

To get the full experience, however, I recommend creating your project from scratch and working through the tutorial one step at a time.

Part Two

Part two of this series will introduce using the CoreLocation capabilities of iOS to receive location information. This location information will in turn be used in Part Three to lookup whether or not there are any weather alerts in the area. Stay tuned!

By

Quickly Adding Multiple Frameworks to an Xcode Project

There are a few popular third party iOS frameworks such as AFNetworking and Parse that require you to add a number of Apple frameworks to your Xcode project. For example, if you add the Parse framework to your project you’re looking at also adding:

  • AudioToolbox.framework
  • CFNetwork.framework
  • CoreGraphics.framework
  • CoreLocation.framework
  • libz.dylib
  • MobileCoreServices.framework
  • QuartzCore.framework
  • Security.framework
  • StoreKit.framework
  • SystemConfiguration.framework

Adding each of these one at a time (with the pane closing after each addition) is annoying, so thankfully Xcode provides a quicker way using the Apple command key. To add multiple frameworks at once hold down the command key when making a selection in the Choose frameworks and libraries to add pane. After making all of your selections click Add.

add_multiple_frameworks

By

HTTP JSON Requests with Swift and Alamofire

Editor’s note: See our latest post on using Carthage for adding Alamofire to your project. You don’t need to suffer through git submodules (the method described below) any longer.

It didn’t take long after the introduction of Swift to begin seeing Stackoverflow questions asking about using AFNetworking, the popular Objective-C framework for making HTTP requests on iOS. Of course it can be done, as Swift and Objective-C can coexist together in the same project, but there’s the Objective-C way of doing things, and then there is the Swift way. Enter Alamofire, brought to you by the same author as AFNetworking.

As you can guess, we’re interested in using this new framework with our Swift projects. Let’s get to it shall we. This tutorial will walk you through creating a new Xcode project using Swift to make use of the MyMemory language translator to translate simple phrases from English to Spanish. Of course we could have built an application that allowed the user to choose the source and destination languages, but we wanted to leave it as an exercise to the reader.

Before you get started, I should point out that I’m using Xcode 6.1 (6A1052d). If you are using a different version, well, as they say, YMMV. While I suggest you go through this tutorial step-by-step, you can fast forward and download the working project on Bitbucket (see the end of this post for instructions to ensure Alamofire is updated in your checkout).

Start Xcode and select File – New Project and create a Single View iOS application. For the Product Name we chose translator, and of course make sure your language is Swift.

Before getting to Alamofire, let’s create a UI. Click on Main.storyboard to bring it up.

Drag four labels and a text input field to the storyboard, arranged as follows:

alamofire_constraints

In this example we have also added constraints to the layout. I’m by no means an expert on using Xcode constraints, but if I did it correctly the UI should look appropriate on the iPhone 5, 6, or 6 Plus. The layout should also rotate correctly.

Wire up the Text Field to the view controller as an IBOutlet. In this example I’ve named it textFieldToTranslate. Also wire up the label where we’ll display our translated text. I’ve named it translatedTextLabel.

alamofire_addtextfieldtotranslate

Your IBOutlets in ViewController.swift should look like:

@IBOutlet weak var textFieldToTranslate: UITextField!
  @IBOutlet weak var translatedTextLabel: UILabel!

Now, let’s add the appropriate delegate methods to the view controller so we can interact with the text field. This includes:

  • Adding UITextFieldDelegate to the ViewController class declaration
  • Setting the delegate property of the UITextField to the view controller
  • Adding the textFieldShouldReturn function to the ViewController class

I typically set the UITextField delegate property in the view controller viewDidAppear method, so let’s add that:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    
    self.textFieldToTranslate.delegate = self
  }
  
  func textFieldShouldReturn(textField: UITextField) -> Bool {
    
    textField.resignFirstResponder()
    
    return true
  }

We can compile and run our code as is, but of course it does next to nothing. Now, let’s wire up Alamofire to translate the text that is in the text field.

Using Alamofire

Alamorefire’s set up is a bit different than with Objective-C frameworks. The Alamofire Github repository currently states, Due to the current lack of proper infrastructure for Swift dependency management, using Alamofire in your project requires the following steps, followed by 7 steps to follow. We’re going to follow those here, with some additional detail provided.

Open a terminal (again, I like iTerm 2), and cd over to your Xcode project directory. In our case we cd ~/projects/blogging/translator, and use the git submodule feature to check the Alamofire code as a git submodule:

alamofire_addsubmodule

Now, with the Mac Finder, locate your project directory, navigate into the Alamofire folder, and drag-and-drop the Alamofire.xcodeproj icon from the Finder window into the Xcode project.

alamofire_addingxcodeproj

You should now see something like this in your Xcode project:

alamofire_addxcodeproj

Now, in Xcode, navigate to the target configuration window of Alamofire by clicking on the blue Alamofire project icon, and selecting the application target under the Targets heading in the sidebar. Ensure here that the Deployment Target of Alamofire is the same as the Deployment Target of the translator application. In our case, we are using a deployment target of 8.0.

alamoFireTarget

Now, click on the translator project icon (the blueprint icon), select the translator target and in the tab bar at the top of that window open the Build Phases panel. Expand the Target Dependencies group, and add Alamofire:

alamofire_targetdependency_alamofire

Finally, click on the + button at the top left of the panel (right under the label that says General and select New Copy Files Phase.

alamofire_copyfilesphase

Rename this new phase to Copy Frameworks, set the Destination to Frameworks, and add Alamofire.framework.

alamofire_addalamofireframework

And that’s all there is to it! Okay, so it’s a pain in the ass doing all that, but I find it worth the trouble. For a minute or two of drag-and-drop-and-configure we have an excellent API to begin making HTTP requests with.

Now that we have Alamofire added, let’s use it in our textFieldShouldReturn function. Remember, textFieldShouldReturn is going to get called when the user presses the action key on the iOS keyboard associated with our text field. First, add the statement import Alamofire at the top of your ViewController.swift file, like such:

import Alamofire

Then, in textFieldShouldReturn we’ll do the following:

let textToTranslate = self.textFieldToTranslate.text
        
    let parameters = ["q":textToTranslate,
                      "langpair":"en|es"]
    
    Alamofire.request(.GET, "http://api.mymemory.translated.net/get", parameters:parameters)
    .responseJSON { (_, _, JSON, _) -> Void in
      
      let translatedText: String? = JSON?.valueForKeyPath("responseData.translatedText") as String?

We’re following the API at MyMemory for translating text from English to Spanish. Notice the simplicity of both the HTTP request with Alamofire as well as handling the response. .GET is an enumeration for the HTTP method to use for the request, followed by our URL, and then a basic dictionary of URL parameters.

The MyMemory API call returns a JSON string, so we can utilize the Alamofire responseJSON function to give us a JSON dictionary (it handles taking the JSON string returned in the body and converting it for us). Although it looks careless the method in which we are getting the translatedText is good form. Since we declare translatedText as String? we are saying “This could be a String or nil.” Moreover, by using JSON?.valueForKeyPath() we are saying, “JSON could respond to valueForKeyPath or it could be nil“. If JSON is nil then it follows translatedText will be nil as well. If JSON is not nil and we find a value at the given key path responseData.translatedText, then we have our translation available (which we’ll display in the label).

All of this code makes heavy use of chained optionals in Swift. For a refresher in Swift optionals in general, visit our post on the topic.

Once we have the translated text we update our view controller label:

if let translated = translatedText {
        self.translatedTextLabel.text = translated
      } else {
        self.translatedTextLabel.text = "No translation available."
      }

Alamofire has a lot of features, and of course we’ve only scratched the surface. Take a look through the extensive documentation on Github; it’s all there, support for POST and the remaining cast of characters, various authentication methods (eg., basic auth), uploading data, etc.

Let’s run our new application and translate Good night, friends! into Spanish.

translator_run

Nice, it worked! Of course, my Spanish-speaking amigos will point out that the opening signo de exclamación is missing, but that can be corrected.

That’s it for today, I hope you enjoyed the tutorial. Again, if you’d like a working example visit the Bitbucket repository. After checking out the repository, make sure and run the following in your alamofire-translator directory:

Exercises for the Reader

You will no doubt notice that the application is only capable of translating from English to Spanish. MyMemory allows for any-to-any translation, why not add a popup menu to allow for choosing which language to translate from and to? And while the HTTP request is pretty quick there’s room perhaps for a progress indicator somewhere on the screen, as either a HUD or basic activity spinner.

By

A HomeKit Update on iOS 8 GM Seed

In our last article we wrote quite a bit about Apple’s HomeKit and it’s usability on iOS 8 beta builds. Well, the iOS 8 GM Seed is available, and while it fixes a few problems, I still have doubts as to whether we’re going to see a useful ecosystem of home control devices any time soon.

Let’s take a took at the state of HomeKit in iOS 8. First, however, if you are new to HomeKit and haven’t read our original article, you might want to do so. This post will jump right in to using the Home Wizard code we posted on BitBucket.

The Good

Let’s first talk about what’s good with this release, and that’s the annoying “lost accessory” bug is gone. This is the issue where if you have an Add Accessory dialog up (to enter the accessory PIN) and the homeManagerDidUpdateHomes delegate was called by HMHomeManager you’d lose your accessory. That is, the completion handler to your addAccessory function would never get called, and the accessory browser wouldn’t see your accessory any longer. The only recourse was to reset your HomeKit configuration and try again.

The Bad

Unfortunately there’s no more good, only bad. Let’s start with OSStatus code 52 (ENETRESET). This is quite the obnoxious error one could get when trying to pair an accessory using an iCloud account that had already been used with HomeKit on the iOS 8 beta releases. I am not kidding when I tell you that to get around this error I had to:

  • Unlink from my iCloud account on iOS 8 GM
  • Create a new iCloud account
  • Log out of my previous iCloud account on the Mac
  • Log in with the new iCloud account on the Mac

On top of this I had to re-enable my iCloud Keychain access and reset my HomeKit configuration on the iPhone several times before finally this error went away. Frustrating to say the least!

Let’s move on to the next HomeKit issue on iOS 8 GM Seed, a warning that “This accessory is not certified and may not work reliably with HomeKit.” with accessories that were created using Apple’s own HomeKit Accessory Simulator. I don’t know what constitutes a certified accessory, but apparently Apple’s simulated accessories aren’t.

image2

Pressing on Add Anyway does result in a successful pairing however.

Which brings me to Apple’s HomeKit Human Interface Guidelines, and I quote, “Avoid using acronyms and technical terminology that they might not understand. HomeKit is a technical term referring to APIs, and should not be mentioned inside your app.” If that’s the case then

  • Why does the iOS privacy settings have an icon and label for HomeKit?
  • Why is there an option to reset HomeKit Configuration?
  • Why do the iOS dialog boxes clearly refer to Add HomeKit Accessory?

The iOS documentation writers and developers are clearly not communicating. If my application refers to HomeKit is it going to get rejected when I go to post it to the App Store? If we aren’t supposed to refer to HomeKit what should we call it?

And finally, as of right now iOS 8 GM Seed Siri has zero knowledge of HomeKit. Trying to lock up Grandma resulted in Siri initiating a Web Search.

image3

Now, I’ll give Apple the benefit of the doubt on this one. Like the rest of its services I’m sure there are beta Siri servers and production Siri servers, and the iOS 8 GM Seed is configured to communicate with production, which isn’t HomeKit-aware yet. If that’s true then we should expect Siri to begin working with HomeKit shortly after iOS 8 is officially released (September 17, 2014).

Update: As of September 17, 2014 Siri HomeKit awareness has gone live in production with the release of iOS 8!

What’s New in Home Wizard

Home Wizard has been updated to include a tabular view of your Homes, Rooms, and Accessories. As of the current posted version to Bitbucket (1.0.1), you can delete homes and rooms. Accessories cannot currently be deleted (working on that one). In addition, Home Wizard allows you to specify whether or not you want to add a home as a Primary Home.

image1 (3)

Nattering Nabob

Look, I understand HomeKit is new, and is going to have some rough edges, but there are so many here that its hard to believe that there will be a robust HomeKit environment with the launch of iOS 8, and that’s sort of what Apple trumpeted at WWDC 2014. Sure, they have a lot going on with Apple Pay, iPhone 6, iOS 8, Apple Watch, Swift, etc., and all of these kinks will be eventually worked out, it’s just disappointing that on the eve of iOS 8 going live this is the best we have to work with. Hopefully we’ll see some exciting updates soon.

By

An In-Depth Look at Apple’s HomeKit

I’m actually surprised there is so little additional material on the web regarding Apple’s HomeKit, so I thought I’d collect my notes and experiences with the nascent framework and post them here. If you review the HomeKit Framework Reference you’ll notice a few things. One, it’s a bit light on detail. Little more than a perfunctory introduction and class hierarchy. Second, its immediately apparent (from the class hierarchy) how Apple wants to organize your home(s) and smart controls. The rationale is obvious.; if you’re going to be talking to Siri and asking her to “turn on the lights,” you might want to be able to specify which lights.

If you poke around a little further on the HomeKit portal and iOS 8 pre-release documentation you will also realize that there are no code samples or supplementary documents (other than the WWDC workshop videos) either. Moreover there’s no OS X framework provided for HomeKit, so for the time being you can forget creating an OS X App Extension for your thermostat.

HomeKit Basics

Let’s walk through the basics of HomeKit. First, the terminology.

In HomeKit-speak, a Home can best be thought of as a standalone dwelling. An apartment would be a Home, as would your typical house on Spooner Street. If you had your own personal thermostat or lamp in your office, you might designate it as a Home (your office, not the thermostat or lamp).

With HomeKit you can have multiple homes defined and controlled through your iPhone. I only know a few people with multiple homes (I’m not one of them, but hey, I can dream), but I do know a lot of folks with grandmothers. So in our example we’ll use Grandma’s House to periodically check her thermostat. She’s always raising it to save money, but we want to make sure she stays cool in the summer.

Most homes I know of are comprised of rooms, and sure enough HomeKit provides the concept of a Room. Typical rooms might be the Master Bedroom, Utility Room, Kitchen, Garage, and so on. In HomeKit rooms are dimensionless and have no location, so it’s best to think of a Room as any label you might apply to a general area in or around the house. In our example we have Front Entry and Front Porch as rooms. Of course they aren’t rooms in the strictest sense but if you want to ask Siri to “turn on the front entry lights” she’s going to have to know about the “front entry.”

Finally, Rooms can belong a Zone. An obvious application of a zone would be the Upstairs and Downstairs. “Siri, set the upstairs thermostat to 74 degrees.” The truth is, this is about the only use I can see for zones, and how often do you want to turn on everything upstairs? You could just as easily create a Room called Upstairs and put a thermostat in it.

What you won’t find in the HomeKit Framework documentation are the details about physical accessories and how they are communicated with via iOS. These details are the purview of the HomeKit Accessory Protocol, access to which is restricted to MFi Program members.

HomeKit Accessory Protocol

I’ll take pains to point out now that there are a number of competing Home Automation protocols out there. Z-Wave, ZigBee, Insteon, X10 are just to name a few. Apple is introducing another, the HomeKit Accessory Protocol, or HAP. HAP specifies both the transport mechanism as well as the API for controlling a given accessory.

Two HAP transport mechanisms are currently defined: IP and Bluetooth low energy (BTLE) (also known as Bluetooth Smart). Again, this makes sense. IP, particularly in the form of IP over WiFi, is ubiquitous in most homes that are looking to “get smart.” As for BTLE, well, I’m no expert, but it has been pushed as an alternative to Z-Wave or Zigbee. This makes sense given that all “smartphones” already have Bluetooth radios so you can communicate directly with your device without the need of a hub. Of course, this makes less sense when you consider remote access will still require a hub of some type in your home. Z-Wave and ZigBee aren’t going to be knocked off their perch any time soon, so it remains to be seen whether there will be a significant shift in the transport mechanism for smart devices.

Sitting on top of the transport mechanism is the control API. This is where the rubber meets the road in terms of controlling your accessories. “Siri, turn on the light.” This must get translated into a command sent to the accessory, and there must be a concept of “on” that the accessory understands. “Siri, turn the thermostat to 70 degrees.” Somewhere on the wire is the value 70 (or more likely, 21.1) along with information about whether this is the cooling setpoint or the heating setpoint. Again, HAP provides the protocol and format of how to convey that information.

Services and Characteristics

A HAP accessory need not be something as simple as a lightbulb or even a thermostat. Imagine a ceiling fan (they are popular in Texas) that has both a fan motor and a lightbulb. Although it’s one accessory (“the ceiling fan”) it provides two services: a fan and a light. The fan can be on or off, and likewise, the light can be on or off. But, wait, there’s more. Not only can the light be on or off, but it could dim or bright. Yet another adjustable attribute.

Each of these adjustable attributes are characteristics. Whether or not you can physically adjust the attribute is irrelevant. You can’t adjust how hot the thermostat thinks it actually is, but its an attribute that adjusts. It becomes readily apparent that like HomeKit, HAP has a structured hierarchy. Accessories have services which in turn have characteristics.

Hands On With HomeKit

As I have found little in the way of hands-on examples using HomeKit, I’ve put together this basic walkthrough. Disclaimer: Like you, I’m still learning the nuances of HomeKit and coding with the API. There are likely bugs in the code to be shared, and as you find them I’d love your feedback and comments.

Starting with iOS Beta 1 and XCode 6 Beta 1 Apple began delivering to developers the HomeKit framework and the HomeKit Accessory Simulator since folks couldn’t go down to Home Depot in early June and purchase a HAP-enabled switch. My goal here is to provide a walkthrough of HomeKit using the accessory simulator with an aim at interacting with Siri. To me that’s the selling point of HomeKit. I already own a Z-Wave hub (three of them actually) and thermostat I can control through the web. We’ve been doing that since 2010. What’s new and powerful here is Siri integration, followed by integration with other iDevices.

To follow along at home you are going to need Xcode 6 (preferably the latest beta, which as of this writing was Beta 7) and an iPhone running iOS Beta 5. Download our Home Wizard and start the HomeKit Accessory Simulator (which has moved as of Xcode 6 Beta 6 to the Hardware IO Tools for Xcode).

We’re going to create the following “home control ecosystem”. Our primary home will be named ‘Spooner Street’ and it will have:

  • a front entry “room” with a light
  • a living room with a ceiling fan, the ceiling fan having an on/off switch and a light
  • a thermostat

Our secondary home will be named ‘Grandmas’ and will have:

  • a single porch light
  • a front entry door with a lock
  • a thermostat

Once we’ve created the accessories in the HomeKit Accessory Simulator we will build our ecosystem with our Home Wizard and we should be able to start talking to Siri with some natural language phrases like:

  • Lock the door at Grandma’s
  • What is the thermostat set to at Spooner Street?
  • Turn the living room fan on
  • Turn off the front entry light
  • Set Grandma’s thermostat to 76 degrees
  • Turn on all the living room lights
  • How many lights are there?
  • Is the front entry light turned on?

And so on.

Okay, let’s get started. Start the HomeKit Accessory Simulator and in the lower left click on the + button to add an accessory. Start with an accessory light by choosing New Accessory and then populate the Name and Manufacturer. We’ll purchase all of our HAP gear from Acme (they’re into everything); the AcmeLight, AcmeThermostat, you get the idea. For the Name type AcmeLight, for the Manufacturer type Acme.

AddAccessory

What one notices right away with the simulator is that each accessory can be populated with an array of services. The light itself isn’t the accessory, it’s a service that the accessory provides. So add a Lightbulb service. Click Add Service and Add LightBulb to see:

AcmeLight

The ceiling fan will be an AcmeFan, and it will have both a lightbulb service and it’s own intrinsic characteristic of On. To create the AcmeFan go back to the + in the lower-left corner and select New Accessory and give it a name of AcmeFan with a manufacturer of Acme. Rather than adding a service first, notice the Add Characteristic under the Name, Manufacturer, etc. Click it and for the Characteristic Type choose On. You can leave the defaults as is and simply click Finish. Then add a Light Bulb service. Your completed AcmeFan should look something like this:

AcmeFan

Finally add a thermostat by adding the accessory AcmeThermostat, then adding the Thermostat service to it.

Once done your HomeKit Accessory Simulator should look something like this:

AcmeAccessories

Now, let’s add Grandma’s accessories. Here’s where you can speed things up by going to Bitbucket and checking out the homekitaccessories repository. You will find .hak files to import into the simulator. Simply download and then in the Homekit Accessory Simulator choose Import Accessory from the File menu. You can import as many lights, thermostats, etc. as you like.

Once everything is configured we can start testing things out to see if Siri understands what we’re talking about.

Using Home Wizard

Disclaimer! Home Wizard is more like Home Wizard’s Apprentice, or Home Wizard’s Chambermaid. The code is very immature but should get you creating a few homes and rooms and putting accessories in them. As HomeKit itself matures we’ll be working on the code to make it more robust, easier to use to manipulate your “ecosystem,” and of course, polish the UI. Again, you can get the code from Bitbucket.

If you have played with HomeKit before, go ahead and go to iOS Settings > Privacy > HomeKit and Reset HomeKit Configuration. This will give us a nice clean slate to work from.

NB: If you haven’t worked with HomeKit before you will need also need to iOS Settings > iCloud > Keychain and enable iCloud Keychain. Failure to do so will result in the annoying Error Domain=HMErrorDomain Code=78 error when trying to add a home.

Build and launch the Home Wizard.

Spooner Street

The first screen you see will have a text dialog under the phrase Home Name. Type in Spooner Street and press Add Home. If adding the home was successful you should see a dialog indicating so. Enter another home named Grandmas. Then press Continue.

The next screen will allow you to create rooms and assign them to a Home. Remember, we’re going to create a front entry and living room for Spooner Street, and since we’re going to add a “house-wide” thermostat we’ll add one called House. So three rooms for Spooner Street: Front Entry, Living Room, and House.

Side Note:HomeKit does provide for accessories to be added to a “house-wide” room, a pointer to which can be obtained by calling HMHomeManager.roomForEntireHome(). For our purposes it was easier to create a “House” room in the UI. Future versions of Home Wizard will have the option to use the roomForEntireHome feature.

Grandma’s House

At Grandma’s we’ll have her front entry, front porch, and a “house” room (for the thermostat).

After you are done adding rooms, press Continue and you will get to the Place your Accessory page. On this page you will see accessories populated in the picker view. If you don’t there may not be any accessories enabled in the HomeKit Accessory Simulator, or if your Wifi isn’t enabled on your iPhone you won’t see them. For IP, accessories actually present themselves as Bonjour services that your iPhone discovers. If you are going to find yourself working a lot with the HomeKit Accessory Simulator, or perhaps are writing your own accessory, you might want to check out the Bonjour Browser application for the Mac. It’s very handy and will show you when accessories come and go in the Bonjour services list, simply look under _hap._tcp.

BonjourBrowser

Note: If you are a member of the MFi Program and have access to the HomeKit Accessory Protocol for IP, the details of the Bonjour TXT records for the accessories is documented there.

Now, let’s add our accessories to the Spooner Street house. If you’ve already added Grandma’s accessories to the simulator you’ll probably notice that there are entries in the accessory list marked with (2). That’s annoying, but if there are two accessories with the same name on the network, they have to be noted as being different somehow. We’ll add the AcmeFan to the Spooner Street Living Room first. Select AcmeFan in the first wheel, and then select Spooner Street on the left and Living Room on the right:

IMG_5922

Press Add Accessory. At this point you will get a dialog initiated by iOS requesting the setup code for the AcmeFan. This is the code you see listed in the HomeKit Accessory Simulator. In our case its 721-47-549. The dashes are for show, you don’t actually enter them. If the code is accepted you should see a dialog box indicating that “AcmeFan has been added to Spooner Street Living Room”.

Finish out Spooner Street by adding the thermostat to the House room, and the light to the Front Entry.

Once Spooner Street is complete you can add Grandma’s accessories. I won’t go through the nitty gritty steps here, it should be obvious. Just recall at Grandma’s we’ll add

  • a single porch light
  • a front entry door with a lock
  • a thermostat

Talk to me Siri!

Now that we have our accessories added, let’s start trying to control them through Siri. If your iPhone is connected to power you don’t even have to press the Home button, just say, “Hey Siri.”

Okay, let’s see how she does. We’ll start off with a straightforward question, “How many thermostats are there?” We have two thermostats, one in the house at Spooner Street and one at Grandmas, so we would expect her to say “two”. When asked though she says one. It turns out when you don’t specify which home you are referring to Siri will default to the primary home you have configured. In Home Wizard the first home you add is defaulted to the primary, so, since there is one thermostat in the Spooner Street, she replies “one”. You can ask the questions How many thermostats are there at Spooner Street? and How many thermostats are there at Grandmas? and she responds correctly.

Let’s adjust each thermostat independently. “Hey Siri, set the thermostat to 74 degrees cooling.” Siri responds back “I set the thermostat to 73.9.” Okay, a tenth of degree off is fine. But unfortunately she didn’t turn it to Cool, and moreover, no plain English phrases I tried could get her to change the thermostat mode.

thermostatAccessory

If you are familiar with Z-Wave thermostat command classes you are used to ThermostatMode and ThermostatOperatingState. In the screen shot above the target mode corresponds to Z-Wave’s ThermostatMode where as current mode corresponds to the operating state (that is, what is the HVAC system doing right now? Cooling? Heating? Off?).

For setting the thermostat at Grandma’s, all of the following questions and commands were accepted.

  • What’s the thermostat set to at Grandma’s?
  • Set Grandma’s thermostat to 74 degrees.
  • Set the thermostat at Grandma’s to 74 degrees.
  • What’s the temperature at Grandma’s?

Enough with thermostats. From my perspective they are broken with Siri integration. Let’s move on to the fan at Spooner Street. Here we have even less luck. No matter how hard I try I can’t get to the intrinsic “on” characteristic of the fan. For example, Turn the AcmeFan on is met with Hmm… I don’t see any HomeKit accessories. But once you’ve set some up, together we could rule… your home.. Well, Siri is certainly optimistic about the future of home automation.

Update: I was quick to come to the conclusion that there is such as a thing as intrinsic characteristics for an accessory, that is characteristics that weren’t tied to a service. Wrong! There is no such thing!. See below for more details.

What we can do is address the light that is a part of the fan, but only in the context of it being in a certain room. Turn off the fan light results in all lights at Spooner Street being turned off! Sacrebleu! A quick table outlining the results of each command to Siri, and remember, without a specifier, Siri operates on the primary home only:

Siri CommandResult
Turn on the lights.All of the lights are turned on.
Turn off the lights.All of the lights are turned off.
Turn on the living room lights.Only the AcmeFan light is turned on.
Turn on the front entry light.Only the light in the front entry room is turned on.
Turn on the fan light.All of the the lights are turned on.

Even more interesting are questions such as Are the lights on? In my tests Siri only considered the light in the living room. If it was turned on Siri would respond “The light is turned on.”

Finally, let’s take a look at Grandma’s door lock. The door lock accessory responded nicely to my commands, but only if I specified that the lock was at Grandma’s. Failure to specify the location defaulted to the primary home, which in our case is Spooner Street. But all of the following responded correctly:

  • “Is Grandma’s door locked?” resulted in “The door lock is unlocked.”
  • “Lock Grandma’s door.” resulted in “The door lock is locked.”
  • “Unlock Grandma’s door.” resulted in “The door lock is unlocked.”

Siri Conclusions

Most folks don’t have two homes, so they don’t need to worry about being that specific with Siri. However, homes with multiple lights and switches will have challenges. Grouping the lights into rooms or zones will certainly help, but even then I was frequently met with I don’t see any HomeKit accessories. For example, the Front Entry room would have its accessories occasionally “disappear” in the sense Siri wouldn’t recognize the room name. In one case I said, “Is the front entry light on?” and Siri caught “Is the front entryway turtle?” Charming. Perhaps like other Siri encounters you find yourself having to ar-tic-u-late slowly and with em-pha-sis.

Siri seems to have a few canned responses to when it thinks you are talking about an accessory and it can’t find it in the hierarchy. I’ve been met with Hmm… I’m not seeing any HomeKit accessories. But if you set some up, I think I’d enjoy the company. along with But once you’ve set some up, together we could rule… your home. and But I can help you once you’ve set some up.

Notable Bugs and Issues

Before wrapping up I’d thought I’d share some additional learnings I’ve discovered working with HomeKit.

I’ve noticed that if you are on a screen and initiate browsing for accessories (the screen where you place your accessories into homes and rooms) and you add a device to the HomeKit Accessory Simulator, you frequently get duplicate accessory notifications from HomeKit. For example, here’s how to start browsing for accessories:

self.accessoryBrowser = HMAccessoryBrowser()
    self.accessoryBrowser!.delegate = self
    self.accessoryBrowser!.startSearchingForNewAccessories()

That seems simple enough. Declare my view controller as an HMAccessoryBrowserDelegate, set the delegate reference, and start searching for new accessories. The delegate function

func accessoryBrowser(
    browser: HMAccessoryBrowser!,
    didFindNewAccessory accessory: HMAccessory!)

gets called. Unfortunately it gets called, and called, and called when adding a single accessory to the simulator:

No, I didn’t add two AcmeThermostats, so why are they there! They even have separate identifiers. This only seems to happen if the startSearchingForNewAccessories() routine is active when I add an accessory to the simulator, so the workaround is either don’t do that or go back to a previous page.

While the previous issue I consider to be a downright bug, the delay between executing a HomeKit routine and when you can actually make use of the outcome is irritating. Consider the following:

func addHome(homeName:String!) {
    
    self.homeManager.addHomeWithName(homeName, {
      (home:  HMHome?, error:  NSError?) -> Void in
      if let addHomeError = error {
        NSLog("Error:  (addHomeError.localizedDescription)")
      } else {
        NSLog("Added home:  (home)")
      }
    })
}

Now, you would think that if the completion handler of addHomeWithName was called you would be able to begin working with the home object. You would be wrong. You must wait until the homeManagerDidUpdateHomes delegate method is called before you can assume that the home is actually available.

This bit me a number of times, and continues to do so. Another example is when presenting, say, a list of homes and you allow the user to delete one of them. This routine looks straightforward enough:

alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: {
        (action:UIAlertAction!) -> Void in
        let hm = HomeManagerControllerSharedInstanced
        let index = indexPath.row
        hm.homeManager.removeHome(self.homes[index] as HMHome, completionHandler: { (e:NSError!) -> Void in
          NSLog("Home removed")
          
          // Update our data
          self.homes = hm.myHomes
          
          // Remove the row
          self.homeKitTableView.deleteRowsAtIndexPaths([indexPath as AnyObject], withRowAnimation:UITableViewRowAnimation.Automatic)
          self.homeKitTableView.reloadData()
          }
        )
        
        }))

Our action when trying to delete a table entry that is a home is to ask our HMHomeManager to remove the home, and we provide a completion handler. Again, you would think that once the completion handler was called, our home would be gone, and we can refresh our datasource (self.homes = hm.myHomes), but you would be wrong again. You must wait until the homeManagerDidUpdateHomes delegate function is called. This can make for some, well, interesting code like this:

hm.homeManager.removeHome(self.homes[index] as HMHome, completionHandler: { (e:NSError!) -> Void in
          NSLog("Home removed")
          
          // Register for a HomesUpdated notification and then delete
          NSNotificationCenter.defaultCenter().addObserverForName(kHomesUpdated, object:nil, queue: nil,
            usingBlock: { (notification:NSNotification!) -> Void in
              // Update our data
              self.homes = hm.myHomes
              
              // Remove the row
              self.homeKitTableView.deleteRowsAtIndexPaths([indexPath as AnyObject], withRowAnimation:UITableViewRowAnimation.Automatic)
              self.homeKitTableView.reloadData()
          })
          
           }
        )

The obvious question is, “Hey, why all the delay?” We’re just building a simple data structure of has-a relationships between Homes, Rooms, and Accessories, right? Well, yes, conceptually the data structure is simple. But remember when we said that you had to have iCloud Keychain enabled to even work with HomeKit? That’s right. Every action and operation taken to our home ecosystem is being pushed to iCloud. The iOS completion handler feedback is instant: the synchronization with iCloud is far from it. Of course this raises the question as to how to handle this in the UI. If I want to allow you to add Rooms to your newly created Home, but I can’t until I’ve received a notification that the homes have been updated then I need to prevent it somehow or make you wait. Of course that means progress HUDs. Our reworked function to handle removing a home is as follows:

var alert = UIAlertController(title: kAppName, message: "Are you sure?  Deleting a home will remove all rooms and accessories in that home.", preferredStyle: UIAlertControllerStyle.Alert)
      alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: {
        (action:UIAlertAction!) -> Void in
        let hm = HomeManagerControllerSharedInstanced
        let index = indexPath.row   
        var hud = JGProgressHUD(style:JGProgressHUDStyle.Dark)
        hud.textLabel.text = "Removing Home";
        hud.showInView(self.view)      
        hm.homeManager.removeHome(self.homes[index] as HMHome, completionHandler: { (e:NSError!) -> Void in          
          // Register for a HomesUpdated notification and then delete
          var tmpObserver:AnyObject? = nil
          tmpObserver = NSNotificationCenter.defaultCenter().addObserverForName(kHomesUpdated, object:nil, queue: nil,
            usingBlock: { (notification:NSNotification!) -> Void in
              // Update our data
              self.homes = hm.myHomes
              
              // Remove the row
              self.homeKitTableView.deleteRowsAtIndexPaths([indexPath as AnyObject], withRowAnimation:UITableViewRowAnimation.Automatic)
              self.homeKitTableView.reloadData()
              
              hud.dismiss()
              
              // Remove our temporary observer, otherwise this block will fire
              // again when homes are updated
              NSNotificationCenter.defaultCenter().removeObserver(tmpObserver!)
          })
          
           }
        )
        
        }))

Even more troublesome is being in the middle of an action such as adding an accessory and having the HomeKit data change while it is in progress. On numerous occasions I’ve seen callbacks (completion handlers) simply fail to be called if homeManagerDidUpdateHomes is called after you’ve called HMHome.addAccessory(HMAccessory!, ((NSError!) -> Void)!)). It’s not that you get an error, you simply don’t get called at all (likely because the HMHome reference you started with has changed underneath you). At the moment I haven’t found how to get around this issue.

Intrinsic Characteristics

After rereading the documentation, both the HomeKit Framework and HomeKit Accessory Protocol for IP, I’ve come to the conclusion that intrinsic characteristics are not supported. It appears that is supported in the simulator, but that is misleading. Adding a characteristic of on to the accessory only adds an entry in the Accessory Information service. As a “work-around” for our AcmeFan, you could add a Switch service to the accessory, but even this is less than satisfactory.

HomeKit Remote Access

I’m going to save the HomeKit Remote Access topic for another article. It’s already confirmed that the AppleTV will be used to facilitate remote access to HomeKit accessories, and iOS 8 Family Sharing will likely be used to share the HomeKit configuration with other iPhones in the family (how else would it work? It’s doubtful everyone in the house would have to go around pairing their phone with accessories).

Conclusion

I have high hopes for Apple’s HomeKit. To reiterate, there’s already a plethora of home automation solutions on the market. Mi Casa Verde, Wink, HomeSeer are to name but a few, and startups such as Prodea Systems and open source projects like The Thing System continue to pop up all over the landscape. What I see that sets Apple apart is number one, it’s Apple. Number two, the use of Siri for voice control is powerful. It will become even more so when you no longer have to be tethered to your phone but have mic pickups in the house that are listening for your commands. There’s still be a place for a user interface and application extensions (let’s face it, if you are at your Mac its going to be quicker to access the “widgets” pane and adjust the thermostat than it is to start talking to Siri), but voice control will be the future of home automation.
Finally, an AppleTV as a remote access point is also powerful. The device is already in your home, and if it isn’t for $99 you get not only remote access to your accessories, but a beautiful media renderer to boot.

Even with those high hopes I have to come to the conclusion that HomeKit is not ready for prime time, at least in its current iOS 8 Beta 5 incarnation. Completion handlers firing before the HomeKit database is really updated, multiple accessories appearing when there is only one, Siri unable to adjust even the most basic thermostat controls, and oh, yes, remote access through the AppleTV doesn’t work with Siri either (it times out essentially); these are just a few of the issues I’ve encountered. I was hopeful that mid-August would bring an update to iOS 8 that would resolve these issues, but was left disappointed. It remains to be seen whether they will be addressed when iOS goes GA.

PostScript

Are you working with HomeKit or other automation protocols? I’d love to hear from you! I’ve been working on a Z-Wave bridging accessory and if you are a member of the MFi Program would enjoy the opportunity to share experiences. Drop me a line at joe@iachieved.it.

By

NSNotifications with userInfo in Swift

Update 9/18/16: Swift 3.0 has brough even more changes to working with notifications and userInfo; come visit our new article on handling Notifications with Swift 3.0

Update: Swift 2.2 has brought a number of changes; come visit our new article on handling NSNotification userInfo in Swift.

I frequently sit down intent on writing about one topic, only finding myself in a situation where I feel motivated to write about something else. This is one such post.

This morning I started with working on a HomeKit application, only to take a detour to working with NSNotifications in Swift. I’m going to make the assumption that you are already familiar with both NSNotification and NSNotificationCenter. I tend to use these a lot when programming applications that have a number of moving parts and threads. I may be doing it all wrong and I’m sure there’s some pattern I’m missing out on, but the model of broadcasting events (notifications) to interested parties has served me well.

Let’s look at the basic Swift method for getting a handle to the default notification center (I’m going to break my verbosity rule here and use nc as the variable name rather than notificationCenter):

Simple enough. Now, I want to register my object (typically a view controller) for receiving a notification.

In plain English, I’m telling the notification center to call my function addHomeError whenever someone posts a kAddHomeError notification. self is the object to call and addHomeError is the function. Notice that the Objective-C @selector() construct is gone in favor of naming the function as a string. And of course kAddHomeError is simply a constant string defined somewhere else. In this case the definition is let kAddHomeError = "AddHomeError".

In another object I can post my notification with:

Since my first object registered to receive a notification when kAddHomeError was posted, its addHomeError function will be called.

In both cases I’ve left the object parameter nil. This parameter is applicable only if you want to distinguish between separate objects registering and posting notifications. When specifying nil in the addObserver function you are saying, “Hey, I don’t care who posts this notification, send it to me.” If you provide an object to the object parameter you are making a statement, “I’m only interested in notifications from this specific object.”

In the above example our function we want called is addHomeError, so we need to write it:

Given that I’m calling presentViewController in our function, it’s a safe bet to assume that I’m handling receipt of the notification in a view controller. So our notification comes from one object, and is handled in a view controller. Straightforward. What I don’t like though is the error message is Unable to add home. It would be nicer if the dialog indicated the name of the home it couldn’t add.

We can accomplish that through the use of the userInfo parameter when calling postNotificationName. It took a minute to figure out the function signature, because quite frankly it looks a little, well, non-obvious. Let’s take a look.

Perhaps you’re a quicker study than I am, but… [NSObject : AnyObject]?. Huh? What? (No pun intended.)

A glance back at the Objective-C signature for postNotificationName and the lightbulb went off. Or at least started glowing a little. The userInfo parameter in Objective-C is an NSDictionary and it stands to reason Swift would be no different. And it isn’t. The signature syntax just looks strange. Seriously though, all you need to do is pass in a dictionary where the keys are derived from NSObject and the values are AnyObject. The question mark is just there to signify that this parameter could be nil.

Unlike earlier versions of Objective-C ([NSDictionary dictionaryWithObjectsAndKeys:] anyone?), Swift comes out of the box with so-called “literal dictionaries,” a construct that other languages such as Python and Ruby call just dictionaries. So we don’t have to do any special dictionary-building to use userInfo. Our error notification can now be transformed into:

We don’t have to limit ourself to a single-entry dictionary either:

Now, let’s handle our notification. The first thing we need to do is update our function to be passed the notification object which contains our userInfo. Recall the first version of the function had no arguments, that is, we didn’t care about the NSNotification object itself.

Our method signature becomes func addHomeError(notification:NSNotification). If we made this change now and ran our application it would most surely crash. Why? Because of this right here:
nc.addObserver(self, selector: "addHomeError", name: kAddHomeError, object: nil)
. That selector definition indicates a function named addHomeError which takes no arguments. We don’t have that anymore, we take one argument. So you’re in for a unrecognized selector sent to instance crash if you try that. Update the selector to addHomeError::

Finally, in our addHomeError function, let’s unwrap our userInfo dictionary and message.

Now perhaps there’s an easier way to do this, but, when receiving the userInfo, the routine handling the notification doesn’t know what type of dictionary is there. Recall that the only requirement was that it be of type [NSObject : AnyObject]?. So our code has to be a bit more explicit as what we know the dictionary to be, and that is of type [String : String!]. Once we’ve set the compiler straight, we obtain our messageString and supply it to our UIAlertController.

Now we get a nice error message:

image1

With that out of the way I can get back to our upcoming article on HomeKit.

By

#if 0 In Swift

I’m enjoying learning Swift, but it didn’t take long before I took to Google looking for #if 0 in Swift. #if 0 (pronounced, by me at least, as “pound if zero”) is a quick and easy way to get around C’s lack of support for nested multiline comments. Consider this snippet:

In C (and C++, and Objective-C), that’s a big no-no. GCC proclaims warning: "/*" within comment and then unceremoniously dies with error: unexpected expression before '/' token. Even the syntax highlighter I use in WordPress trips up on it and leaves the last */ without decoration. Amusing.

So rather than using /* and */ to comment out a block of code, I’d rely on #if 0, like this:

Voilá. Problem solved. Once I’m done experimenting or troubleshooting a bug I can either delete the code within the #if 0 block, or remove the #if and #endif pair.

Of course I tried using my trusty #if 0 when coding in Swift, only to find that it isn’t supported. This didn’t shock me; #if is a C preprocessor directive after all. But I still wanted a way to quickly comment out a block of code and not worry about whether there were other comments within the block. So, again, I went straight to Google looking for #if 0 in Swift. What I should have been searching for is nested multiline comments in Swift..

Straight from the horses mouth (also known as the Swift Programming Language Guide): Unlike multiline comments in C, multiline comments in Swift can be nested inside other multiline comments. … Nested multiline comments enable you to comment out large blocks of code quickly and easily, even if the code already contains multiline comments.

Well, isn’t that handy! Now I can comment out large blocks of code at will by placing /* and */ around them. Life is good. 🙂