• WatchKit Tutorial with Swift

    26 March 2016

    WatchKit is Apple’s new framework and related technologies that allow you to create apps for the Apple Watch, released along with Xcode 6.2.

    In this WatchKit tutorial, you’ll create your first WatchKit app with Swift. Specifically, you’ll take a Bitcoin price tracking app, and make a Watch app that goes along with it.

    In the process, you’ll learn learn about how WatchKit apps are architected, how some of the new WatchKit-specific UI controls work, WatchKit layout, and much more.

    Let’s get started! ┗(°0°)┛

    Getting Started

    First, download the starter project for this WatchKit tutorial.

    Open the starter project and select the iPhone 6 simulator target. Build and run the app to get a feel for it. The app (by team member Mic Pringle) contacts the BitcoinAverage Price Index API to get the latest Bitcoin price and displays it on the screen.

    Are you a Bitcoin billionaire yet?

    Are you a Bitcoin billionaire yet?

    It’s time to get started with tracking your Bitcoin fortune on the Watch!

    In Xcode, navigate to File\New\Target… and select the iOS\Apple Watch\Watch App template.


    A Watch app is bundled with the main iOS app in the same way extensions work, so this option will create a separate target for you. Click Next to continue.

    On the following screen, Xcode will fill in many of the values and you won’t be able to change things such as the Product Name.


    Make sure the language is set to Swift and both Include Notification Scene and Include Glance Sceneare not checked. Click Finish and Xcode will set up the target and some starter template files for the Watch interface, ready for you to customize!

    If you look at the project navigator, you’ll see there are two separate groups:


    1. The Watch App group contains just the storyboard and image assets…and no code! Think of this as the “view” of your app.
    2. The WatchKit Extension contains the code that is executed upon events like the app launching, button taps, or a switch value change. Think of this as the “controller and model” of your app.


    Until we get “native” Watch apps, the best way to think of this setup is the Watch as a second smaller screen controlled from the extension. In fact, the simulator treats the Watch as just another external display, as you’ll see as you start to add and test the interface objects. ;]

    Note: The terminology for Watch apps is a little different from iOS and Mac apps – rather than views, controls and view controllers, you have interfaces, interface objects and interface controllers.

    Watch Interface

    When designing your Watch app interface, you use storyboards and Interface Builder just as you would for an iOS app. To see this, open the BitWatch Watch App group and select Interface.storyboard.


    You’ll see a single empty Interface Controller in the storyboard. Let’s add some controls to it!

    Start by dragging a Label from the object library to the interface. Then, drag a Button underneath it.


    You might notice something strange here – you can drag the interface elements around on the storyboard but that only changes their position vertically (not horizontally), and locks the controls vertically one after another.

    In other words, you can put the label on top of the button or the button on top of the label, but you can’t drag things around “freehand” as you can on iOS. Those of you used to Auto Layout in iOS might feel like this guy:


    It turns out Auto Layout is no more on the Apple Watch – it uses its own system for layout and positioning. Let’s take a look.


    It’s not quite Auto Layout, but every interface element in your Watch app has to be “pinned” to something, whether that’s a screen edge or another object. This will determine its final position.

    By default, your two objects are positioned at the top; you can change their order to have one on top of the other, but their position will be relative to the top of the screen.

    You can tweak the positioning of your interface elements from the Attributes inspector in the Utilities panel. Select the label and change its Horizontal position to Center, and leave its Vertical position as Top.


    Next, select the button and change its Horizontal position to Center, and its Vertical position as Bottom.


    In Auto Layout terms, you can think of this as pinning items to the top and bottom layout guides. Since the watch screen is so much smaller, it’s as easy as setting two position choices – horizontal and vertical. You’ll see how easy it is to get objects to position themselves relative to each other as you add more things to the interface later.


    First, let’s set some text and formatting to get the interface looking good and explore some of the other options in the Attributes inspector.

    Select the label and make sure the Attributes inspector in the Utilities is open. This label will show the Bitcoin price, so set the text to $0.00 and select center alignment. Open the font pop-up by clicking the “T” icon in the Font field, and set the values to System – System, Semibold, Size 30.


    Select the button and change its title to Refresh. Change its font values to System – System, Medium, Size 16.


    That looks good!


    Note that you can change things like text and titles and colors from code at runtime, but you can only access font and positioning properties here in the storyboard. So you could be spending a little more time in Interface Builder for your Watch apps compared to iOS apps if you’re used to setting up your layouts in code.

    Actions and Outlets

    Actions and outlets work just as you would expect, allowing you to access interface elements from code and to handle user actions such as button presses.

    If you think too hard, it seems strange to connect these things since the storyboard is in the Watch App target and the code is in the WatchKit Extension target.

    How can the actions and outlets connect across targets? Or across devices for that matter, since the app is on the Watch and the extension is on the phone!

    Luckily, this is the magic part that Apple handles behind the scenes so you don’t have to worry about it.

    Remote actions and outlets? Don't give it another thought!

    Remote actions and outlets? Bluetooth magic!

    Behind the scenes, Apple takes care of all the wireless communication between your WatchKit app and your iPhone’s WatchKit extension behind the scenes, using Bluetooth. Pretty cool, eh?

    Open the Assistant editor and make sure InterfaceController.swift is showing up there. Control-drag from the price label to inside the InterfaceController class definition to create an outlet, call itpriceLabel, and click Connect.


    Next, control-drag from the button to the class. This time, make sure to select Action to create a method rather than an outlet. Call the action method refreshTapped, and click Connect.

    That’s it for the interface so far. It’s time to switch to the code and get some data showing up!

    Basic WatchKit Code

    The starter project includes a framework called BitWatchKit that contains some custom code to fetch the Bitcoin price. We put this code in a framework so it can be easily used in both the iPhone app, and the iPhone WatchKit extension.

    To add this framework to your extension, open the project file in the navigator and select the BitWatch WatchKit Extension target. Select the General tab and then scroll down to the Linked Frameworks and Libraries section.


    Press the + button under the list of frameworks. You’ll see a window listing the available frameworks to add, and you should see BitWatchKit.framework right at the top of the list. Select it and click Add.


    Now that the framework is there, you can update your Watch interface with some real data!

    Switch over to InterfaceController.swift in the WatchKit Extension group. Add the following import statement to the top of the file:

    import BitWatchKit

    This will allow you to access the Tracker class defined in the framework.

    Next, add the following properties to the class definition:

    let tracker = Tracker()
    var updating = false

    You’ll need an instance of Tracker to access the Bitcoin values over the network. You’ll use updating to keep track of whether there’s a pending request to update the price.

    Add the following helper method to the class:

    private func updatePrice(price: NSNumber) {

    This method takes an NSNumber and updates the label on the Watch. Tracker also includes a handy number formatter that will take a number like 93.1 and turn that into a string like “$93.10″.

    Add one more helper method to the class:

    private func update() {
      // 1
      if !updating {
        updating = true
        // 2
        let originalPrice = tracker.cachedPrice()
        // 3
        tracker.requestPrice { (price, error) -> () in
          // 4
          if error == nil {
          self.updating = false

    Let’s review this step by step:

    1. Runs a quick check to make sure you’re not updating already.
    2. Caches the current price so you can only update the UI if the price changes.
    3. requestPrice() is a method on the Tracker class that gets the latest Bitcoin price via a network request. Once it completes, it executes a closure.
    4. If the request was successful, all you need to do is call updatePrice() to update the label.

    Now you just need to call these methods from somewhere to get the real data to appear.

    Interface Lifecycle

    Your first chance to set up your interface elements is in awakeWithContext(_:). By this time, the entire interface is loaded and the actions and outlets are wired up.

    That means you can set up the interface objects and start displaying data. However, the interface isn’t necessarily going to be on the Watch just yet! So it’s important to get things to a reasonable state, but you might not want to perform expensive operation such as making network calls.

    Add the following to the end of awakeWithContext:


    This will update the price label with the previously cached value, if one exists. Since this value is cached by the tracker, it’s local data and inexpensive to fetch.

    Add the following to the end of willActivate():


    willActivate is like viewWillAppear in iOS, and means the interface is about to activate and appear on the Watch. This is a good signal to refresh the data, so calling update() here will kick off the network request and update the label.

    Finally, add the same line of code to refreshTapped:


    When the user taps Refresh, you want the same thing to happen: call update() and display some fresh data.

    Testing Your App

    To test your Watch app, you’ll need to enable the Watch as an external display. If you have the iOS Simulator app running, switch to it. Otherwise, build and run the app, stop it in Xcode, and then switch back to the iOS Simulator. In the menu, navigate to Hardware\External Displays and select one of the Apple Watch options.


    You should have two simulator windows now – one showing the iPhone, and one for the watch.

    Back in Xcode, select the BitWatch Watch App scheme from the toolbar, and select the iPhone 6Simulator.


    Build and run and then switch to the simulator. You should see the Bitcoin data on the Apple Watch!


    More Interface

    Now that you have the basics in place, it’s time to look at some more things you can do with the interface.

    There are two more elements from the app you’ll implement on the Watch too:

    • The last update time
    • An up / down image to show whether the price has increased or decreased

    To do this, you’ll first need to add the images to the asset catalog. Open Images.xcassets in the BitWatch Watch App group. Be careful here: there are three Images.xcassets files in the project so make sure you’re in the correct group!

    The starter project includes two image files: Down@2x.png and Up@2x.png. From a finder window, drag those two files to the list of image sets.


    Note that only the @2x variant of the image is included. On WatchKit you only need @2x images – there are no non-Retina Apple Watches!

    Next, open Interface.storyboard again to return to the layout.

    Drag another label to the interface and place it above the Refresh button. Set the text to Last Updatedand the font to text style Subhead. Also change the text alignment to center.

    For the position settings, change the Horizontal position to Center and the Vertical position to Bottom.


    You might be wondering: if both the Refresh button and this new label have their position set to Bottom, how does the Watch know what order to place them in?

    On iOS, the view order in the document outline determines the z-order, or which views are “on top” or “behind” other views. For Watch interfaces, interface elements do not overlap so there’s no need to keep track of z-order. Instead, the order in the document outline reflects the vertical top-to-bottom order (in combination with the vertical alignment settings).


    To see what I mean, swap the order of the Refresh and Last Updated controls in the outline – you’ll see them swap visually in interface builder as well. Swap them back when you’re done.


    So far, your Watch interface consists of interface object stacked on top of each other. But what about side-by-side objects?

    Another object available for your use are groups. Groups are like containers, and you can put interface objects inside them. However, they also have a setting for whether they should lay out their items horizontally or vertically. You can have groups inside groups with different layouts to achieve all kinds of arrangements!

    Note: Groups can be interface elements too, with background colors and images. For this app, they’ll be invisible to the user – except for how they contribute to the layout, of course!

    Here’s what the price increase/decrease indicator looks like in the iPhone app:


    So the overall plan is to have an image next to a label.

    Drag a new group from the object library to the storyboard, and place it between the price label and the last updated label. In the Attributes inspector, make sure the group’s Layout setting is set to Horizontal.


    Next, drag an image from the object library and place it inside the group. Then drag in a new label to the group and place it to the right of the image.


    You have your two objects side-by-side, but now it’s a matter of setting their position and size while they’re inside a group.

    Position and Size

    Select the label and change both its Horizontal and Vertical positions to Center. Do the same for the image.

    Since the image and label are inside a group, they are centered within the group and not within the outside world of the storyboard as a whole. That means as the group moves around and if it were resized for some reason, the image and label would stay right in the center of the group.

    By default, most interface objects have their size set to Size to Fit Content. This works well for labels, since the text may change. However, the arrow images are right in the bundle and have a known size so you can set the size in the storyboard. That means the Watch won’t have to calculate the size on the fly later.

    Select the image and find the Size section in the Attributes inspector. Change the Width setting to Fixed Width and the Height setting to Fixed Height. Set both the width and height values to 32.


    Wrapping Up the Interface

    The label’s text won’t change so you only need to set it up once in the storyboard. Change its text to 1 BTC.

    The image will change depending on the previous Bitcoin value so you’ll need an outlet. Open the assistant editor and control-drag from the image to the class definition. Call the outlet image, and clickConnect.

    You’ll need one more outlet for the “Last Updated” label. Control-drag from the label to the class definition and call the outlet lastUpdatedLabel, and click Connect.

    That’s it for the interface – there’s a little more code and some tricks about updating the layout from code before you start tracking your Bitcoin-funded retirement!

    Wrapping Up the Code

    Open InterfaceController.swift. There are two new things to deal with: the “last updated” label and the image.

    Add the following helper method to the class:

    private func updateDate(date: NSDate) {
      self.lastUpdatedLabel.setText("Last updated <span>\(Tracker.dateFormatter.stringFromDate(date))"</span>)

    This method updates the last updated label similar to how the price label method does its job. Again, theTracker has a formatter object that in this case, will format the date to just show the time.

    Add one more helper method to the class:

    private func updateImage(originalPrice: NSNumber, newPrice: NSNumber) {
      if originalPrice.isEqualToNumber(newPrice) {
        // 1
      } else {
        // 2
        if newPrice.doubleValue > originalPrice.doubleValue {
        } else {

    This method will compare the original price to the new price.

    1. If the two prices are the same, hide the arrow image.
    2. If the prices are different, set the image to either “Up” or “Down” depending on the direction the price changed. Unhide the image to make it visible again.

    Calling setHidden will cause the layout to reflow. Remember how both the image and the “1 BTC” label are centered inside the group? When you hide the image, that will leave extra space to the left of the label. However, the interface is smart enough to then recalculate the layout and move the label over so it remains centered inside the group.

    Note: You can hide things and still have them “take up space” by setting an interface object’s alpha to 0 with setAlpha.

    Now that the helper methods are in place, you need to call them. Add the following lines to the end ofawakeWithContext:


    Since you don’t have any previous price information on launch, you start out with a hidden image. The price is coming from the cache, so you can grab the date from the cache too.

    Next, find update() and add the following lines inside the innermost if block with the if error == nil {condition:

    self.updateImage(originalPrice, newPrice: price!)

    This will update the last updated label to show the current time. By this point, you have both the previous price and the new price so you can use updateImage to show the up or down arrow image if the price has changed.

    Build and run the Watch app and have a look at the simulator.


    You should see the current price displayed as before. Since you ran the app once already there should be a cached price and if it’s different, you should see an up or down arrow too.

    The source data is updated every minute, so you can wait a minute and tap the Refresh button for the latest Bitcoin price. Can you afford that solid gold Apple Watch Edition yet? ;]

    Final project source code is available here http://devcodemarket.com/bit-watch-app


Comments closed on this post.