More “WatchKit Mistakes”

A sequel to “My Biggest WatchKit Mistake”, we asked a few more of our friends
to share the most useful thing they learned building for Apple Watch.

You can read part 1 here.

Joe Hughes

Mobile lead at Citymapper

Now that the Citymapper Watch App is finally on people’s wrists, we’re finding that we want to pay more attention to the communication between the Watch and the phone, and how that’s signposted for the user.

Handoff is really convenient — you can swipe up on your phone to get a fuller view of whatever you’re looking at on your Watch. Citymapper already supports it when you’re looking at live departure pages, trips, and service alerts. However, I’d missed how perfect it is for the initial setup experience, when you need to send someone to the phone to configure your app. I’ve seen a few apps do this well, some even including little “swipe up” animations on the Watch.

On the other hand, we also have interactions that start on the phone and end on the Watch, for instance setting your current trip so you can follow along on your Watch. Here, we’re adding more messaging on the phone side specifically for Watch users. While there’s no API to tell whether a user has a Watch connected, what you can do is save a flag to your shared NSUserDefaults the first time the user runs your Watch app, so that you know to enable Watch-specific messaging and features on the phone.

It’s exciting to finally be able to test our apps in the wild — I think we’re all learning a lot about how the make the most of short wearable attention spans!

You can follow Joe on Twitter @joooe

Phillipe Casorla

Director of Mobile Engineering at Lifesum

My biggest WatchKit mistake was building for iOS 8, as if iOS 7 did not exist. I found out the hard way: iOS 7 exists.

At Lifesum we love refactoring. I wanted to use embedded frameworks to build our own core library, then share that code across our apps, widgets, and now our watch app. So far so good, but close to release we learn that iOS 7 doesn’t support embedded frameworks! At that point we basically had two options: build our own static library, or move all the source files back to the main target. Guess which one we chose?

The first mistake led to a second, more painful mistake. One of the nice things about using an embedded framework library was the contrast with using a Core Data stack runing on an AppGroup. With a framework, the main app and the extensions can easily read/write the same data using Core Data’s file coordination.

Well, as you might imagine, Apple never tells you how to do the migration from your documents folder to the new app group. The first, naive solution: just move the files manually from the old folder to the new folder. Easy! Trust me, Core Data is more complicated than that. A much better solution is to do a NSPersistentStore migratePersistentStore:toURL:options:withType:error, but it involves writing some fairly complex code. The amount of quirky cases you need to take into account are, shall we say, amusing. Some will be missed out.

We were lucky, all migration cases were handled correctly; but there was one thing we forgot to account for: the time it could take to finish a migration. For some iPhone 4S’ it took more than 30 seconds. The delay, and the fact that the migration was holding the return of application:didFinishLaunchingWithOptions, left some users unable to open the app at all because the migration would be terminated by the OS before it could complete. So, many lessons!

You can follow Phillipe on Twitter @Darkzlave

Kostiantyn Koval

Organizer and blogger at Rocketfarm

My biggest mistake was looking for resources in the main bundle of my shared framework. You can share code and resources between an iOS and a WatchKit app by making a framework, but remember: when accessing resources like images, plist files, or data models in your framework you should specify a bundle.

// The wrong way
UIImage(named: "Rectangle")

// Instead, look for image in main Bundle

// The right way
let bundleForClass = NSBundle(forClass: self)
let bundleForId = NSBundle(identifier:"kkoval.SharedKit")

// "kkoval.SharedKit" is a framework bundle identifier

UIImage(named: "Rectangle", inBundle: bundleForClass, compatibleWithTraitCollection: nil)
UIImage(named: "Rectangle", inBundle: bundleForId,    compatibleWithTraitCollection: nil)

let modelURL = bundleForId?.URLForResource("Model", withExtension: "momd")
let model = modelURL.flatMap { NSManagedObjectModel(contentsOfURL: $0) }

If you’d like to experiment, I’ve made a sample Xcode project.

You can read Kostiantyn’s writing on his blog.

Natasha Murashev

iOS Engineer at Capital One and blogger at Natasha The Robot

Last week I told you about my biggest mistake. I learned one other very useful thing, which I’d also like to share: how to debug your iOS and WatchKit app at the same time.

In your iOS app’s delegate, there is a function to receive information from your WatchKit extension:

