Real Story about WatchKit


Paul Taykalo, Lead iOS Engineer at Stanfy, a mobile design and development studio based in San Francisco.


The Apple Watch hasn’t been released yet, but we already have a lot of posts with “How to Start developing for Apple Watch”. Companies are trying to find possible products which they can present on the new platform, and developers are trying to answer the question “Will Apple Watch be able to do <X>”? There are a lot of things we know: what will be possible to do on AppleWatch, and possible use cases. Apple has released SDK long before the real device. I think, it could lead to a big boom of applications that will fail to run on the real devices, but it’s just a prediction.

So this is a post about some things you’ll need to know in order to correctly tell your managers what Apple Watch is capable of. It is not a tutorial about “How to create an Apple Watch Application”. It talks about real-world questions that have been asked, and some thoughts about possible implementations.

Starting point

We need to have some prerequisites. Make sure that whoever is asking the question and you have read Apple Watch Human Interface Guidelines.

So. At this moment we know that there are Glances, Notifications, and other type of screens. Let’s say we have some guy, who generates ideas, and who will ask us questions. As awesome engineers, we need to provide answers.

Glance Pitfall

Some guy: “Hey, please take a look at the Glance design we’ve made. I know that Glance cannot have buttons, and I know that after user press on it, he will move to the application, right? So we thought that it should look like the same first screen in the application itself. So here it is.”

Some guy: So, what is your answer?

You: …

PAUSE

Yeah. this is quite a simple screen, and this is how Glance should look — only the most valuable information. It seems that it’s an easy one, but, if you look closely at how Glance can look, you’ll see that it has a default layout of two main parts (upper and lower). Speaking short, there’s no full-screen layout template for Glance ( I wish that I were wrong, but for now — it’s what we see on Glance).

UNPAUSE

You: Well, no, we cannot make it look like this. We don’t have a layout template for this. At least, not with the current SDK. We can put it in the lower part of the screen, and make it look like this.

Layout Tips and Tricks

Some guy: Well. Okay, I have another idea. I’m thinking about a simple scroll shooter for the WatchKit. We’ll show a rocket and the user will be able to move it around by pressing left and right.

You: Well, you know, that won’t work well, since the Apple Watch is not intended to be used this way. Based on the latest reports, it will be able to run at most 3–4 hours. So I don’t think that user will waste his precious Apple Watch battery life on this kind of game. And also, don’t forget that reaction time on buttons will not be immediate. The command will need to go to the iPhone and get a response from it. And this is performed over Bluetooth. It’s slow on the Simulator itself, so I’m not sure about whether it will work fast enough on the real device. It can be even slower than in Simulator.

Some guy: I don’t care. We’ll say it’s real-world simulation, when the commands cannot be sent to the rocket immediately — distance to the rocket, speed of light limit, and those things, you know. So, can you do it?

PAUSE

Let’s throw away thoughts like : ”It drains battery” or “It will not make user happy.” Let’s look at it as the layout problem.

In the WatchKit we don’t have too many methods for laying out items. There’s no setFrame: or setBounds:. Interface elements tend to automatically lay themselves out, based on the content. So, can we put elements in any position we want on the screen?

Yes. We’ll need to have proper setup for that, but we can do it. There’s possibly better ways how to do it but here’s an example how it can look like.

We have a Main group, which has a vertical layout and contains 3 other groups (Top, Center and Bottom).

The Center group has a horizontal layout and contains Left group, Rocket and Right group.

In this example, we used “Group” objects everywhere, so you’ll be able to add interface elements to them later.

We cannot use setFrame:, but we can use setWidth: and setHeight:. All we need now is to calculate the correct height and width values for each group, and update those values each time the user is pressing buttons.

For changing Rocket’s Y coordinate, you’ll need to update the top and bottom groups’ heights.

For changing Rocket’s X coordinate, you’ll need to update the left and right groups’ widths.

For placing

