Looking for

Tuesday, April 19, 2016

This post is part of a set of short tutorials on Watch. If you want to see the previous post. In this tutorial, you're going to see how you can communicate between your Watch and your iOS app using Application Context.

Get starter project

In case you missed Watch tutorial 5: Watch Connectivity - Direct Message, here are the instructions how to get the starter project.
Clone and get the initial project by running:

[1]: Before sending to the Watch, as a best practice, check is the Watch is paired and the app in installed on the Watch. No need to do a context update when it's doomed to failure.
[2]: You send the Context update. Here your don't try catch, but you could do it and display the error.

You need to import WatchConnectivity to make Xcode happy.
Still in DoItCoach/DetailedTaskViewController.swift call sendTaskToAppleWatch(_:) in timerStarted(_:) as done in [1] (Note all the rest of the method is unchanged):

[1]: You get the dictionary definition of the task that was started on the iPhone.
[2]: You find its matching Task object in the list of tasks in the Watch.
[3]: You assign the startDate defined on the iOS app.
[4]: You make sure you go to UI thread to send a notification for the Watch to refresh its display.

Refreshing Watch display

In DoItCoach WatchKit Extension/InterfaceController.swift in awakeWithContext(_:), add one line of code [1] to register to the event CurrentTaskStarted:

[1]: For the current task, replay the animation.
[2]: Calculate how much is images is already started. You will have a short delay since the task was started in the iPhone and you received it on the Watch.
[3]: As you've seen in Tutorial3: Animation, launch the animation.

Build and Run

You can now start a task from your phone.
The careful reader that you are, will notice that once a task started from the phone is completed, it is not refreshed on the Watch app. That brings us to the next section, let's talk about your challenges.

Challenges left to do

Your mission, should you choose to accept it is:

make the task list refreshed on the Watch when a task started from your phone get completed

remove the bootstrap code in TaskManager.swift. All tasks should be persisted to the iPhone (all the persistence code is already written for you in Task.swift). When the iPhone app launch send the list of tasks to Watch. Whenever a task is added on the phone, send the list of tasks to the watch.

make the animation carries on where it should be when the Watch app go background and foreground again.

Get final project

If you want to check the final project, here are the instructions how to get it.

cd DoItCoach
git checkout step6
open DoItCoach.xcodeproj

Or if you want to get the final project with all the challenges implemented:

cd DoItCoach
git checkout master
open DoItCoach.xcodeproj

What's next?

With this tutorial, you saw how you can send update application context messages from your Watch to your phone. Since you know how to communicate between your app and your watch, you're all ready to make great apps!

This post is part of a set of short tutorials on Watch. If you want to see the previous post. In this tutorial, you're going to see how you can communicate between your Watch and your iOS app.

How does my Watch talk to my phone, and vice versa?

WatchConnectivity framework provides different options for implementing a bi-directional communication between WatchKit and iOS apps.

Application Context Mode: allows exchange of data serialized in a dictionary object from one app and another. The transfer is done in the background. The messages are queued and delivered to the receiving app via a delegate method. One specificity of Application Context mode is that only the latest update is sent (ie: older data is overwritten by the new data). This is perfect if the receiving app only need the latest state.

User Information transfer mode is similar to application context mode. It is also a background mode, message get queued and unlike application context all messages will be sent once the destination app is available.

Whether you send a message from your iOS app or from your Watch app, the method to call is the same on both devices. Similarly when you receive a remote call the delegate method to use is the same. You'll get the "déjà vu" feeling when developing with WatchConnectivity especially for bi-directional messages.

Although, there is a symmetry of usage of WatchConnectivity framework, choosing which option to use (queued messages vs direct messages) really depends on your use case and where do you send it from. Time to dig into the nitty-gritty of Direct Messages.

Get starter project

In case you missed Watch tutorial 4: Animation, here are the instructions how to get the starter project.
Clone and get the initial project by running:

The Use Case

Let's start using WatchConnectivity with this use case in mind. You want to start the task on your Watch and be able to see it as started on your iPhone.
From Apple Documentation:

