WatchConnectivity: Sharing The Latest Data via Application Context

Check out previous blog posts on WatchOS 2 if you haven’t already:

Background data transfers via Application Context should be done when your Watch App only needs to display the latest information available. For example, if your Glance shows the game score, the user doesn’t care that the score was 4-2 two minutes ago. They only care that the current score is 4-4. Another example for this use-case is a transportation app – your user doesn’t care that the last bus left the station five minutes ago – they only care about when the next bus will be arriving.

So the way Application Context works is that it queues up a piece of data, and if there’s a brand new piece of data available before the data is transferred, the original (old) data is replaced by the new data, which is then transferred over unless it is replaced yet again by an even newer piece of data.

Inspired by Kristina Thai’s Application Context tutorial, I’m going to walk through building a similar emoji-based app. In my app, the user selects a food emoji from a menu, and the latest food item is displayed by the Apple Watch app:



Note that in my app, I’m going to write more of an abstract data updating model than you’ll find in Kristina’s tutorial, so it’ll be over-engineered for the app I’m demoing.

The assumption I’m working with is that your actual app is going to be a lot bigger than this, and it’ll need to update several views or data sources with the data received from the iOS app (or the Watch app). So if your app is actually simple like the one I’m demoing, with only one view updating, Keep it Simple and check out Kristina’s tutorial!

I’m also playing around with different ways of doing the abstraction layer of sending the updated data to the different parts of the app that need it, so I’m sure there are better ways of doing it! If you think of any, let me know your thoughts in the comments.

The Setup

For this tutorial, I’m assuming you know how to create a Single View Application in Xcode, and create a simple Table View with a list of Food Emojis. If you’re having problems replicating it, take a look at my FoodSelectionViewController here.

I’m also assuming you know how to create a Watch App and do the basic styling in Interface.storyboard of your Watch Application. If you need help settings this up, make sure to read my WatchOS 2: Hello, World tutorial.

Finally, you should be able to set up the basic singleton to wrap around WCSession and activate it in application:didFinishLaunchingWithOptions in the AppDelegate and applicationDidFinishLaunching in the ExtensionDelegate in your Watch Extension. If not, take a look at my WatchConnectivity: Say Hello to WCSession tutorial.

You should have something that looks like this in your iOS app:

And something like this in your Watch App:

And of course, you can always refer to the source code for this tutorial if you ever need extra clarification.

Now, on to the fun stuff 🚀.

Sending Data

In my application, when the user selects a food item, it needs to be transferred in the background to my Watch App. That means the iOS app is the sender. This is super simple to do.

Just extend the WatchSessionManager singleton in your iOS app to update the application context:

So now, when the user selects a food item cell, you can call this method:

That’s it! The food item is now in a queue and will be send over to your Watch App, unless a new food item is selected before it gets transferred of course!

Receiving Data

Your Watch App now has to receive the data. This is pretty simple to start with. Just implement the session:didReceiveApplicationContext: WCSessionDelegate method.

Updating Data

Now that you received the data, this is the tricky part. Trying to let your Watch Extension’s InterfaceController and other views or data sources that your data has been updated. One way to do this is through NSNotificationCenter, but I’m going to try out a different approach. This is the part that can be done in multiple ways and is a bit over-engineered for this app, so keep this in mind.

Since we’re in Swift, my goal is to change to a value-type model as soon as I can. Unfortunately, as I mentioned in my blog post on WCSession, the WCSessionDelegate can only be implemented on an NSObject. So to mitigate that, I created a DataSource value that can take the applicationContext data, and convert it into something immutable and usable by multiple InterfaceControllers.

I can now set up a protocol that will update all parties that need to know about the data change with the most updated data source:

So now onto the interesting part! Your WatchSessionManager will need to somehow keep track of all the dataSourceChangedDelegates. This could be done through an array, and methods that will add and remove delegates from the array:

We can now add the implementation for when the application context is received:

Now, we just need to make sure our InterfaceController is DataSourceChangedDelegate, and is kept track of by the WatchSessionManager.

And that’s it!

You can view the full source code on Github here.

Note: A special thanks to @allonsykraken for reviewing my code and giving me some great ideas!

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.