WatchConnectivity: Sharing All Data via User Info

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

Background data transfers via User Info should be done when you need to make sure that all your data is transferred (not just the latest like with Application Context). User Info data is queued up for delivery in a FIFO (first-in-first-out) order, so nothing is overwritten.

One examples for when you might want to use this is in a text messaging app – when the last message is just as important as the first to see the full conversation and context. Or if the user updated a few pieces of their profile information and all the changes need to be synced to the Watch profile.

For this tutorial, I’m going to walk through building a food emoji communication app because food and emoji! I LOVE 🍦!

Also, this could potentially be an Apple Watch based grocery list app – you select the food you plan to buy from the list of emojis on the phone, and it goes to the app, which you can then glance at as you shop at the grocery store!

emoji grocery store


Note that for this app, I’m going to write more of an abstract data updating layer meant for larger applications with multiple places in the UI that need to have the updated data source, so it’ll be over-engineered for the app I’m demoing.

I’m also experimenting with different architectures, especially in Swift, so if you have feedback on how to make the abstract data layer better in Swift, let me know 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 and the WatchKit: Let’s Create a Table 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, every time 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 transfer user info:

So now, when the user selects a food item cell, you simply call the above method:

That’s it! The selected food item is now in a FIFO queue and will be send over to your Watch App!

Receiving Data

Your Watch App now has to receive the data. This is pretty simple to start with. Just implement the session:didReceiveUserInfo: 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 userInfo data, and convert it into something immutable and usable by multiple InterfaceControllers. Since user info is received in a FIFO queue order, the DataSource should also keep track of the data in the order received.

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. The WatchSessionManager also needs to keep track of the latest copy of the DataSource, so it can use the data from that DataSource to create a new DataSource with the newest data:

We can now add the implementation for when the user info 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.

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

  • Sabes™

    How do your datasource delegates perform on your devices Natasha? Mine are really hit or miss…especially my Glance, which works on the simulator but not on device. I am calling transferUserInfo and updateApplicationContext in my app delegate methods (didFinishLaunching and didEnterBackground) not sure if that makes a difference?

  • Thank you for this wonderful tutorial Natasha! I have a quick question. I recall that you created a tutorial, when WatchOS1 was being used at the time, to make the Apple Watch share data, from Core Data, with the iPhone. Now that we have WatchOS2, do you recommend, since both devices now have their own data stores, that developers send their Core Data objects to the Apple Watch (and back), using dictionaries, constantly when something changes? What would you recommend?

  • Adriana Pineda

    Hi Natasha! Thanks for the blog post. Im doing a watch app and after watchOS 2.2 Im having trouble transferring user info and updating the application context. Sometimes I see that the session is not valid (but it is, I mean the watch is paired and has the app installed) and some other times the event simply are not received. I thought I was doing something wrong, but it turns out that many people are having this issues; I tried to run your project and same problem with my watchOS 2.2
    Have you had this issue before?