Calling this method from your WatchKit extension while it is active and running wakes up the corresponding iOS app in the background and makes it reachable. Calling this method from your iOS app does not wake up the corresponding WatchKit extension. If you call this method and the counterpart is unreachable (or becomes unreachable before the message is delivered), the errorHandler block is executed with an appropriate error. The errorHandler block may also be called if the message parameter contains non property list data types.

If I call sendMessage(_:replyHandler:) from my Watch, it has the ability to wake-up my iOS app. How cool!

I prefer to use Direct Messages for actions from the watch -> iPhone. The other way around iPhone -> Watch is less useful, as the chances that your Watch app is active when you send DM from your phone is slim.

Direct Message from the Watch to your Phone are useful to say "hi phone, go fetch me these resources", but bear in mind, they are not queued, so if your phone is switch off or out of range, they fail and will not be delivered.

As a rule of thumb, when synchronizing data use Application Context or UserInfo. We could have used ApplicationContext, but we'll design DoITCoach Watch App to be a companion app of the watch. All the states are persisted on the Phone.

Initialise WCSession

WCSession.defaultSession() returns a singleton object. You still need to define which object will handle delegate methods and activate the session.

Where?

For the Watch app, the best place to do it is ExtensionDelegate.swift as this is the place where the life cycle of the app takes place.

The compiler is now complaining because your class has to implement WCSessionDelegate.
In DoItCoach WatchKit App Extension/ExtensionDelegate.swift afte3r the calss definition, add the extension declaration:

extension ExtensionDelegate: WCSessionDelegate {}

Send DM from Watch to Phone

In DoItCoach Watch Extension/InterfaceController.swift add the method to do the send:

Receive Message in iOS app

Where?

For the iOS app, the best place to do it is AppDelegate.swift as this is the place where the life cycle of the app takes place. You want to be able to receive direct message even when your iOS app is not started.

[1]: You need to dispatch to main thread as eventually we want to refresh the UITableView in the UI queue.
[2]: You get the task name from the dictionary. You find the matching task in iOS app (task name is used as an identifier).
[3]: You set either startDate or endDate on the task itself. When you end the task, you need to issue an event so that UITableView get refreshed.
[4]: You save all tasks.

Get final project

If you want to check the final project, here are the instructions how to get it.

cd DoItCoach
git checkout step5
open DoItCoach.xcodeproj

Build and Run

Before launching the app, delete any previous version of DoItCoach on your Phone.

What's next?

With this tutorial, you saw how you can send direct messages from your phone to your watch.
As you've seen, you can do a lot with direct message: wake up an iOS app but there are still cases where your message won't reach your phone. When it comes to synchronise states between AppleWatch and its iPhone companion app, Application Context or User Info transfer mode are much more suitable. See Watch tutorial 6: Watch Connectivity (Application Context) to learn more.

Animated images is simply a set of images. When you run them quickly, your eyes see them as animated: the basic of cartoon animation :)

Build your images

When I first started on AppleWatch, I searched for this cool ring (that is used in Activity app) in the UI control list without success. It is not part of the start ui controls. You can do such animation but you've got to build your own imagines.

[1]: start an timer that is used to refresh UI display. Note the the Timer UI component can not be associated to a fires method.

[2]: in the fire method, you need to display start button, hide timer info, stop the animation and display the next task.

[3]: to initialise the Group background image to the initial image

Hooray, your timer starts, animate and go to the next task! Victory.

Animation and Watch App life cycle

For the need of testing DoItCoach, we made the timer duration to 10 seconds. In real life, the timer would be of 25 mins.
Your AppleWatch won't say in foreground the whole duration of the task. You need to take care of replaying the animation in willActivate() in InterfaceController.swift. You will have to calculate the remaining time and make it match the image number for your animation. This is your challenge!

Get final project

If you want to check the final project, here are the instructions how to get it.

cd DoItCoach
git checkout step4
open DoItCoach.xcodeproj

What's next?

With this first introduction tutorial, you saw how you can do animation that look like the Activity app on your AppleWatch app. In the iOS app we can also see task in progress by selection the task in the table view. what about if the task has been started with the watch. wouldn't be nice to see it running on both AppleWatch and iOs app?

