watchOS 2 Tutorial Part 1: Getting Started


Note: This is a brand new tutorial released as part of theiOS 9 Feast. Enjoy!

This year at WWDC, Apple introduced watchOS 2, which signifies a huge change for Apple Watch developers. Now, you can make native apps that run directly on your watch.

In this watchOS 2 Tutorial, you’ll build a simple but fully functional watchOS 2 app. Specifically, you will work on a watchOS app for a fictional airline called Air Aber.

In the process, you’ll learn:

How to add a watchOS 2 target to an iOS app; How to share data across the two targets; How to add a watchOS 2 interface controller to the Storyboard, and lay out the interface objects; and How to create the WKInterfaceController subclass and wire everything up.

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

Getting Started

Start by downloading thestarter project for this tutorial.

Open it in Xcode and build and run. You should see a blank white screen:

There’s not much to this project as it stands; it just includes a few helper files you’ll need, and not much else. You’ll address that now!

Adding the WatchKit App

Select File/New/Target… , and in the dialog that appears choose watchOS/Application/WatchKit App and click Next :

In the following screen, set Product Name to Watch , make sure Language is set to Swift , and uncheck any checkboxes that are checked. Click Finish :

You’ll be asked if you want to activate the watch scheme, which you do, so make sure to choose Activate :

Congratulations, you’ve just created your first watch app! It really is that easy.

You’ll notice that this action actually created two targets, not one, and two corresponding groups in the Project Navigator. This is because the code of a watch app actually runs as an extension bundled within the watch app, in much the same way Today extensions on iOS work.

If you expand the Watch and Watch Extension groups in the Project Navigator you’ll see that the storyboard resides in the Watch group, and the classes created by the target template reside in the Watch Extension group:

This is the pattern you’ll follow moving forward. Any code you add must reside within the Watch Extension group and be added to the Watch Extension target, whereas any assets or storyboards need to go in the Watch group.

A Little Housekeeping

Before continuing, you need to remove a couple of things added by the target template that you don’t need.

Right-click on InterfaceController.swift in the Project Navigator and choose Delete . When prompted, choose Move to Trash to make sure the file is actually removed from the project:

Next, open up Interface.storyboard , select the only interface controller that’s in there, and hit backspace to delete it. This should leave you with an empty storyboard, or as I prefer to think of it, a blank canvas.

Sharing Data and Code

The starter project includes a JSON file containing all the Air Aber flight details, and a model class that represents that data. This is exactly the kind of thing that should be shared amongst targets, since it’s highly likely the iOS app and the watch app will use the same model class and data – you do remember DRY , right?

Expand the Shared group in the Project Navigator and select Flights.json . Then, find the Target Membership section in the File Inspector , and check Watch Extension :

The file should now be included in both the AirAber and Watch Extension targets.

Repeat the process for the other file in the Shared group, Flight.swift .

And with that done you can finally begin building the flight details interface!

Building the Interface

Open Watch/Interface.storyboard , and drag an Interface Controller from the Object Library onto the storyboard canvas. With the interface controller selected, open the Attributes Inspector and set Identifier to Flight and check Is Initial Controller :

You set the identifier so you can refer to the interface controller in code. Checking Is Initial Controller simply informs WatchKit that this is the interface controller you want to display when the watch app first launches.

Next, drag a Group from the Object Library onto the interface controller:

Although it doesn’t look much now, this group will eventually contain the Air Aber logo, flight number, and route.

With the new group selected, head over to the Attributes Inspector and change Insets to Custom . This will reveal four extra text boxes where you can manually set the insets for the top, bottom, left, and right of the group. Change Top to 6 :

This just gives the layout group a little extra padding at the top.

Next, drag an Image into the group. If your group shrank in response to changing the Top inset ( thanks Xcode! ), then drag the image into the Document Outline instead, making sure it’s a child of the group, rather than a sibling:

Now you need an image to display. Downloadthis logo image and drag it into your Watch/Assets.xcassets . This should create a new image set called Logo, with the actual image in the 2x slot:

You want to tint this image, so select the image and then in the Attributes Inspector change Render As to Template Image .

Re-open Watch/Interface.storyboard and select the image. Using the Attributes Inspector, make the following changes:

Set Image to Logo – if it doesn’t appear in the dropdown, you can simply type it; Set Tint to #FA114F (you can type this in the Color Sliders panel); Set Width to Fixed , with a value of 40 ; Set Height to Fixed , with a value of 40 .

The Attributes Inspector should now look like the following:

Don’t worry if you can’t see the logo, as it turns out that Xcode doesn’t tint template images at design time!

Next, drag another group into the existing group, making sure it appears to the right of the image, and set its Layout to Vertical using the Attributes Inspector. Also change Spacing to 0 and Width to Size to Fit Content . Then drag two labels into the new group, positioning one underneath the other:

Select the upper label and using the Attributes Inspector, set Text to Flight 123 and Text Color to #FA114F .

Then select the lower label and set its Text to MAN to SFO . Your interface controller should now look like the following:

This text is simply placeholder text that’ll be replaced when you hook the interface up to its controller class.

Next, drag another group onto the interface controller, but this time make sure its a sibling of the very first group you added. If you can’t get the group positioned at the correct place in the hierarchy then use the Document Outline instead.

With this new group selected, set its Layout to Vertical and Spacing to 0 .

Now, drag three labels into this new group:

Make sure the labels are inside the group, not siblings of the group!

Select the top label and use the Attributes Inspector to change its Text to AA123 Boards .

