Teknologi

I Bouvet er vi flere hundre teknologer som brenner for å programmere og utforme gode, digitale løsninger. I denne bloggen utforsker vi teknologien og deler det vi finner med dere.

Creating a simple Weather App for Apple Watch. Part 2: Watch connectivity

In part 1 of this tutorial I showed how to add Apple Watch support to an IOS app and how to create the user interface. We ended up with a functional Apple Watch app but with hardcoded data. You can either complete this tutorial or clone the repository from github and checkout the branch tutorial_part1.

Now I’ll show how to get actual data from the webservices and present them to the user. This involves using the WatchConnectivity framework to get data from the IOS App.

Setup WatchConnectivity session

The first step is to setup and start the watchConnectivity session. This needs to be done on both the watch and the phone, and should happen as soon as possible. On the phone a good place to execute this is in AppDelegate didFinishLaunchingWithOptions, on the watch it should be in awakeWithContext in the initial Interface Controller.

WatchConnectivity on phone

Let’s start with the phone side. To setup the connection you need the following code:

First we check that WatchConnectivity is supported (this might not be the case, e.g. if you’re running the app on an Ipad) If connectivity is supported, retrieve the singleton session, set the delegate class and call activateSession(). This assumes that self implements the protocol WCSessionDelegate.

After calling activateSession, your delegate will receive a callback to the function session(session: WCSession, activationDidCompleteWithState activationState: WCSessionActivationState, error: NSError?) If activationState is .Activated, your watch is connected and you can start transferring data.

It makes sense to wrap this code in a singleton object to avoid having to keep references to the WCSession all over the place. Therefore, start by creating a new file WatchSessionManager.swift, in the group Services, and add the following code:

In AppDelegate.swift add the following:

This will create and start the session. Any other class which needs to communicate with the watch can now access the shared WatchSessionManager object.

WatchConnectivity provides several different methods to communicate between phone and watch. These come in two main groups:

  • Asynchronous background transfer
  • Synchronous messages

When using background transfer one side sends a package of data to the system, which then forwards it to the other side at some point in the future. We’ll use this to transfer the current location and weather. The function we’ll be using is

This transfers an application context to the watch. The context is a dictionary containing property list types. This means that to send arbitrary objects they must be conterted into valid property list types.

Add the following code to WatchConnectivityManager.swift

This function gets an object of class Weather, retrieves the relevant bits of information (place, weather symbol, temperature and precipitation) and sends it as a dictionary

This needs to be used when the app has retrieved user location and weather, which happens in LocationsViewController.swift.

First, we need at get the shared WatchSessionManager object. Add the following line, right above the declaration of fetchedResultsController (line 30)

This gives us the manager to work with. Then, on line 244, above the call to self.tableView.reloadData(), add the following line:

WatchConnectivity on watch

To receive the weather information we need to setup connectivity on the watch side as well, we do this in InterfaceController.swift. First import the framework. Add the following line at the top:

Then we need to setup the session. In awakeWithContext, add the following lines:

Note that we don’t need to call WCSession.isSupported() as WatchConnectivity is always supported on the watch. You also need to make InterfaceController implement  WCSessionDelegate. Change the class definition to the following:

Once the application context is transferred from the phone, it is available on the watch as the property receivedApplicationContext on the WCSession object. We need to send this to the WeatherController to display.

As we set up a segue from InterfaceController to WeatherController in part1, we can use the method contextForSegueWithIdentifier(segueIdentifier: String) to transfer the weather information. Add the following code to InterfaceController.swift

 

Here we first check that the segueIdentifier is the correct one. If so, and if the context has been transferred, get the dictionary containing weather information and send is as the context for WeatherController

Next, we need to present this information in WeatherController. Add the following lines to awakeWithContext

If the context is not nil and contains keys, we update the view outlets with the correct values.