This is time to talk about WatchConnectivy! See Watch tutorial 5: Watch Connectivity (Direct Message)

Are you a AutoLayout Guru?

Watch apps do not use the same layout model used by iOS apps. When assembling the scenes for your Watch app interface, Xcode arranges items for you, stacking them vertically on different lines. At runtime, Apple Watch takes those elements and lays them out based on the available space.

For Watch, you won't use AutoLayout!!!

AppleWatch Layout is much basic and therefore much easier to use :)

You must use storyboards to design your interfaces. Remember that storyboard is part of the WatchKit app bundle. Everything is laid down in storyboard, you won't be able to access a position coordinate at runtime.
To me, Watch layout looks much more like box driven ie: CSS-like rather than constraints based Layout ie: iOS-like.

Get starter project

In case you missed Watch tutorial 2: Watch Architecture, here are the instructions how to get the starter project.
Clone and get the initial project by running:

Drag and drop the Timer onto your Button group. Oops! what is happening, you timer is out of screen. Remember, by default Group have a horizontal layout, go to the Group under your Button and change Layout to Vertical. You can now see your timer element. For your timer, choose Horizontal: Center, Vertical: Center, Hidden: true, Units: Second, Minute checked, Text Color: Green, Front: system, Ultra thin, 27.

Build and run.
Et voila!

Add outlets

To link UI controls to your Swift code, same technique as for iOS app: use outlet and actions.

Control drag Label to InterfaceController.swift, select outlet, name it taskNameLabel

Control drag Group (the top level one) to InterfaceController.swift, select outlet, name it group

Control drag Button to InterfaceController.swift, select outlet, name it startButton

Control drag Image underButton Group to InterfaceController.swift, select outlet, name it startButtonImage

Control drag Timer to InterfaceController.swift, select outlet, name it timer

Add Shared business model

To add Task business model to WatchKit App Extension, go to Build phases, in Compiled Sources, add Task.swift and TasksManager.swift.

In TasksManager.swift, replace the empty init() by the following one to bootstrap some values into your AppWatch:

Get final project

If you want to check the final project, here are the instructions how to get it.

cd DoItCoach
git checkout step3
open DoItCoach.xcodeproj

What's next?

With this tutorial, you saw how you can layout your first AppleWatch screen, how you connect your UI element to the code to add dynamic effect on your screen. Now it is time to talk about animation: how do you make the ring show timer progress?

This post is the second post of a set of short tutorials on Watch. If you want to see the previous post. In this tutorials, you're going to build your first AppleWatch app: DoItCoach.

Let's start by talking about what is an Watch app...

How does a watch app work?

First thing to know about AppleWatch app is that is always come bundles with its companion iOS app. It's the same idea as the App extensions introduced in iOS8, where you have a main iOS app and you can add extensions. Those extensions are embedded into Today, Shared (depending on their type) component. Like Extension, for AppleWatch app, you create an app project which comes with several build targets.

WatchKit Extension: contains the code needed for your native AppleWatch app. This is the part to get compiled and the binaries get transferred to your watch.

Side note: WatchOS vs watchOS2

As a side note, I think it's interesting to look back and know the differences between watchOS (the first version released in April 2015) and watchOS2 (released in September 2015). An image is worth a thousand worlds:

Now In watchOS 2, the extension runs on the user’s Apple Watch instead of on the user’s iPhone, as was the case in watchOS 1. This is the fundamental change for watchOS2: you can run watch native apps. The separation into Watch app / Watch Extension makes even more sense in the context of WatchOS1 as the binaries were deployed in different physical targets. In watchOS2, there is still the distinction of Watch app (storyboard, resources) and Watch extension (code binaries) although both are deployed natively to the AppleWatch.

The separation between WatchKit App and WatchKit Extension also means that the app's user interface is static and can't be changed at runtime. Adding or removing elements, for example, isn't possible. You can show and hide user interface elements though (I'll tell you more about that in Layout tutorial). This is done this way to save Watch resources so that it doesn't drain the battery.