// AppDelegate
func application(application: UIApplication, handleWatchKitExtensionRequest
                    userInfo: [NSObject: AnyObject]?, 
                       reply: (([NSObject: AnyObject]!) -> Void)!)

When I first implemented this method, it wasn’t working and I didn’t know how to debug it — I could only run one target at a time, either my WatchKit app or my main iOS app. I ended up putting in alert views in my iOS Application to try to debug my code. Later, I found out there is a way to debug both your iOS and WatchKit app at the same time:

  • Run your WatchKit App Target
  • Open your iOS App in the Simulator
  • Go back to Xcode
  • Select Debug > Attach to Process > (YOUR iOS App)

Now your breakpoints and usual debugging tools will work for your iOS app while you’re running your WatchKit app! Very helpful :)

Watch Natasha’s video: Architecting Your App for the Apple Watch

Stephen Tallent

iOS at Mercury

We quickly learned that a typical approach taken by iPhone apps — fetching server data and images at display time — didn’t work well for WatchKit apps. WatchKit apps are a little slow to begin with, and if you add to presentation time by making call roundtrips from extension, to host app, to server and back, you end up with a very poor user experience.

Users will need and demand even more instant information on their Watch than they do on their phone. It took a lot of effort, but now we pre-populate most of the data our Watch app needs well before the user ever launches it. Our iPhone app writes out Watch data on a continuous basis, both during regular app usage, as well as with modest background tasks. In effect, our Watch app renders the screen and nothing else. This was hugely positive for performance and usability.

Stephen will be blogging more about this on the Mercury blog

Jeff Grossman

Co-founder of Confide

If your app is heavily localized, don’t forget to test support for right-to-left languages. Interface object defaults are typically “position left horizontal” and “size width to fit content”. For right-to-left languages, you will have right aligned text in a left aligned text WKInterfaceLabel, and the text will appear to float awkwardly on screen. To fix this, make your widths relative to the container when applicable.

Something else I learned early on: Apple doesn’t include an API for notifying your Watch app when handoff to an iOS device has completed. However, I usually want to close the current WKInterfaceController upon handoff completion.

One way to do this is to include a handoffUUID (generated via [[NSUUID UUID] UUIDString]) in the userInfo dictionary passed to updateUserActivity(:userInfo:webpageURL:)

In my iOS apps I use the UIApplicationDelegate method application(:continueUserActivity:restorationHandler:), and when successful I check for the handoffUUID. If present, I use MMWormhole to send a handoff success message back to the Watch. WKInterfaceController listens for that message, and when it receives a matching handoffUUID, it closes itself, or performs a different handoff success task.

* Note: When using kSecAttrAccessibleWhenUnlocked for the keychain or NSDataWritingFileProtectionComplete, remember to test on a real device with the passcode lock enabled. Just because an Apple Watch is unlocked that does not mean the iPhone is unlocked, or the data is accessible!

You can follow Jeff on Twitter @jeffrey903

Chad Etzel

iOS Lead at Super

Many iOS developers create all of their views programmatically and never use Interface Builder for anything (myself included). WatchKit apps require using Storyboards to create the Watch interfaces, which meant I had to relearn how to use Interface Builder.

To learn WatchKit, I wrote my example app, FlickrWatch, in Objective-C at first. Once it was working, I started a new project and tried to write it again in Swift. To save time, I thought I could just copy my Storyboard file over to the new Swift project (it uses the exact same interfaces, after all), but this turned out to be a big mistake!

All of the outlets in the storyboard file were confused about which files were connected to them, and even trying to sever the outlet connections and change the components to the proper class files turned out to cause issues. The app would launch but the storyboard scenes just would refuse to load (it would show the infinite spinner on the Watch). I tried everything I knew to fix the outlet connection issues, but ultimately I had to just throw everything out and start over from scratch. I recreated the entire storyboard for the new Swift project and everything worked just fine.

You can follow Chad on Twitter @jazzychad

Andrea Mazzini

Cofounder & iOS developer at FancyPixel

I guess the biggest mistake I made was continuing to think about Watch apps in the same way we think about iOS apps. Most apps are meant to be designed for short interactions; we fetch new data and present it to the user. In WatchKit, these interactions are actually even shorter: once the user closes the Watch app, the extension that runs it is stopped.