let maxWidth:CGFloat = 156;
let maxHeigh:CGFloat = 140;
let rocketWidth:CGFloat = 25;
let rocketHeight:CGFloat = 50;
var currentXPosition:CGFloat = 0.5;
var currentYPosition:CGFloat = 0.5;
@IBOutlet weak var playingGroup: WKInterfaceGroup!
@IBOutlet weak var topGroup: WKInterfaceGroup!
@IBOutlet weak var centergroup: WKInterfaceGroup!
@IBOutlet weak var bottomBroup: WKInterfaceGroup!
@IBOutlet weak var buttonsGroup: WKInterfaceGroup!
@IBOutlet weak var leftGroup: WKInterfaceGroup!
@IBOutlet weak var rightGroup: WKInterfaceGroup!
override init() {
super.init()
updateXPosition(currentXPosition);
updateYPosition(currentYPosition);
}
func updateYPosition(position:CGFloat) {
let topSize = position * (maxHeigh — rocketHeight);
let bottomSize = maxHeigh — rocketHeight — topSize;
topGroup.setHeight(topSize);
bottomBroup.setHeight(bottomSize);
}
func updateXPosition(position:CGFloat) {
let leftSize = position * (maxWidth — rocketWidth);
let rightSize = maxWidth — rocketWidth — leftSize;
leftGroup.setWidth(leftSize);
rightGroup.setWidth(rightSize);
}
@IBAction func leftButtonAction() {
currentXPosition -= 0.05
if (currentXPosition <= 0.05) {
currentXPosition = 0.05
}

self.updateXPosition(currentXPosition)

}
@IBAction func rightButtonAction() {
currentXPosition += 0.05
if (currentXPosition >= 0.95) {
currentXPosition = 0.95
}

self.updateXPosition(currentXPosition)
}

Hard-coded values! Hard coded values! You’re using hard-coded values!

Yes, we’re using hard-coded values here. You’ll need to update this code later. ☺ (Consider using WKInterfaceDevice and WKInterfaceController’s contentFrame)

But, you got the idea.

Even if we cannot manipulate interface object frames, we can use embedded layout strategies for placing objects where we want them to be.

UNPAUSE

You: Well this is how it can work (explaining).

Some guy: Perfect, perfect (rubbing his hands). Now, I want to have flying animations. Rocket should move through space!

Animated Backgrounds

You: Well the last request is the easiest one. You will even be able to change the speed of how fast this thing should scroll.

Pause

It’s a pretty simple thing to do in WatchKit. In UIKit, we can have animated images only in UIImageView control. But in WatchKit we can have animated images and backgrounds almost everywhere.

The animation speed/part can be changed by calling

func startAnimatingWithImagesInRange(imageRange: NSRange, duration: NSTimeInterval, repeatCount: Int)

function. Note that we can only play “animations” forward. (Note that we can only play “animations” forward. You cannot reverse them. So, in case you need to have reverse animation, you’ll need to prepare images for that too.) UPD: In order to play animation in reverse , you can set negative duration

UNPAUSE

Grouping Groups in Groups

Some guy: And then, we’ll have asteroids, and they will come from different sides…

You: Hold on. You want some other objects to be flying over the screen?

Some guy: Well, yeah. It’s scroll shooter anyway…

PAUSE

This request is tough. And now you think, “Why didn’t I say that we cannot do it just before we started?”. But there’s no way back. We need to go deeper.

Okay, so we used 4 additional interface objects for placing one object at a certain position. How many additional objects would we need to add in order to position more than one object at any position on the screen? And how complex could this solution be?

Another way that we can create overlayed independent animations is by using groups with semi-transparent backgrounds in the other groups. Well. (I need to say, that this works on the Simulator pretty fine, but it definitely needs to be tested on the real devices).

This is how interface objects hierarchy will look.

You’ll need to have a set of images with objects on the second layer (asteroids). If you insert animations of two or more of them you can freely choose which one should appear and when.

UNPAUSE

You: Well, we can do it. We would need to handle collision detection somehow, and we would need to have a LOT of images, but, theoretically it should work.

Some guy: Theoretically?

You: Yes, we will need to check it on the real device first.

Can we post it?

Some guy: So can we post it?

You: No, we need an iOS App.

Some guy: Sorry?

You: You cannot publish a WatchKit App only. You need to have an iOS App, which will contain an additional WatchKit App in it.

