Software Engineering watchOS

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.

6 thoughts on “WatchKit: Best practices for sharing data between your Watch and iOS app”

  1. Hi kristinathai,

    i like you document about the apple watch data sharing but still here above code snippet you have mentioned the MyObject class in phone app side its ok, but how the same class would be accessible in watchkitextension context due to which i am always here getting the crash of excution. can you please guide me here in this situation?

  2. The NSUserDefaults method of sharing data between iOS and WatchOS has been discontinued as of WatchOS 2

  3. Please tell me a way to populate the Table controller present in Watch kit using the core data stored in iOS app.

Leave a Reply

Your email address will not be published. Required fields are marked *