With Apple Watch set to be released next month we decided to get our hands dirty by building a sample WatchKit app. We didn’t build anything earth shattering, but wanted to prove we could put together the different pieces of an iOS/WatchKit application that shares data between each other. This is what we learned.

For starters, today there is no such thing as "building a stand-alone Apple Watch app". A WatchKit app is really just an extension of an iOS application. Apple Watch may eventually support native apps, but for now just think of them as providing a glimpse of an iPhone app’s data to the user’s wrist. In order for an Apple Watch to be useful it must be able to communicate with a nearby iPhone as it does not have its own data or GPS connection. Code running on the iPhone is responsible for sending data to and from the watch.

The most challenging part of building this app was finding working examples to base our code on. Since the Apple Watch and Swift eco-systems are both still maturing, code examples written just a few months ago no longer build or are behind at least one Swift version. Now that we have a functioning app we wanted to share the knowledge by open-sourcing our code. Undoubtedly our code will break with future releases, but as of March 4th, 2015 at 4:37pm the repo is compatible with Xcode 6.3 beta 2, including iOS 8.3 SDK with Swift 1.2 which was released Feb 23, 2015.

Functionality

On the iOS side, we built a simple To-Do list type app that allows you to create different activities and add steps to each activity. The idea is that you’d create activities using your iOS device beforehand and when you’re ready you view the steps of the activity on your Apple Watch. For example you could use it to plan your workouts or even save your favorite recipes. When ready to do your workout or prepare your favorite dish, you use your Apple Watch to select an existing activity. Once started the watch displays one step at a time until you’ve completed the entire activity.

Before getting into any code, here’s a preview of what the resulting functionality looks like.

You can use the iOS app to create the different activities and steps.

And when ready to perform an activity, you select it on the watch which presents each step as you complete them.

Now that we know what it does, let’s dive into the code.

Project Structure

The project is broken down into three main parts - the code for the iOS app, the code for the WatchKit app, and the data models that are shared between them. When you add a WatchKit App target to an existing iOS project it automatically adds a folder for your WatchKit app code - ActivityBuilder WatchKit Extension - and a second folder containing the storyboard for the watch - ActivityBuilder WatchKit App. Despite being located in the same Xcode project, the iOS and WatchKit apps are separate entities.

Sharing Data

If you want to share data between your iOS and WatchKit apps you have to be a member of the iOS Developer Program and use it to create an App Group. You can create a new App Group using your iOS Developer Program login.

Once created you need to configure the App Group capability for your iOS and WatchKit projects inside Xcode.



We used Core Data to model the activities and steps, but in order to make this data available to both the iOS and WatchKit apps we had to create these models outside the scope of the iOS and WatchKit code. To do this we added a new Framework target to our project - WatchCoreDataProxy.

File > New > Target… > iOS > Framework & Library > Cocoa Touch Framework > Next > give it a Product Name > Finish

Configuring both apps as part of the same App Group gives them the ability to share data - but for sharing to work they need an actual data model. If you look in the WatchCoreDataProxy code you’ll see a file called Model.xcdatamodeld that defines the Activity and Step data models and the relationships between them.



These models correspond to the Activity.swift and Step.swift classes contained in the model package. As you can see an Activity contains multiple steps, but a Step can only be part of a single Activity.

	public class Step: NSManagedObject {
	    @NSManaged public var name: String
	    @NSManaged public var detail: String
	    @NSManaged public var status: String
	    @NSManaged public var activity: Activity
	    @NSManaged public var number: Int16
	}

	public class Activity: NSManagedObject {
	    @NSManaged public var name: String
	    @NSManaged public var category: String
	    @NSManaged public var detail: String
	    @NSManaged public var steps:NSSet?
	}

After creating the models we configured Core Data to save its data in a way that the shared App Group can access it. The important pieces are to connect your managed object model to the App Group and point your store to the shared App Group container (see the full configuration in WatchCoreDataProxy.swift).

  
let sharedAppGroup:String = "group.com.makeandbuild.activitybuilder"

public lazy var managedObjectModel: NSManagedObjectModel = {
      let proxyBundle = NSBundle(identifier: "com.makeandbuild.WatchCoreDataProxy")
    
      // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
      let modelURL = proxyBundle?.URLForResource("Model", withExtension: "momd")!

      return NSManagedObjectModel(contentsOfURL: modelURL!)!
  }()

  public lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
      // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.

      var error: NSError? = nil

      var sharedContainerURL: NSURL? = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier(self.sharedAppGroup)
      if let sharedContainerURL = sharedContainerURL {
          let storeURL = sharedContainerURL.URLByAppendingPathComponent("Model.sqlite")
          var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
          if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error) == nil {
              // error handling goes here
              abort()
          }
          return coordinator
      }
      return nil
  }()

We also added public data manager classes to the WatchCoreDataProxy that provide CRUD methods for accessing the Core Data store - DataManager, ActivityManager, and StepManager. These managers are used by both the iOS and WatchKit code to handle all data access.

public class DataManager: NSObject {
    public class func getContext() -> NSManagedObjectContext {
        return WatchCoreDataProxy.sharedInstance.managedObjectContext!
    }
    
    public class func deleteManagedObject(object:NSManagedObject) {
        getContext().deleteObject(object)
        saveManagedContext()
    }
    
    public class func saveManagedContext() {
        var error : NSError? = nil
        if !getContext().save(&error) {
            NSLog("Unresolved error saving context \(error), \(error!.userInfo)")
            abort()
        }
    }
}

Once your project is configured for data sharing you are free to design your iOS/WatchKit app any way you choose. We’re not going to go into details on how we built the rest of the app, but feel free to browse the source code listed below. Hopefully sharing this code will save you from some of the coding headaches we encountered while trying to get our very first app up and running.

Source Code

WatchKit Activity Builder - https://github.com/makeandbuild/watchkit-activity-builder