WatchKit: Best practices for sharing data between your Watch and iOS app

The Apple Watch is here! I’m looking forward to finally doing some development on the actual device. In the meantime, I’ve been getting a lot of comments and questions about sharing data between the WatchKit extension and the parent iOS app in my tutorials. While Apple has provided one API to do this (the reply from handleWatchKitExtensionRequest), that simply isn’t enough for developers that want to transfer/share larger amounts of data, or if they want to share a common database for both apps. I touched on a couple of methods briefly in my contribution to the “Biggest WatchKit Mistakes” post by Realm, but here’s a breakdown of all the different possibilities and when you should use them.

Reply dictionary in HandleWatchKitExtensionRequest

This is currently the only API that Apple has provided for developers to transfer information from the iOS app back to the WatchKit extension.To see how to do this, check out my tutorial on communicating back from the parent app via reply. Unfortunately, there isn’t a way to initiate a transfer from the iOS app to the WatchKit extension, without some help from something like MMWormhole.

The main downfall of this is that the information you can store in the dictionary is limited. Only the following types are supported by the plist:

  • NSData
  • NSString
  • NSNumber
  • NSDate
  • NSArray
  • NSDictionary

While these objects and primitive types can all be stored in the reply dictionary just fine, inserting any custom class object will throw an error that looks like this:

Error Domain=com.apple.watchkit.errors Code=2 "The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]" UserInfo=0x61000006f640 {NSLocalizedDescription=The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]}

Since the custom object can’t be serialized by the property list (plist) file, it thinks that the parent iPhone app never called reply in handleWatchKitExtensionRequest even if it did.

NSKeyedArchiver

An interesting workaround for this is to use NSKeyedArchiver to archive your custom object, or any other data you want to store, and then unpack it once it gets sent to the WatchKit extension. See this snippet for an example:

On the phone side, build your dictionary

NSMutableDictionary *reply = [NSMutableDictionary new];
MyCustomObject *myObject = ;
reply[@"myKey"] = [NSKeyedArchiver archivedDataWithRootObject: myObject];
NSAttributedString *myString = ;
reply[@"otherKey"] = [NSKeyedArchiver archivedDataWithRootObject: myString];

And unpack it back on the watch side

NSData *objectData = replyInfo[@"myKey"];
MyCustomObject *myObject = [NSKeyedUnarchiver unarchiveObjectWithData: objectData];
NSData *stringData = replyInfo[@"otherKey"];
NSAttributedString *myString = [NSKeyedUnarchiver unarchiveObjectWithData: stringData];

Thanks to Brian Montz for this snippet!

App Groups

The bulk of the data sharing options require you to enable App Groups. After you do that, there’s three different options that open up allowing you to share data.

NSUserDefaults

NSUserDefaults is a property list (plist) where data can be stored by your app. This actually has the same limitations that I mentioned using the reply dictionary (it only supports object types that can be serialized by a plist). On the plus side, it is easy to use and allows you to share one storage plist between both the WatchKit extension and your iOS app. If you’re looking for something that’s basically as simple as the default API provided by Apple, this is the way to go.

See this tutorial by Coding Explorer on how to do this.

NSFileManager/NSFileCoordinator

Using NSFileManager is another way to share a single storage container between the WatchKit extension and the iOS app. This particular method is best for finite lists and multiple images. One thing to watch out for is data corruption with multiple reads and writes, which are best handled using NSFileCoordinator. Natasha talks about this in her presentation about Architecting Your App for the Apple Watch. You can find it under NSFileCoordinator.

MMWormhole

MMWormhole is a third-party open source tool that ” creates a bridge between an iOS or OS X extension and its containing application. The wormhole is meant to be used to pass data or commands back and forth between the two locations. Messages are archived to files which are written to the application’s shared App Group.” You can find more information about it on GitHub.

KeychainAccess

This last one is primarily best for when you need to store secure data. Security is one of the most important issues nowadays facing developers and the Apple Watch is definitely no exception. Natasha has also provided a thorough walkthrough of this in her presentation about Architecting Your App for the Apple Watch. Look for it at the end of her talk under Keychain Access.

WatchKit tutorial: Maps on the Apple Watch

We finally have a release date for the Apple Watch! I’m looking forward to building apps with the actual device, but for now the simulator will just have to do. There’s an interesting new watch simulator called Watch Sim that will let you evaluate your Apple Watch app in real size on an iPhone or iPad, but it’s a bit pricey. If you’re not planning to get a watch though, $9.99 is a great bargain (but would you really be making Watch apps without owning an actual watch?). I find that Bezel is a pretty good substitute, but there’s no physical interaction (it’s still only on your computer screen).

Onto the tutorial!

Apple Watch Maps

If you’ve been following along, this next tutorial should be pretty simple compared to the last couple that I’ve made. We’ll be looking at displaying a map on the Apple Watch and setting both native and custom markers on it.

Map Interface
The downside is that that’s pretty much all you can do at this moment (as of the iOS 8.2 release). Users can’t even interact with these static maps – once the user taps on it, it will open up the default Apple Maps app. You can read more about map capabilities on the Apple Human User Interface Guidelines.

As always, I’ve provided all of the project code on GitHub. You’ll also need some image files – download the zip file here. Let’s get started.

Continue reading

WatchKit Tutorial: Communicate from Parent App Back to Watch via Reply

Note: this will be a continuation from my last tutorial on WatchKit – Send Data to your Parent iOS App. If you haven’t done it already, I suggest you start there first.

WatchKit Updates

It’s been about a month since my last WatchKit tutorial and we’ve had 2 new beta releases for iOS 8.2, including a few changes for WatchKit. Here’s the release notes for iOS 8.2 beta 4:

watchkit iOS 8.4 beta 4 release notes

WatchKit still has pretty limited functionality and there haven’t been many new changes – it’ll be interesting to see what kinds of apps people create once it’s live. Looking back on the Apple keynote where they announced the Apple Watch and showed all the cool tricks like being able to send your heartbeat to someone else, I feel a bit cheated as a third-party developer. So far, we don’t have access to any of these interesting features. It’s like when a movie trailer shows all the cool parts of the movie, and the movie itself is pretty lame.

Reply Back to Watch app

Counter Reply

Now, to get down to business – I’ve added on to my original tutorial that allowed you to send data to your parent iOS app from the watch. We’ll be exploring replying back to the Watch app from the parent iOS app. This is useful for cases where you may want to update the UI of your Watch app or execute some code after your iOS app receives data from the Watch. In this case, we’ll be showing the user a “Saved” notification indicating that their save to the iOS app was successful.

You can see the finished project for this tutorial here on Github.

Modify Watch Storyboard

To get started, bring up the finished counter project from the last tutorial. If you haven’t gone through it yet, you can grab it here from Github.

Continue reading