Featured • watchOS • 39:16
Building Watch apps with Xcode 7 is easy and straight-forward. Learn how to build and debug a Watch app for watchOS 2 from the ground up. Walk through adding a Watch app to an existing application, building the user interface, debugging and profiling with Instruments, and discover how to efficiently communicate between iPhone and Apple Watch.
Speaker: Neil Desai
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
(Applause)
[Neil Desai]
Hi, everyone. Welcome to Building Watch Apps. My name is Neil Desai, and I am an engineer on the watchOS program. Today I am going to show you how to build a watchOS app from the ground up. So we're going to talk through the architecture of a Watch app, we are going to walk through the app life cycle, the layout model. We are going to use those different interface elements to provide a great experience for your user. We're going to debug the app, and we are also going to enhance our app using various watchOS 2 features.
So, on the Apple Watch you have four opportunities to present information. From the Home screen on Apple Watch to where it can manually launch your Watch app. The Watch app offers an in-depth user experience and where you will focus most of your development efforts. You can optionally provide a Glance, which offers the most timely and relevant data from your app in an abbreviated form.
You can also provide a custom UI for your app's remote and local notifications. Apple Watch takes advantage of the existing interactive notification support on iOS to allow the wearer to respond with a specific action, transition to a specific area of your app, or later view the notification in Notification Center on Apple Watch. And lastly, you can add -- now you can add a Complication to display information on the clock face, and this is one that I am really excited about. This is great for small pieces of information that the user may want to know while viewing the time.
So now that we know the different ways the user will interact with your app, let's discuss the architecture of a Watch app. Your existing iPhone app contains your WatchKit extension and Watch app. These both get installed on the Watch after initial pairing at the user's request or a later user-initiated installation via the Apple Watch app.
The Watch app is comprised of two pieces, first, the WatchKit extension, and this extension now runs on Apple Watch and executes code in response to user interactions. The second piece is the Watch app. Your Watch app's UI is loaded from a bundle containing your storyboard and static resources such as images.
So this UI, which lives in the Watch app, then talks to your WatchKit extension, where you can place all of your logic. Now that the extension lives on the Watch, you can enable all sorts of new capabilities, which I will get into later. And in the moments when your Watch app needs to communicate with your iOS app, there's a new framework called WatchConnectivity that you can use to offload heavy processing or networking tasks, and WatchConnectivity lives on both iOS and watchOS 2 so you can communicate back and forth.
To summarize, your storyboard and all of your static resources are stored in the Watch app bundle. The Watch app target provided to you by Xcode already contains the storyboard you'll use during development. Because these resources are part of the Watch app, they must all be specified at design time and can only be modified slightly at runtime.
This means you need to incorporate all of the UI elements that you might need into each scene of your storyboard. At runtime, coding your WatchKit extension can update those UI elements, including hiding, showing, or animating them as needed. Additionally, your WatchKit extension can send dynamically generated content over to the Watch app for display.
So in Interface Controller manages the UI elements for a single stream of content. It sets the initial values of those elements, responds to user interactions, and updates the UI as appropriate. When you create your Interface Controller subclass, you add outlets for each UI element you need to manage. These outlets connect to objects in your storyboard and handle the transfer of information between your WatchKit extension and Watch app.
In addition to outlets, you set up action methods for the interactive elements of your storyboard, and as with outlets, you connect each action method to the corresponding UI element. And when the wearer interacts with that element on the Watch, WatchKit will then call your action method. So let's get started thinking about how to modify an existing iPhone app.
So in the run-up to WWDC, I have been a little forgetful about contacting people such as my family and friends, so I wanted to create an app that reminds me who I have contacted and who I haven't, and it's called WWDC Call Me Lately and helps notify me when I should contact them.
So I communicate with my server to keep track of the people I need to contact via NSURLSession. This is a great iOS app I made, but really, it makes even more sense as a Watch app. When I am busy, I quickly can know who I need to contact next. Let's get started making our Watch app.
So what I want to do is I want to show you how to add a Watch app target to an existing iOS app, and then we are going to walk through where to get started configuring your interface. And lastly, we are going to build and run to see how things look in the simulator.
So speaking of the simulator, we actually have a brand-new simulator in the watchOS 2 SDK. So you guys are all probably familiar with the iOS simulator, and it looks something like this. And now we have a new Watch simulator. So what's great about this is it's a fully fledged simulator, so we can easily look at our 38-millimeter and 42-millimeter sizes. We can simulate touch pressure.
We can also look at different clock faces. So like I mentioned earlier, there's now the ability to support Complications in watchOS 2, and so you can look at how your Complication views in different clock faces in the simulator. And lastly, you can have multiple apps installed at the same time. So we think this is going to be a really great way for you to easily make a new project, iterate on an idea, and then quickly go back and forth between different apps.
So to simulate touch pressure, you go into the Simulator menu, and you go to Hardware, Simulate Touch Pressure, and then you can select the Deep Press. This is useful for things like wanting to change the clock face in the sim. Now let's jump into Xcode and see how we can add our Watch app and use this brand-new simulator. So let's go to demo.
Great. So let's launch Xcode. And let's select our app. And let's make this a little bit easier to view. Great. So now if I wanted to add a new app, I would just go to File, New Target, and then I now have a new watchOS pane, so I have an application and framework in library. I select Framework In Library so I can add a Watch framework or Watch static library.
For now I am going to add a WatchKit app. I double-click, add a product name, and my main app is always going to be enabled by default, but I can also include a Notification scene, a Glance scene, and a Complication. So I can add those here. I already added it to this app. Let's just press Cancel.
Now let's see what we are given. Here is our Watch app, and here is our WatchKit extension. Like I mentioned before, our Watch app contains our storyboard, all our static resources, and our asset catalog. Our WatchKit extension will contain all our interface controllers and any other code we might want to add. Let's go into the storyboard.
So now, as you can see, I have a main interface, I have my Glance interface, and then I have my static and dynamic interface for my notification. Now, let's also go into the utilities inspector, and I can look at the object library and see that I have a lot of different objects here that I can use, and I have all my WatchKit interface elements as well. So let's just drag and drop an interface controller over.
For instance, if I wanted to very simply make a page controller, I could select Control on my keyboard and then drag over to my new interface controller and create a segue for next page. So for now, we'll just get rid of that, and if you see below, there's a static interface and a dynamic interface, so the dynamic interface is actually what hooks up to your Notification Controller class.
So when your user first raises their wrist when they get your notification and then they see it, your Notification Controller class will get called, and you can provide a very rich UI there. Now, your static interface is typically going to be invoked when the user, for example, misses your notification and goes to view it again in the Notification Center, and that's typically when you will see the static interface.
So also, I have this My Category here. So for different categories that we send for different payloads, we can actually create custom UIs and custom notifications for those different categories. So it's not just one interface for one -- for all of the notifications you send. You can actually modify this and create multiple interfaces. So let's just add a new notification controller.
Let's place that here. So if I wanted to, I could drag over an interface controller and then, just like I did before, I would Control-click, and then create my new dynamic interface. Or I can also just go here into the attributes inspector and select Has Dynamic Interface. So now I can modify my category and I can create a new interface, and then I possibly would want to create a new Notification Controller class and then hook that up as well.
So also we have given to us by the template a push notification payload .apns file. If you see here, this looks just like a normal payload we might have. It has an alert, a body, a title, a category. What's great about this is in the simulator, we can use these different payload files and mockup different interfaces. If we want to test out all of these different notifications, we don't have to just do it to device. We can now quickly iterate on different ideas using the simulator and these notification payloads.
So let's go back to my storyboard. So now I just want to build and run and view my main app in the simulator. So I am going to go up to my schemes, and I see I have my iOS scheme and then my WatchKit app scheme. And then I have two simulators here, an iPhone 6 Plus with a 38-millimeter and an iPhone 6 Plus with a 42-millimeter. As I mentioned before, this is actually a full simulator for the Watch. It's actually going to be paired with my iPhone sim, so let's select the iPhone 6 scheme, and let's build and run, and now I have two simulators open.
So what I expect to have happen is that the main app will launch and I will get attached to it in the debugger. So great. So let's press Allow, because I do want to allow this. And now I am attaching the debugger, and I have a small thing that has appeared here, it says "extension received request to wake up for complication support." So if I were to implement my Complication, this would later go away.
So now let's just stop this debug session. Let's go back to our Watch sim. So here's my new simulator. I have my clock face. Now if I really just wanted to change out my clock face, I can go into Hardware, Simulate Touch Pressure, and now I can simulate a deep press, then I can go back to a shallow press, and then I can modify my clock face.
Now I can customize my clock in the simulator. So if I were to go to this screen on a device, I would use my Digital Crown, and then I would easily change the color. So there isn't a Digital Crown on my mouse, but thankfully I have a scroll wheel, and I can use the trackpad or Magic Mouse, and I can change the color as such.
Now I can simulate the Digital Crown behavior. Also, when I am in my Complications view, as I swipe over, I can then enable my Complication that I have, and so if I am iterating on Complication ideas, I want to build some new Complications on different faces, I would just enable these Complications as such.
So for now, let's just go back, and let's do Command-Shift-H for home, and let's select and then do Command-Shift-H again. So now I am at the Home screen, so I have my Settings app, my clock face, and the app I have. And then I can launch my app and see it as I expect. So let's go back home.
And now, like I mentioned, these sims are paired. I have two different sims to choose from. I go into schemes and I see my two sims. But what happens if I want to have an iPhone 5 sim with a 42-millimeter? I am going to make a new sim and have it paired for me.
So let's go into Xcode and then select Window, and then Devices. And so now in my sidebar, I have all of my different simulators. So if I go to the bottom left, I can then add a simulator. So I can call it iPhone 5. Change my device type. I will keep my iOS version. I will change the paired watch to be a 42-millimeter, so I can easily create that. Now if I go back to my scheme, there's my new simulator. So let's go back to the storyboard.
And so as I mentioned, there's these schemes that we can pick from, so there's a Notification scheme, there's Glance scheme, and a Complication, so I can select these different schemes and quickly debug those parts of my app. And now, for example, I want to just test out my Notification scheme, I want to see how it looks in the simulator. We already talked about the APNS files. I want to see that appropriately appear.
So let's just add a label to my dynamic interface. And let's drag it over. And let's just rename this Dynamic. So now I selected my Notification scheme, and I am just going to build and run this to the simulator. So what I want to have happen is on the Watch sim, my notification will appear, and then it says Dynamic, and that's all it should say for now.
So here's my notification, and it says Test Message, so I am going to look into my console. It says "took too long to show custom notification. Falling back to static." Okay. So let me look into my notification controller and see why that might have occurred. Here is my code for my notification controller. I go down. Okay, so my Did Receive Remote Notification is commented out. I should probably uncomment this.
And now let's just build and run again. So now hopefully, after I dismiss the old notification, I should then see my new one, and it says Dynamic like I expect. So on device, if I was in this scenario and got a notification, I could select the App icon or button and I would be taken right to the app. So I can just do the same thing here in the simulator, and I am taken right to the app as I expect.
So now I could flesh out this code and add a great UI story in my notification, and I still want to be testing my static interface. The only way I just saw that happen was I had to comment out Did Receive Remote Notification to get my static interface, and I don't want to be doing that while I am iterating on my code.
So there's a couple ways I can do this. One, I can change this custom in my completion handler to call Default, or I can actually go into my schemes, I can edit the scheme, and now I can choose my Watch interface. So I can change it to be a static notification rather than a dynamic, and as you can see, here's my push notification payload. So if I had multiple payloads, they would appear here, and I can quickly change out which payload I am trying to test.
So for my purposes, I might want to keep a static scheme around for a while, so I am just going to duplicate this scheme. Let's call it Static Notification. Then let's go to Run. Then I change my Watch interface to be static. Let's press Close. So now I have a static notification as well as my normal dynamic notification.
So this is great, and we have the beginning of a template project here, but let's understand some context of how the app life cycle works on watchOS. So let's go through the life cycle of a watchOS app. So we looked at the interface for the Watch app but haven't yet observed its life cycle.
It's important to know when the extension and interface controller get called for setup and tear-down so you are configuring UI elements at the right time, as well as performing cleanup operations or finishing tasks when the wearer stops interacting with your Watch app. When your Watch app is launched, the extension delegate will get Application Did Finish Launching. The extension delegate is analogous to UI application delegate on iOS. As such, you will also receive an Application Did Become Active callback to allow you to update your model data.
Next, the storyboard in your Watch app bundle is loaded on Apple Watch, and the initial interface controller is created. Once the UI is initialized, your interface controller will receive an Awake With Context call, giving it a chance to initialize variables for state and configure UI elements with their initial values.
When the UI of your Watch app is in the process of getting displayed on screen, Will Activate will be called, allowing you to perform any last-minute tasks or final UI element configurations. And now, as the wearer interacts with UI elements, WatchKit calls the action methods you defined on your interface controller.
Those action methods are your chance to execute code and update your app's UI. Keep in mind that engagement times are short. On iOS, we traditionally think in terms of our user interacting with our app in a sense of minutes, but on watchOS, we are going to be thinking in terms of seconds. Actually, we are going to hit a Did Deactivate call after the wearer stops interacting with our app. And after that method is called, then at some point in the future, the interface controller will then get deallocated.
And now our extension delegate, after the wearer stops interacting with the watch completely -- for example, they might go back to the Home screen -- you will receive an Application Will Resign Active callback, and so this is your opportunity to save any app-related state information and close out any ongoing tasks.
So we know the life cycle of the WatchKit extension, but another important thing to talk about is the layout of the Watch app. So the model in watchOS for WatchKit is different from UIKit and AppKit because it's a flow-based layout. So all of our elements go one after another, and our UI is primarily created in Interface Builder.
So when we have an interface controller, we can use our object library and just drag an image or drag a button, and then in the instances when we want to have a slightly more involved layout, we could use a WK interface group, which will just manage the layout for different items. So for example, we could add an image and a label into the group. And so the programming model is really important to note. You don't write object creation code with WatchKit. You have fine-tuned control in the Interface Builder of positioning and sizing, the layout hierarchy, and animation.
And I highly encourage you to go to the Layout and Animation Techniques talk for WatchKit on Thursday. It's going to be a great session on how to create really rich UIs for your users. And so I've been thinking about how best to make my Watch app, and if you recall, my iOS app is just a simple UI table view.
So my first idea was to have a list of all the people that I need to call, very similar to my iOS app, and when I started thinking about it, I realized I don't need all that information in my Watch app. Really, I just want to know who next to call rather than knowing a list of all those I should call. So my UI might look something like this.
So great. Now I have an idea of what I want to make, and I want to start creating my UI. So I am going to walk through how to create your UI for your main app, your Glance, and your Notification. So let's jump into Xcode and see what we have.
So we understand what the life cycle is and generally speaking what the layout is, so let's go into our storyboard. If I wanted to add an image to my interface controller, I would select it here in the object library, and then I can just quickly drag an image over, and now I just want to add a label as well. So let's just find the label and drag it over.
And if you notice, a blue line appears, so in my flow-based layout, I can position my label to be after or before my image. So let's place it after. Now, for instance, if I want to place them horizontally next to one another, I could -- okay, so I can't actually do this in Interface Builder. There's no blue line that appears to help me out.
So let me use a WK interface group to do this. So let's drag this over, resize our image a bit, and now let's add the label into the group, and then let's also add the image right next to the label. So this is how it can position things horizontally.
So for now, let's take our label out and our image out, and let's just delete the group, and then in my attributes inspector, already in my asset catalog, I have an image for my friend, Ethan. So let's just reference that image. Ethan looks a little squished. Let's make him look a little bit happier.
There we go. He seems pretty happy. So let's also change the label text to be Ethan. Now my layout is positioned -- it's left-aligned, so I actually want to position it centered in the screen. So if I just select on my image, then go to my attributes inspector, and I can change the position to be in the center.
Then I can also change the position vertically, I can change the size, I can change the view for this particular image, and I can do the same for my label. I just want to position it in the center. Now let's move on to the Glance. If you notice, this Glance controller looks different than our main app. There's two groups here. That's because Glances are actually templated.
So I have an upper template and a lower template. So if I select in my attributes inspector my upper template, I have a list of options that I can choose from. So for this particular case, let's just have a large text, and let's change it to be Call.
Then let's add an image to my lower template. I am going to do the same thing as before, just change the image to reference Ethan. So when I look at my Glance, I really kind of want the app to scream at me because I am really bad about calling Ethan, so the app will help me do that. And so also I can do the same thing for my notification interfaces as well. So for the most part, I figured out my new interface, and now let's just build and run the interface by selecting the schemes. Let's build and run to the Watch sim.
So I just expect my new interface to appear. Okay. Great. So there it is, and I have attached in the debugger. So let's just stop this session, and now let's take a look at the iOS sim because it's important to note these are paired devices. So I scroll up, and I pan to the right.
I see I have my iOS app installed, so just like on a device, the only way your app will get to a user's Watch is by also being installed on the iOS device. And so this is what's happened here. Now let's select our Apple Watch app, and in the simulator, I can change out my Complications. So Call Me Lately is already enabled. I can also uninstall my app and reinstall it, and I can also disable my Glance and reenable it.
So if I wanted to view my Glance, I could go in the simulator, go back to my clock face, and just like on device, I swipe up, and then my Glance -- so let's just reenable it. So -- oh, excuse me. So I would expect my new UI to appear. That hasn't happened, but let's just move on for now. So I figured out my new interface, but I need to know who the next person is, and I need to figure out how to update my app to do this. So let's go back to slides.
So my iOS app mainly communicates to my server via NSURLSession to get my list of loved ones. What's great is in watchOS 2, I can use a lot of the same NSURLSession code to talk to my server. However, there are times when I need to make sure that my iOS app and watchOS app are in touch with each other, and I can use WatchConnectivity to do so. So let's take a moment and talk about the new WatchConnectivity framework before we see how we can use it in Call Me Lately.
So WatchConnectivity is a framework that has four core uses for communication between iOS and watchOS. An important thing to note is that WatchConnectivity lives on both iOS and watchOS, so you can use APIs on both platforms. This isn't just your Watch app talking to your iOS app when there are times when you want your iOS app to be updating your Watch app.
So the first use is to allow your iOS app to update your Watch app's application context, and vice versa. This may not be urgent data, but something you know you'll want to update your user with. The second is Send Message. This is for small pieces of information that I need to transfer back and forth between my iOS app and Watch app. And the third is a simple transfer file API. And lastly, there's a transfer user info API that ties in more so with Complications.
There's also a wider range of things you can accomplish, and I encourage you to go to the talk on WatchConnectivity to find out more information. Let's see in Call Me Lately how I can best use WatchConnectivity. I just want to add WatchConnectivity to my app, I want to debug it, and lastly I want to just profile it with Instruments. So speaking of Instruments, for those that don't know, Instruments is a powerful profiling tool to check out the performance of your app.
So you will get a profiling template when you first initially launch Instruments, and you can pick from a time profiler, leaks, allocations, and then you will see something, if I select a time profiler, for instance, this is the view I would see, and I can easily profile my app.
And it's really important, so engagement times are short on the Watch. So this means that performance is critical, and we want to be using Instruments to make sure that our app is giving a good user experience, and we want to check it out on both device and simulator.
So now that we know a little bit about WatchConnectivity and Instruments, let's go into demo again. So in my app, I might have an algorithm of sorts that says, oh, this is the next person I want to call. And so when I have this data available, I want to update my iOS app to know who is the person that I am viewing on my Watch and the person I should next call.
So I might want to maybe on the initial launch of the app just update my application context over to the iOS app and see that appear. So I would probably just want to do this on my Application Did Become Active in my extension delegate. So here I could just place this code. So let me first import WatchConnectivity.
And now in Application Did Become Active, I would add some code, just create a context that's a string named Ethan, and then on WC Session, which is part of WatchConnectivity, I have the default session and I call the method Update Application Context with the key WC Application Context, and I would just send over my context. And worse comes to worst, I am just going to print my error for now. So let's see this work. And let's build and run.
Let's select our Watch app. My app launched, but I see a WC error domain, 7004, so I know from previous experience that my session just isn't activated. Let's take a look at my code. I also haven't set my delegate either, so I want to set a WC session delegate, and then after -- or before my update application context code, I just want to set the delegate to be myself, and then I want to activate my session.
And it's important to note that you want to set the delegate before activating your session because you might have messages waiting for you when you've activated that session. So you want to receive the appropriate delegate callbacks. So now let's just build and run again. Now let's just make sure that my error code has gone away.
Okay. Great. So I don't have my error anymore. So that means this is appropriately sending my application context to my iOS app. But now I want to see that my iOS app actually receives it. So I can do this by going to Schemes and selecting my iOS scheme, and I am going to select the same simulator that's paired with my Watch sim. So I'm going to select my iPhone 6 sim.
Now if I click on Control and then press on Build and Run, I can actually run without building, so this way I don't have to -- if my app is really large, I don't have to wait for it to keep compiling and install the app, et cetera, et cetera. So let's just press Control and then run without building.
So now it looks like my app launched, and I've hit on my app delegate in my iOS app, I've hit my delegate callback, Did Receive Application Context, and I can then update my UI in my iOS app. Let's just continue through. So now if I am on my Watch sim, and let's go back home.
So my debugging session is still ongoing in my iOS app, and my Watch app, when it's activated -- excuse me -- when it's launched, it will then update my application context. So I can hit it again on my iOS app and hit the delegate callback. So this is great because you can now debug in both simulators at once, which is really great for when you are using WatchConnectivity, so you can make sure all of your calls are occurring in the order you want them to on both watchOS and iOS.
(Applause) Thank you. Let's just stop the session for now, and I just want to quickly profile an Instrument. So I'm going to hit Product, Profile, and I am going to have Instruments come up, and I expect my template to appear, my template selection. So let's find my -- okay.
Let's find my time profiler. Let's just choose that. And then instead of my iOS sim, I actually want to be debugging my, profiling my extension. Select that here. I can press Record. And it looks like I've hit an error. So let's just open up my Watch sim, place that here.
Press Record. And there I have attached, and now I can profile my app. So in my call tree, I can also see my sample list just like on an iOS app, I can do the same sorts of things, and I can see that most of my code is being run on my main thread here, which is great, and then I can go back to my call tree.
And so this is just very simply you can easily profile something using Instruments. So let's just stop this session. And now I've written an app that basically has a very simple interface and uses WatchConnectivity, and I've profiled my app in Instruments. Now I could talk with my designer and really flesh this out.
So let's talk about what we were just able to do. So we added a Watch app to our existing iPhone app. We created an interface for our app and Glance. We used WatchConnectivity. We debugged our iOS and watchOS apps at the same time. And we profiled our app in Instruments.
So these are just a few of the things that you can get started with in WatchKit, and there are so many other watchOS 2 features that I am incredibly excited about, and like I mentioned before, there's Complications, and as you might have seen in the Keynote yesterday, you can even do Time Travel, which I think is just the future.
You can just, like, go back and forth. It's amazing. It's so cool. And then you can also use Digital Crown, so you can create really intuitive experiences for your users using the Digital Crown. You also have simple animations to create really responsive apps for your user, just nice and playful.
You also have access to the heart rate sensors, so if you are making a Workout app or some sort of fitness app, you now get that data via HealthKit and can use that to provide a better experience for your user. You can also provide a great experience using haptics and lightly tap your user and provide some context as to what you are doing in the app. And there's also media playback, so like you saw in the Keynote yesterday, you can view a simple video.
And you can also have audio playback as well, so this would use the speaker on the Watch, or if the user has paired Bluetooth headphones, it would then route there as well. And you can do audio recording, so for the first time, you can get access to the mic on the Watch itself and provide a great experience. And you have access to the Keychain now, so you can provide a really secure experience for your user.
So in recap, we were able to discuss the architecture of an app, we added a Watch app, we talked through the app life cycle, we used the different interface elements and we made our app and our Glance, and we talked through our notification interfaces and how to easily iterate on different notifications and using different categories and different payloads. And we were able to build and run in the new simulator, and we debugged our app. And we were able to use various watchOS capabilities.
So there's some great documentation online, the watchOS 2 Transition Guide is excellent. I highly encourage you guys to all check it out. There's some great technical support. You can check out the Forums, some sample code. If you have general inquiries, please contact Jake Behrens, our watchOS framework's Evangelist.
There is a lot of really amazing sessions happening this week, there's a lot of info to dig through, WatchKit in Depth, Complications, Layout, and so much more. If you have any questions, if you have general questions or you're digging into the code for a first time, or if you are an experienced WatchKit dev, please come by, we will be happy to answer any questions you might have. So thank you, everyone. (Applause) Thank you.