Having the code binaries deployed natively makes your apps launch quicker, and be far more responsive as you remove the bluetooth latency. It also changes drastically the way you communicate/synchronize data between AppleWatch and its companion app. I tell you more about that in WatchConnectivity tutorial.

You now need to share a common business model between you iOS app and your Watch app. For that purpose, I like to separate the business model in a Shared group. This group is included in both iOS and Watch Extension target so it gets compiled and deployed on both.

Shared business model

Before you start coding,have a look at the shared business model. This model is used in the iOS app and will also be used in the Watch to represent a Task. Looking at the Task protocol:

You should now be able to see your the new target: DoItcoach Watch App, Xcode should have created its matching schema:

You've just created your first Watch app, but if you run the appleWatch screen is all black :(

Let's add a label

In Xcode:

Go to newly created Group named DoItCoach Watch App

Select Interface.storyboard, in the bottom right hand side Object Library search for a Label UI control.

Drag and drop the label onto your main screen

In Attributes Inspector:

in Alignment section, select Horizontal: center

change Text Color to Blue

in Font select System, UltraLight 17

Build and Run

To run in the simulator

Select DoItCoach WatchKit App schema with iPhone6sPlus + AppleWatch - 42 mm as targeted simulators.
The command should start both simulators and launch the iOS app and the AppleWatch app. In Xcode, in the left hand side Debug Navigator, you can see debug information for each app.

Note: Sometimes, Xcode failed to attache the debug process of the iOS app, you can do it manually:

either by selecting the schema that is not launched and run it again.

or by manually attaching the iOS app debug process to Xcode. It quite simple and I think this blog post explained it well

To run on Watch

When you want to run your app on Watch, plug your phone to a USB port, and keep you watch close by. You can install the app on your phone:

either by selecting DoItCoach WatchKit App schema with your iPhone and Paired Watch. This way, you can install the app in debug mode.

or by selecting by selecting DoItCoach schema with your iPhone selected. Open Watch app, in General -> App Install section, make sure Automatic App Install is checked. Once the app is installed on your iPhone, its Watch app will be automatically installed on your Watch. With this approach you won't be able to debug your Watch app but the install might be quicker.

Get final project

If you want to check the final project, here are the instructions how to get it.

cd DoItCoach
git checkout step2
open DoItCoach.xcodeproj

What's next?

With this first introduction tutorial, you saw how the Watch app is architectured:

You also created you first AppleWatch target, see how to build and run the apps on simulators or iPhone/paired Watch. You are now ready to add more UI controls on your watch screen. See Watch tutorial 3: Layout.

This post is the first post of a set of short tutorials on Watch. In these step by step tutorials, you're going to build your first AppleWatch app. Yay!

I'll guide you through. No prior knowledge on watchOS2 is required, but some basic iOS development skills (storyboards usage) and Swift language knowledge are assumed.

When designing for an AppleWatch, you should keep the features simple. Bear in mind they'll have to work at on a 312 pixels wide by 390 pixels tall screen for a 42 mn watch. Don't try to fit too many features with a complexe screen hierarchy. Also, remember that your watch app comes with its iPhone companion app. Therefore, you don't need to fit all the features in the watch extension: a well chosen subset will do well.

Let's start by talking about the app, you're going to build...

DoIt Coach

Have you ever wonder how to get more things done during the day, how to stay focus on your tasks? After all, getting your job done efficiently gives you more free time ;)

Based on a well known time management technique, with DoItCoach, you break your day in small tasks interlaced with small breaks. DoItCoach's main goal is to be more efficient and stay healthy.

Start the day, planning the list of task to be done. For the planification use the iPhone app. Add one task followed by one break. After a 3 of those add a longer break.

Let's spice it up: since you want to stay fit in your life, you're going to try to do something physical during your breaks.
Shorter breaks could be perfect for some weight lifting or curls ;) while longer breaks could be used for outdoor walk or short run.

The iOS app

Since the goal of this tutorial is about Watch app, you'll start with an initial project. All source code is available on github DoItCoach project for the final project.