With the middle label selected, change its Text to 15:06 . Also change Text Color to #FA114F , and Font to System , with a style of Regular and a size of 54.0 . Finally, change Height to Fixed , with a value of 44 .

Select the bottom label and change its Text to On time and Text Color to #04DE71 .

Your interface controller should now look like the following:

You’ve now just one more group to add before you can create the outlets and have this interface display some real data.

Drag a new group from the Object Library into the lower group, this time making sure it’s a child rather than a sibling, and that it’s positioned at the very bottom of the containing group. Then add two labels to it. Your complete interface object hierarchy should now look like this:

Using the Attributes Inspector, set Text to Gate 1A for the left label. For the right label, set Text to Seat 64A and set the Horizontal alignment to Right .

The completed interface should now look like the following:

Congratulations, you’ve finished laying out your very first watch app interface. Now it’s time to populate it with some real data and get it up and running in the simulator.

Creating the Controller

Right-click on the Watch Extension group in the Project Navigator and choose New File… . In the dialog that appears select watchOS/Source/WatchKit Class and click Next . Name the new class FlightInterfaceController , and make sure it’s subclassing WKInterfaceController and that Language is set to Swift :

Click Next , and then Create .

When the new file opens in the code editor, delete the three empty method stubs so you’re left with just the import statements and the class definition.

Add the following outlets to the top of FlightInterfaceController :

@IBOutlet var flightLabel: [email protected] var routeLabel: [email protected] var boardingLabel: [email protected] var boardTimeLabel: [email protected] var statusLabel: [email protected] var gateLabel: [email protected] var seatLabel: WKInterfaceLabel!

Here you’re simply adding an outlet for each of the labels you added earlier. You’ll hook them up in just a moment.

Next, add the following property and property observer just below the outlets:

// 1var flight: Flight? { // 2 didSet { // 3 if let flight = flight { // 4 flightLabel.setText("Flight /(flight.shortNumber)") routeLabel.setText(flight.route) boardingLabel.setText("/(flight.number) Boards") boardTimeLabel.setText(flight.boardsAt) // 5 if flight.onSchedule { statusLabel.setText("On Time") } else { statusLabel.setText("Delayed") statusLabel.setTextColor(UIColor.redColor()) } gateLabel.setText("Gate /(flight.gate)") seatLabel.setText("Seat /(") } }}

Here’s the play-by-play of what’s happening here:

You declare an optional property of type Flight . This class is declared in Flight.swift , which is part of the shared code you added to the Watch Extension target earlier; You add a property observer that is triggered whenever the property is set; You make sure there’s an actual flight rather than nil in the optional property. You only want to proceed with configuring the labels when you know you have a valid instance of Flight ; You configure the labels using the relevant properties of flight ; If the flight is delayed then you change the text colour of the label to red.

Now you need to set flight when the controller is first shown. Add the following just below the declaration of flight :

override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) flight = Flight.allFlights().first!}

Later in the session you’ll change this implementation to use the context that’s passed to it, but for now you simply load all the flights from the shared JSON file and then take the first one from the array.

You’ll also learn more about awakeWithContext(_:) later in the session, but for now just know that it’s called early enough in the interface controller’s lifecycle to make it a great place to set flight .

Now there’s just one final step before you can build and run, and that’s to connect the outlets.

Connecting the Outlets

Open Watch/Interface.storyboard and select the interface controller. Using the Identity Inspector, set Class/Custom Class to FlightInterfaceController .

Next, right-click on the yellow icon at the top of the interface controller to invoke the outlets and actions popup:

Now, connect the outlets as per the list below:

boardingLabel : AA123 Boards boardTimeLabel : 15:06 flightLabel : Flight 123 gateLabel : Gate 1A routeLabel : MAN to SFO seatLabel : Seat 64A statusLabel : On time

Before you hit run, there’s just one more thing to do. The sample app you’re building throughout this tutorial has been designed for the 42mm Apple Watch, so you need to make sure you have the correct watch simulator set up, otherwise some things may look a little off. For a real world app you’d want to make sure your interfaces work equally well across both sizes of watch, but that’s outside the scope of this tutorial.

In Xcode, select Window/Devices to bring up the device manager, and then click the + icon in the lower left corner. In the dialog box that pops up, name the simulator iPhone 6 – 42mm , change Device Type to iPhone 6 , change Paired Apple Watch to Apple Watch – 42mm (watchOS 2.0) and click Create :

Close the device manager, select the Watch scheme, and choose the new simulator:

Build and run. Once the simulator has finished loading you should see the following:

Note: If you receive an error message stating the installation failed, then you can either try again with Xcode, or manually install the app in the watch simulator. To do this, open the Watch app in the iOS simulator, tap on AirAber , and then flick Show App on Apple Watch to On . Once that’s done, jump back to the watch simulator, press Shift+Command+H to navigate to the home screen, and then tap the AirAber icon to launch the app.

Congratulations! You’ve now finished implementing your very first WatchKit interface, and got it up and running in the watch simulator using real data, nice work.

Where To Go From Here?

Here is the finished example project from this tutorial series so far.

In this exercise you’ve learned how to add a watch app to an existing iOS app, how to create an interface controller and lay out a pretty complex interface using nested groups, and how to tie the whole thing together using a WKInterfaceController subclass. So, where to next?

Part 2 of this tutorial series, of course! In part 2, you’ll learn all about tables and navigation in WatchKit.

You might also be interested in our book watchOS 2 by Tutorials that goes into much greater detail about making watchOS 2 apps – from beginning to advanced.

If you have any questions or comments on this tutorial, please join the forum discussion below! :]