In this tutorial I will show how to create a simple weather app for an Apple Watch running WatchOS2 (or 3). This tutorial will show the following:
- Add Apple Watch support to an existing app
- Set up the Apple Watch interface to show data
- Communicate with the main app to get weather information from a third-party web service.
The basis for this tutorial is an IOS app which shows free weather data from the Norwegian Meteorological Service, yr.no. It will show the weather for your current location and allow you to search for locations worldwide. The code for this project can be found on github. This app uses data from three external services to get location- and weather data.
- SSR (Norwegian placename database), used for location search in Norway
- geonames.org, free worldwide placename database for location search in the rest of the world
- yr.no to obtain weather data worldwide.
Note that the call to geonames.org uses the "demo" user, which may fail due to usage limitations. To avoid this, create a new free account on geonames.org, login to your account and enable web services, and update the value for "geonames username" in Info.plist.
Start by cloning the project from github, opening it in Xcode then build and run. You will get a screenshot like the following: This app will show the weather for your current location (provided you give it access) and allow you to search for locations worldwide and show the weather.
Add WatchKit target
First step is adding the WatchKit app target. Go to
File -> New -> Target... and choose
watchOS -> Application -> WatchKit App. See screenshot below. Set Product Name to "Watch" and click Finish. Leave the rest of the options unchanged. When asked to active the new Watch scheme, click "Activate" This has now given you two new groups in the project, Watch and Watch Extension. Watch contains the user interface storyboard and image assets, Watch Extension contains the code to be run on the Apple Watch. Note that if you build the project now it will fail due to a naming conflict in the image Assets. You now have two
Assets.xcassets, both containing an
AppIcon. To fix this, change the name of the app icon inside the watch group to WatchAppIcon and change the App Icons Source for the watch target in the project view.
Create user interface
First step in creating this app is to build the user interface. Open the file
Interface.storyboard. Here you'll see three items. Ignore the two bottom ones, they are for showing notifications. Instead we'll focus on Interface Controller, which contains the user interface for the initial screen. The watch app will allow you to either see the weather for your current location or allow you to select weather from one of your saved locations. Therefore, start by dragging two buttons onto the Interface Controller. Name the first one "Current position" and the second one "Saved positions" Once this is done you can try running your app. Choose the Scheme 'watch' and run on the simulator. Once xcode has built the project the watch simulator will start and launch the app. Next step is to implement the interface controller to show weather for a location. Open the storyboard and drag a new Interface Controller onto the board. First, drag a label to the interface. Set width to
Relative to Container, and height to
Size To Fit Content. Center align the text and replace it with the text "Bergen". In the sidebar, change the name of the view to
Placename Next, drag an image onto the screen. Increase the size to almost fill the rest of the screen, leaving the same amount of space as that used by the
Placename label above. Set width to
Relative to Container. Change the name of this view to WeatherSymbol. This Image will show a symbol representing the current weather. These images are stored in the
Assets.xcassets folder for the main app and need to be added to the Watch target. Open WeatherWatch and select
Assets.xcassets, in Target Membership, add "watch" Go back to the watch storyboard and select the image. Write
01d in Image, set Mode to
Aspect Fit and set alignment to
Center (both horizontal and vertical. Next, add a Group to the interface. This will initially place itself above the image. Set Horizontal alignment to center and vertical alignment to bottom and it will move to the bottom, below the image. Set width to
Relative to Container and Height to
size to fit content. Next, drag a button to the group. Set horizontal alignment to left and change the text to "20 °". Set the name of the view to Temperature. Finally drag another button to the group, set horizontal alignment to right and change text to "0 mm". Change the name of this button to Precipitation. Finally, we need a segue to launch this Interface. Ctrl-drag from the button "Current position" to the interface, choose push segue and set the identifier to "Show Weather"
Next, we need to create the interface showing our saved locations. Drag another interface controller onto the storyboard and change the name to "Locations Controller". Add a Table to the interface. You will see that you get a
Table Row Controller containing a group. To reference this table row later it needs an identifier. Select the table row controller, go to
Attributes inspector and set the identifier to LocationRow, and make Selectable is checked. For this table we only need a name, so drag a Label onto the group. Set Width and Height to
Relative to Container. Set text on the label to Bergen and set text alignment to
Center. Change the name of the label to Name. Finally we need to set up segues to show this interface and to show the weather for each location. Ctrl-drag from the button Saved Positions to the new interface and choose Push, set the identifier to "Saved Locations". Ctrl-drag from the Table Row Controller to Weather Controller, choose Push again and set the identifier to "Show Weather" The storyboard should now look like this.
Create interface controller classes
After creating the interfaces we need to create the controllers. This will be part of the watch extension target. First, we'll create the class for Weather Controller. Choose
File -> New -> File... and
watchOS -> Source -> WatchKit Class Name the class
WeatherController, click next, change Group and targets to "watch Extension" and click Create. Go back to the storyboard, choose the new interface and click on Show the
Identity Inspector. Change the class to
WeatherController. Change to the Assistent Editor and choose the new file (
WeatherController.swift). Ctrl-drag from the top label to the class and create a new outlet, calling it
placename. Do the same for the rest of the views, calling them
precipitation, respectively, giving you the following outlets: Next, we'll create the class for the Location list. Again, choose
File -> New -> File... and
watchOS -> Source -> WatchKit Class. Name the new class
LocationsController and set group and target to watch_extension. Go to the storyboard, choose the
Locations Controller, go to
Identity Inspector and set classname to
LocationsController Go to the
Assistant Inspector, choose the new file (
LocationsController.swift) and ctrl-drag from the table to create a new outlet. Name this outlet
locationsTable If you start the project now you will notice that the
WeatherController works and shows the data we've set in the storyboard. However,
LocationsController will just show an empty screen. To show information here we need to setup the table in code. Open
LocationsController.swift. Later we'll retrieve the saved locations list from the iphone app, but for now we'll use some hardcoded values. Add the following array to the class: [crayon-590cd5bc63b72582493636/] Unlike in normal IOS apps, in watchkit you need to setup the table manually. This requires four steps:
- Define one or more row controller types in your storyboard. (Already done)
- Define a custom data class to manage the contents of each row type.
- Tell the table object how many rows (and of what type) to display at runtime.
- Use instances of your custom data class to configure each row’s contents.
For more details, see the documentation for WKInterfaceTable.
1. Define row controller
We already did this when setting up the storyboard above.
2. Define custom data class
We need a class containing outlets for the views in the table row. Choose File -> New -> File... and watchOS -> Source -> Swift File. Set the filename to LocationRow and set group and targets to watchExtension Add the following code to this file: [crayon-590cd5bc63b80332990941/] This creates a new class LocationRow and adds an outlet variable for the name label in the table row. Next we need to connect the outlet. Open the storyboard and choose the table row (
LocationRow), go to the
Identity Inspector and set Class to
LocationRow. Ctrl-drag from
LocationRow to the Name label, and choose the outlet name.
3. Tell the table object how many rows (and of what type) to display at runtime.
Next, you need to declare how many rows the table should contain. You do this with the following function call: [crayon-590cd5bc63b86243329015/]
4. Use instances of your custom data class to configure each row’s contents.
Finally you need to create a controller instance per row and set the correct value for each label. [crayon-590cd5bc63b89552653436/] Here we iterate over each element in locations, instantiate a new row controller and cast it to our custom class, and finally set the name outlet. The full awakeWithContext function for LocationsController should look like this: [crayon-590cd5bc63b8c239355900/] Now you can build the project and run it on your watch. The interface and navigation is completed but all the data is hardcoded. To show actual data requires that we connect the watch app to the main IOS app, which will be covered in part 2. If you want to see the final code, checkout branch tutorial_part1 of the git repository.