We are going to dig deeper into developing apps for Apple Watch, such as the architecture of a WatchKit app and what you should expect before starting development.
Recall in my previous post that all WatchKit applications must be bundled with an existing iPhone app and must be in range of the user’s iPhone in order to function. This is not simply an arbitrary requirement, but a necessity in Apple’s first iteration of the WatchKit framework. Each WatchKit app has two parts: a WatchKit extension that runs on the user’s iPhone and a set of user interface resources that are installed on the user’s Apple Watch.
Yes, you read that right, “a WatchKit extension”. In this first iteration, third-party developers will not be able to create native Apple Watch apps, but instead, the WatchKit framework will be leveraging the extensions functionality that Apple created with the release of iOS 8. In layman’s terms, the extension on the iPhone contains (and runs) the code and data-driven resource files while the Watch contains the app’s storyboard file and cached images. Because the code is run on the iPhone, a WatchKit app that becomes disconnected from the paired iPhone will no longer function.
While it might seem daunting to manage functionality between the iPhone and Apple Watch, Apple has made the experience completely seamless and transparent. The information is transferred between the devices using a combination of Bluetooth and Wi-Fi depending on signal strength and the needed transfer speeds.
Since this is essentially an extension app, any API restrictions that apply to other extension types (i.e. Today or Share) will also apply to WatchKit extensions. Sharing data with the containing iOS app is also identical to other extension types by enabling shared app groups in project settings. Also, WatchKit supports Handoff if the user wishes to start a task on their Watch and complete the task on other devices (such as a MacBook or the containing iOS app).
Perhaps the biggest shock to an experienced iOS developer will be the absence of one of the most important frameworks for every iOS app – UIKit. With WatchKit, Apple has decided to create completely new objects to replace beloved classes such as UIViewController, UITableView, and UIButton. Instead you will be working with the likes of WKInterfaceController, WKInterfaceTable, and WKInterfaceButton.
Although each of these new interface objects will feel familiar to iOS developers, their API footprints are significantly smaller than their UIKit counterparts. For example, UIButton has nearly thirty configurable properties to modify its appearance at runtime while WKInterfaceButton only has six. In many ways this limitation is liberating as apps will not be able to abuse the design guidelines that Apple has created, and every WatchKit app can feel familiar, yet unique enough to stand out from the crowd.
The absence of UIKit in WatchKit also means developers will not have to work with another major system in iOS development – Auto Layout. Developers rejoice!
In WatchKit, the layout of interface objects is handled automatically by the system. Since the Watch screen is so small, most interface objects will simply be laid out from top-to-bottom. If a design calls for side-by-side elements, they must be added as children to a “Group” which can lay out elements from left-to-right.
So far I’ve been talking exclusively about storyboards when breaking down how to create your interfaces. In WatchKit, developers are required to use storyboard (no Nibs) and must create all of their interfaces at design-time. In fact, the only way to radically change your views at runtime is by hiding and showing interface objects or by using a WKInterfaceTable to swap out different types of rows.
Thankfully, the world is still round and WatchKit, like iOS, conforms to the MVC pattern. Each scene in the WatchKit app’s storyboard is managed by an instance of WKInterfaceController, a simpler counterpart to UIViewController. Only one controller may be presented onscreen at a time and they may be presented modally in addition to the app’s chosen navigation style (hierarchical or page-based).
Never perform long-running tasks in these controllers as iOS will deactivate the active controller and suspend the extension as soon as the user stops interacting with Apple Watch. Therefore, any network calls or complex tasks should instead be run in the background of the containing iOS app. WatchKit apps can delegate these tasks by using the openParentApplication:reply: method to send requests to the containing app and receive a response. This method will wake up the containing iOS app in the background and call the application:handleWatchKitExtensionRequest:reply: method of its app delegate.
The life cycle of a WKInterfaceController is very streamlined, but I wanted to take this opportunity to dig into the purpose of each:
- init – Provides the first opportunity to initialize the controller.
- awakeWithContext: – Allows developers to configure the controller with any available context data. Context data is a standardized way to pass data between controllers in WatchKit. For example, when pushing a new controller in a hierarchical app, pushControllerWithName:context: can be called to pass data to the new controller.
- willActivate – Called just before the interface will be visible and should be used to only make small changes.
- didDeactivate – Used to clean up the interface and prepare it for the background state. Interface objects may not be modified from this method.
UITableView is on of the most flexible and customizable classes that Apple offers developers for their iOS apps. Often the crux of some of the most popular apps, it is not surprising to find a WKInterface counterpart in the WatchKit framework. WKInterfaceTable can display any number of rows (cells) that may each have their own row type and interface. Sounds familiar, right? What might be surprising is how much simpler it is than its predecessor.
In UIKit, it is extremely common practice to find objects with data source and delegate properties which allow the view controller to populate data and handle interactions. In WatchKit, the role of the controller does not include managing views of your interface so data source and delegate properties no longer make sense. Instead, views mostly manage themselves, and the controllers only respond to actions such as button taps.
So where does this leave WKInterfaceTable? In my opinion, a bit of a weird place. Because tables tend to be dynamic, the controller still needs to tell the table what dynamic data to display. But Apple has removed the most convenient way to manage tables. So with WatchKit, developers have to be a little bit more explicit in setting up tables:
- Create an outlet for your WKInterfaceTable in your controller
- Use the setRowTypes: or setNumberOfRows:withRowType: method to create the rows
- Iterate over these newly created rows using the rowControllerAtIndex: method to configure the row contents
Ready to start programming for WatchKit? I highly recommend you check out Apple’s fantastic programming guides which has detailed descriptions on all the interface objects, architecture, and best practices.