Some guy: Why didn’t you tell me that before????

You: You never asked. I thought you read this before you asked me to make a WatchKit App.

Interoperation between Main Application and WatchKit Extension

Later..

Some guy: I have thought about the thing you’ve told me. Correct me if I’m wrong — we need an iPhone app first, and then, we need to create a WatchKit app, so technically speaking, we need to have two apps?

You: Technically speaking — Yes, there will be two “independent” apps.

Some guy: You are saying that they’re independent. But they should perform the same tasks. Can there be a situation, when there’s some information on the device, and the WatchKit App doesn’t know about the new data that the user just passed into iOS App? Will the user be able to perform the same task, switching between devices, i. e. start a task on one device and then proceed on another?

PAUSE

As you already know from Apple Watch Programming Guide , there’s actually three parts: Main App, WatchKit extension, bundled in the Main App, and WatchKit App, bundled in the WatchKit Extension (which is bundled in the Main App). Right?

We’ll comment on the WatchKit App, since it’s just container for static resources, and nothing more. WatchKitApp just reflects the state that WatchKit Extension holds. So let’s concentrate on Main App (Parent App), and WatchKit Extension.

It will be good to know about their lifeTime, just to understand, what is happening here.

Passing data from Parent App to WatchKitExtension

We need somehow to tell WatchKitExtension that something happened in the Parent App.

First thing you need to know is that we cannot start an Extension from the Parent App. I’ll rephrase it. You cannot start an Extension without user interaction. You can just place data in some place, from where Extension would be able to get that data.

For example, you can create a Local Notification, to ask the user something, and by doing this, System will start your Extension (if it’s Dynamic Notification, and not a Static one of course).

Or you can leave some “messages”(actually files) in the shared directory and Extension will look there, as soon as it is started. There’s already a tool for that called MMWormhole

Or you can use Handoff API.

  1. We cannot launch Extension from our Parent App. All we can do is to try to force the user to react, so Extension will be launched
  2. If you want to “notify” Extension that there’s new data, you can save it to the shared directory. But you cannot be sure that Extension will read that data.
  3. There’s no API, by which you can check if your Extension is running. So try to structure your app in such a way that your Parent App can perform all functions the user needs. And use WatchKit to make it easier.

Passing data from WatchKitExtension to Parent App

There’s really a simple way to communicate from Extension with the Parent app.

So use this method to make some complex computations in the Parent App, or to perform some long running tasks.

// Extension
[InterfaceController openParentApplication:@{ @"command": @”foo" }
reply:^(NSDictionary *replyInfo, NSError *error) {
self.item = replyInfo[@”bar”];
}];
// Parent Application’s AppDelegate
-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
// Do something with request
reply(@{@"bar" : @"This is the most awesome bar ever” })
}}

Still, you cannot force Parent App to come to the foreground. If you call openParentApplication:reply: method in the Extension, when Parent App is not running, System will launch Parent App in the background without any UI.

You can use Handoff API here too.

And you can use https://github.com/mutualmobile/MMWormhole.

Ready to unpause?

UNPAUSE

You: Yes, we can do this with some restrictions. So you need to be a bit more detailed about things you want to do.

Afterword

WatchKit SDK is a strange thing. It’s SDK for a device you have never seen, and never used. But a lot of people are trying to update their apps to be the first on the new market. Since there’s no device yet, they are working with some “imaginary” device that will solve all the user’s problems.

So as engineers we need to be prepared to answer those questions. We need to know what the device is capable of. More than that — if we want to be first in the store — we need to know how we can perform some “simple” tasks like “App <-> App” communications. Apple announced that first devices will be shipped in April, so there’s not so much time left (Remember, you need to have iOS App first :) So it still can be you who will write the WatchKit App that will push UX to a whole new level.

You can download sources of example project here. If you have any questions, feel free to contact Paul on twitter.


Stanfy is an agile software development company. We help others evaluate technology, develop and deploy software systems that tuned to work in mobile and wearable world. Drop us a line if you need a collaboration on your lean technology product or startup.

If you like the post, please recommend it.