I’ve learned to keep the WatchKit extension’s actions short and to the point, delegating the heavy lifting (e.g. a network fetch) to the main app instead. WatchKit provides a simple way to achieve this. By using openParentApplication(:reply:) from a WKInterfaceController we can wake up the main app, fetch the new data in a background task and store it somewhere safe, ready for the next Watch interaction.

You can read Andrea’s writing on his blog

Marthin Freij

Developer and co-founder at Amazing Applications

My first mistake was thinking too big. As developers, we’re often blinded by what we can do rather than what makes sense to do. The Apple Watch is all about context, so really challenge yourself to figure out the core of your idea and how that would fit on the wrist of your users.

Much like Andrea, I started by thinking about Watch apps in the same way I do about iOS apps. When adapting our cooking app Green Kitchen, I began by wanting to transfer the full experience from our iOS app to the Watch. Imagine, finding and cooking recipes directly from the wrist! However, when we started to design, it quickly became clear that doing this would produce an app that would be too complicated, too clunky, and too slow. In short, it would not be a great use case for the Watch.

We took a step back and started to think hard about the user’s context. We found that almost all cooking is divided into active and passive periods: our iOS app worked well in the active periods, when the user was actually cooking, but didn’t help with the passive periods, when the user was waiting — for the stew to simmer or the oven timer to ring. Was there something about the Watch that could do this better than the phone?

Narrowing our focus, we came up with a more Watch specific solution: a context-aware cooking timer, started from the user’s iPhone, that could trigger targeted wrist-based information when the time is up — in this case, a descripion of how the food should look, feel or taste. If the food is not ready, the user can extend the timer directly from the wrist without interacting with the phone. If the food is ready, we use handoff to pass the user right back to the recipe. A great lesson in the importance of thinking small!

You can follow Marthin on Twitter @marthinfreij

Jeff Forbes

iOS at Foursquare

The biggest mistake we made was one of scope and less of development. Optimizing experiences for the Watch takes careful thought to make sure the app stays simple and the interactions stay brief but useful and delightful. As always, KISS applies! — Keep It Simple, Stupid!

There’s a lot of uncertainty around the apps we’re making too; it’s really hard to know the mistakes we don’t know we’ve made yet. Until, that is, the Watch arrives in user’s hands — we’re only just finding out now!

As Marthin and Andrea highlight, you can get into a design hole where you’re basically just recreating your app’s feature set on the Watch. In the end it’s really important to think about the Watch’s ideal use case: deliver useful, actionable information in the shortest time possible. Adding features just adds clutter around that goal, watchmakers call them “complications” for a reason.

You can follow Jeff on Twitter @masterjeef

Get more development news like this

Next Up: New Features in Realm Obj-C & Swift

General link arrow white

About the content

This content has been published here with the express permission of the author.

Joe Hughes

Mobile lead at Citymapper; fan of design, urban form, public transit (and open data), experimental video games, lindy hop, fine cocktails, and ramen

Phillipe Casorla

Taking over storage through Makespace. Previously Director of Mobile at Lifesum and CTO at Sabor Studio. Living the world through my products.

Kostiantyn Koval

iOS Dev at Agens. In love with iOS & Swift. Swift High Performance author

Natasha Murashev

Natasha is secretly a robot that loves to learn about Swift and iOS development. She previously worked as a senior iOS Engineer at Capital One in San Francisco, but now she travels around, writing about her experiences with new technologies. In her free time, she works on personal projects, speaks at meetups and conferences, contributes to open source, and likes to cross things off her bucket list.

Stephen Tallent

Hockey fan and skateboarder when I’m not furiously coding. Actually, even when I am.

Jeffrey Grossman

Developer. Cook. Photographer. Co-founder of Confide.

Andrea Mazzini

Andrea Mazzini writes code primarily for iOS but sometimes for Rails. When coding gets to his nerves he unwinds by writing tech articles and drawing stuff with Sketch. You can checkout his work over at Fancy Pixel and his open source work on his Github page.

Chad Etzel

Overall programming nerd with too many project ideas for one lifetime. Nixie tube enthusiast.

Marthin Freij

Developer and co-founder at Amazing Applications. 💚 Green Kitchen ❤️ Healthy Desserts

Jeff Forbes

Dude. iOS Dev @ Dropbox.

4 design patterns for a RESTless mobile integration »