Finally, in order to see the weather symbol, we need to make the Assets.xcassets catalog from the iphone app available on the watch. Select the Assets catalog and ass watch Extesnion to the Target Membership.

To try this out, run the watch target, start the app on your phone and wait until it has found current weather. Then you should be able to see the same weather on your watch.

Show saved locations

The final step in this tutorial is to show the list of saved locations. For this we’ll use a different function in WatchConnectivity, we’ll send a message to the iphone app requesting the list of saved locations. The function call we use is

This sends a message in the form of a dictionary to the counterpart, and receives the reponse in a handler closure.

First we need to remove the dummy code we entered in part 1. Replace the declaration of locations with the following line:

Replace the awakeWithContext in LocationsController.swift with the following

Here we get the WatchConnectivity session and send a message containing a simple key-value pair (needed to identify the message on the phone app). In the replyHandler we get an array of weather dictionaries (similar to the one used in WeatherController) wrapped in a dictionary.

The last step is to add the code on the iphone side to generate this reply. Here we use the function

This receives a message, prepares the reply and executes the replyHandler. Add the following code to WatchSessionManager.swift

  1. We check that the message is requesting saved locations.
  2. We get the weather data all saved locations and execute the replyHandler with the weather data wrapped in a dictionary (as this is the expected parameter for the handler)
  3. Getting the actual data is extracted into a separate function. First we get the list of saved locations
  4. Iterate over the locations and get the current weather for each location. We use dispatch_group_enter/dispatch_group_leave to record when each call to getWeather finishes (as it is an asynchronous call)
  5. Extract the relevant parts of each weather object into a dictionary and save in the watchWeather array
  6. Wait for all calls to getWeather to finish and return the result.

didReceiveMessage will by default execute in a background thread, therefore we can block in getSavedLocationsWeather without fear of affecting the UI on the iPhone.

After this is done you can build and run the project and any locations you have saved on the iphone app should be available on your watch.

Next steps

To see the source code for the final project, checkout the branch tutorial_part2.

In this tutorial I have shown an example on how to create a simple watch app, create the user interface and communicate with the iPhone app. If you’re interested in learning more I recommend you look at the apple documentation, and the sessions from WWDC 2015 and 2016.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *

Kotlin – an Introduction

At Google I/O 2017, Google announced that Kotlin would become a first-class language in Android. This means that Kotlin will be supported..

Eress Forum 2017

Eress and Erex Eress is an organisation created to provide a simple, efficient, reliable, accurate and flexible standard energy settlement..

Devoxx UK 2017

Introduksjon Devoxx UK er en todagers konferanse som holdes i London. Det er en mellomstor konferanse med over 1200 deltakere..

Bouvet Battle Royale – Robot Wars

Introduction Robot Wars, a robot-sumo competition, was held at Bouvet early April for students attending technology courses at the University in..

Magic Mirror – version 1

Introduction A while back I discovered the exciting world of “magic mirrors”. I don’t remember how or where it caught my attention, but..

DevOpsDays Oslo 2016

5.-6. september hadde eg gleden av å delta på den første norske DevOpsDays i Oslo. Her er en oppsummering av høydepunktene..

Bouvet at JavaZone 2016

This year JavaZone celebrated it’s 15th year with with 3000 attendees and over 170 sessions. As one of Norway’s premier Java..

IT years are like dogs years

One of the characteristics of the IT industry is that time works differently for us. This is challenging and fun,..

The Future of SharePoint

Den 4. mai holdt Microsoft en virtuell event om fremtiden til SharePoint, jeg fikk heldigvis anledning til å delta de..

SharePoint 2016 er på vei!

Som lovet var Microsoft ferdig med utviklingen av SharePoint Server 2016 (RTM – release to manufacturing) rett før påske, og..

Teknologi

I Bouvet er vi flere hundre teknologer som brenner for å programmere og utforme gode, digitale løsninger. I denne bloggen utforsker vi teknologien og deler det vi finner med dere.