App Frameworks • watchOS • 30:20
watchOS 2 provides a new architecture that runs your WatchKit extension directly on Apple Watch. Take an in-depth look at how this new architecture works. Learn how to migrate existing WatchKit apps to watchOS 2, how to support both versions of watchOS, and hear about updates to Glances and Notifications.
Speakers: Andrew Platzer, Forest Hill
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
[Andrew Platzer]
Good morning, everyone, and welcome to WatchKit in Depth Part 1, the first of two sessions to explore a bit more about the changes in WatchKit. My name is Andrew Platzer and along with Forest Hill we will be covering some topics today hopefully of interest to you.
I'm going to be covering three sections, architecture, the basic layout of how a watch extension works inside a watch app. I will talk about where resources and data live because it is a two-part system, and so it may be a bit confusing to start with. For those who have already worked on watchOS 1, a WatchKit extension, a watch app, I will talk a bit about migrating over, what's changed, what's the same, and then Forest will carry on and talk a bit more about some of the new API and the new classes in WatchKit.
So your Watch app has three parts. It's got an iOS application, and that is what you would get installed on the phone, as well as that, you provide a WatchKit extension, this is the code you write, and then a watch OS application which contains resources and interface description.
You have your phone and you download your app, and what we have done is added a new bundle of data, bundle of files that get along with, install along with it, and that's the Watch app, that contains your interface description and maybe some resources, and then your WatchKit extension which contains code and additional resources. So when you pair it with your watch, we copy over all of that information, and it appears on the Home screen as a full application.
Of course, a copy is still left on the phone for later in case it needs to be reinstalled. So I want to talk about the two parts that you are going to be providing. One is the interface and the other is the actual code and the extension. When you create a new WatchKit App, you can see there are actually two separate targets, two separate components, the app with the interface storyboard and your code, in this case in Swift, and you have up to four different things you can add code for.
So for the storyboard, this is the interface part of it, you can edit in IB just like you would an iOS application, and we provide a reasonably rich set of interface elements, labels, images, et cetera. This is all in watchOS 1 and we have added a couple of new ones in watchOS 2, the thicker view, which will give you a lot more rich interface, and the movie view.
So, for example, here are three standard possible controllers with all of the controls you can see, and these are created in interface builder. We also have custom interfaces for specific functions, one is Glances, and it's got a more specific layout and the other two are for notifications. One is for static, one is for more dynamic information which you provide at run time.
So as I said, there are four roles for your extension. There is an application role, so when your application is launched from the Home screen, we call your extension. It's also used for the single page glance that appears from the clock, notifications when you receive one, and now complications.
And for each of these, there is an associated controller, for the Glances and the regular application, there are WKInterfaceController. There is the specific sub class called WK user notification controller which you should use for notifications, and there is a new data source object CLK complication data source. I won't talk anymore about the complications. There is a session later today that will go into great detail about this.
So your WKInterfaceController is your main connection to your interface. The main thing it does is it provides automatic creation of interface properties so you tag an interface element in your interface design and say this is like, for example, my label, and then we create an associated object on the controller and we connect it up automatically for you.
The controller also supports menu handling. You can customize menus or have static ones. We provide navigation, push and so on, or paging, modal presentation of controllers and alerts, and action sheets. One other thing we do provide is a number of system UI sheets, text input, video play back, audio recording.
So, for example, here is a very simple interface controller class. It has one outlet, app image and we have loaded up in IB and when we run the application we see it pulls the image and displays it on the screen. So I want to talk a little bit about the location of stuff, where your resources live, where you can pull your data from.
Because there are two parts to this watch app, there is the watch app itself and the WatchKit extension, there are two places where data is stored, there's the WatchApp bundle and the WatchKit extension bundle, and you have got to be sure you remember where it is. For example, here I have created another project, it has an interface storyboard as before and also an application image.png and the localized string file and the extension itself also has the same kind of thing. It has another .png file and a localizable string.
So now if we do the obvious, which would be create a couple of outlets, wire them up using IB outlet to indicate that these are the properties, and call set image, well, we don't get an extension image. The reason for that is that when you call set image named on a WKInterfaceImage, it doesn't look up in the applications bundle, but the extension image doesn't live in the applications bundle.
So instead, what you need to do is directly fetch it in the code that's running in that particular bundle. So in the extension code, you call UI image 'image named, and it will pull the image locally since it knows how to search inside its own bundle. Then you send that across. You call set imagine instead of set image named so it will pass over the image and both will appear in your application.
So now you want to store some data. You don't want just pull some static images. You have a much more dynamic application, so there are two folders that are of interest. One is the documents folder, this is where you would store more persistent information. It's not purgeable. That means it hangs around between reboots, et cetera, but one thing to note, it is not restored, so you may have to check for that if the watch was erased or you go to a new watch. There is also a caches folder. This one is purgeable, so if the system decides it needs more storage for music or pictures or other apps, it will remove those images, so you should consider those may go away at any time.
And so here is a quick example of where to find it. In this case, the main call here is the document directory class file manager for URLs for directory. It will ask for the first one and that's the URL. So we can create a URL including our file name and write some data to it.
Now, media presents another interesting problem because the application is in charge of playing the media, and it's also in charge of recording it audio somewhere. So when you, your extension requests to play a movie or play audio or record audio, we send that off to the application. On the other hand, the extension is in charge of getting that media, downloading it from the internet or generating it or whatever, and it's also in charge of getting the recorded audio files that you may have set up and sending them out to your server.
So what you need to do is set up what's called a shared container. And that basically lets both processes, the application and the extension have a common place to access. Because for security reasons, normally you can't access from one process into another's storage area. And you enable this index code. You use something called 'app groups' for both the extension and the application you give a unique identifier and that's your application group.
And so the only real thing here you need to worry about is there is a single function, again, the file manager, you can ask container URL for security application group identifier and you will pass in the group identifier that you created and that will give you URL to a shared storage area.
So from there, you can save files that the app can read from the extension or pull files that the app wrote into the extension and an example here we have a present audio recording controller and that takes the URL from the shared container, so it will record the audio to that file.
So now, I want to talk about getting the data to the watch. There are two ways of doing that, one is NSURLSession, whichs in foundation. And the other is a new framework called watch connectivity. So the NSURLSession is the one you use a lot to get stuff from the internet, if you have got a chat app or something like that, you will want to talk to your server, it gives you direct access to the internet using HTTP and HTTPS and there are several ways of configuring it.
And it allows for backgrounds and downloads. The reason for that is your extension is often not running, your watch screen is turned off, your extension is sleeping, your extension may not even be running for awhile that it takes to download the data. One thing to note is once we tell you the data is there, once the file is completely downloaded, you need to grab it right away, because otherwise it will be removed from a temporary cache.
So here is some code, a couple of pages, a simple downloader class. The first thing we do is cut an NSURLSession, we do it lazily in case we don't need to ever call it. And the main call here is to create the session. And there's really two lines.
One is you configure it for the background, so you want to say here is a background session, and we want to pass in an ID, and I'll talk about that in a moment, and we create the URL session with that configuration and we make the downloader class itself the instance after a delegate so we get notification when the file comes in. Then to start the URL download, we ask for a new task and tell it to go, and it will send off the request and start the download.
So as I said, often your extension is shut down or not awake while that's happening. In the case where it's shut down, you will want to reconnect to all of those download tasks you have set up. So what you will set up is, for example, a function here called restart that you might call from your WK extension, and Forest will talk about that, at start up to restart the download so that you know they are coming. Actually they will be going, but you won't know that they have finished.
And so here is the actual delegate method. This is the one you are wired up to the NSURLSession, and it's called when the file finishes downloading, and as I said, we need to copy the file immediately so it doesn't go away, so the system doesn't remove it. In this case, we will get the caches directory because maybe we will need it a short while.
We will generate a URL, and we will call the file manager to copy that from the original URL that passed in, the location URL to the cache's directory URL. So we have a copy of it and we will keep track of it for later so we can access whatever the data is you downloaded.
There is also the WatchKit connectivity framework. It actually exists on both sides. If you share data between them so, for example, you could set up a dictionary that could share between the watch and the iPhone. It lets you transfer files over, again, the background, and it lets you send direct requests from the watch to the phone app your parents, your parent iPhone, parent application. And there is a session on that tomorrow, and I suggest you definitely listen to it because it is new and does exist on both the watch and the iPhone.
So just a few slides on migration, for those of you who have started a watchOS 1 app, you have probably run into this. The WatchKit extension for watchOS 1 is something you have created already, there is a target in your project, but it uses the iOS platform in SDK. So it lives on the iPhone rather than on the watch.
Because of that, it lets you share a framework on the phone, so you might have some code that's common to both the extensions, for example, fetching your information from the network, and you will have that same code being run by both the iPhone application and the watch application.
Because you have no direct storage access to the watch, we gave you a way of caching images of basically saying here is an image and here is a name and later on when I say 'said image named, we will have already downloaded that asset, those resources to the watch, and it will be much faster. So we gave you a way of directly talking to the application. Obviously this is from the phone to the phone process so it's very fast.
With watchOS 2 we have added a new SDK. It's completely separate, similar to the iPhone and OS 10 SDKs. It does give you a subset of available iOS frameworks, so you won't get the complete set of functionality as you do on the phone, but you will get a lot.
You can, in your project, include a framework just like you did on your iPhone watchOS 1 app, but, of course, this framework is downloaded with the watch application, watch extension, and so you don't get to share the code in one device, but it does let you still separate out your network access code into a separate framework that the watch can use. Now, of course, the watch, for example, for watch framework that you provide, for example, might use NSURLSession to access information.
So if you have done a watchOS 1 application, you have actually already actually got a lot ready for watchOS 2. It's got the same API with some changes and additions. But you should be able to compile a lot of it without any changes at all and copy resources over, so if you have images in your watchOS 1 extension you could add them to the target of the watchOS 2 extension and have them copied to the watch. But you do want to make sure they are sized appropriately for the watch.
There are a couple of new things. The main thing, of course, is that your watch app is running on your watch, and so the extension is running, and that means UI responsiveness is much better. You tab a button, and it immediately responds. Of course, you have also got independent operation now and you don't have to worry about the phone being nearby, you don't need to worry about the phone being connected or on the network.
We added a couple of UI elements as well as some new system UI sheets and those will be talked about later in the session and in other sessions. We have added animation, so now you can animate the transitions between changing, for example, the size of a graph or a graphic or a size or something like that or the opacity of a string, you can animate that. There is a session for that tomorrow. So I suggest you take a look at that one as well.
So for controllers, the API is pretty much the same. So you have got the same interface controller and the same, you will use the glance, nothing has changed there and the same for the notification, you don't need to make any changes there. There are a couple of APIs which are no longer there, image caching and open parent, those have been replaced with direct images and watch connectivity. Two new things are the extension delegate which Forest will talk about in a bit, and the complication data source which is used to provide the images and text for complications display on the watch face.
For those having existing Xcode projects, you can add a new target. You can have the watchOS 1 there, you can say give me a watchOS 2 and add files in, mark files as part of the same target, so on, add the frameworks to be compiled and loaded on the watch.
If you want, you can start from Xcode with a whole new project, and it will automatically create the iOS and the watchOS 2 app so you can start it filling in from there. There was a session yesterday, I suggest you take a look at the videos that detail this procedure much better. And that's it for me. So now I will hand it over to Forest who will go into more detail about the new classes and changes to the existing API.
[ Applause ]
[Forest Hill]
My name is Forest, I'm an engineer on WatchKit. I would like to talk to you about the APIs we have added in WatchKit for watchOS 2. First I'd like to talk about WK extension delegate. On iOS we have UI Application Delegate. Among other things, this serves to help you track your app's life cycle, this includes things like your did launch, your did become active. And your will resign active. We have added WK extension delegate to track your application's lifecycle.
First up, I will start with application did finish launching. This will be called once when the application has finishes launching from when your extension hasn't been run at all. This is a great place to perform initialization steps that your application might need, set up notification observers and warm up any services you might need later. It's important to note that at this time your application is not yet active.
Next we have application did become active. This will be called each time your application is brought from the background to the foreground or after first launch. This is a great time to start any code that needs to be running only while your application is actually active, activate any timers you might need and especially update any state that might have changed while your application was either in the background or not running at all.
Application will resign active is called prior to your app resigning being active, moving to the background. You want to prepare to be in an inactive state. If you have started any timers, this is a good place to pause them. If you choose not to pause them at this time, they won't run in the background, you won't get background running time, but you will lose control over the exact cycle in which they run.
So if you want control over that, it's a good idea to pause them when you become inactive and restart them specifically on did become active. And you want to save your application state at this time. Because after this point, your extension may be killed if another process on the system needs that memory.
So in summary, WK extension delegate will help you to track your application life cycle. It's very important to note that this does have not have anything to do with your notification UI, your glance UI, or your complication data. This is only about tracking the application's lifecycle. Next up I would like to talk about handle user activity.
Handle user activity is an existing call from watchOS 1. In watchOS 1 it was used when application was launched by a tap on your glance to navigate to the appropriate location in your application that was reflected from the glance. We are building on that in watchOS 2 in two ways.
First is that this will also be called if your application is launched by a tap on your complication. So similarly any state you reflected in your complication you will want to navigate to the appropriate place in your application for that state. Additionally, we are moving it to WK extension delegate rather than calling it on the root interface controller, which is what we used to do.
This should give you greater flexibility with what you want to do to handle your state maintenance. So we have a new call on a new object called WK extension. The new call is root interface controller, which I have to warn you this is coming in a future seed. It's not in the existing seed. So in order to approximate this for now, you'll have to save off your interface controller when it's first created.
With that in mind, here is how you might implement handle user activity on you are WK extension delegate for now. I will get the root controller with the new call, pop back to the root and then I will ask the root controller to go ahead and do whatever it is that would be appropriate to restore your state. Note there are lots of other things you could do here.
You might put up a modal alert or other things to handle the user activity you have been handed. Next up, on IOS we have UI application which is a singleton object that represents the running application. So on watchOS 2 we have added WK extension which is roughly analogous to that.
Among other things this is the main interface for opening standard system URLs. So you will be able to open the open system URL API to do things like start a phone call, send a text message or display PassKit UI. That's open system URL and WK extension. Now, I would like to talk a little bit about notifications.
First, I would like to talk about remote notifications. Remote notifications come from the internet, and they always go to your phone first. At that point, your phone will decide whether to display the notification itself or whether to forward it on to be displayed on the watch. Now, the phone uses a set of rules to determine this, and the criteria in these rules include whether or not your phone screen is locked and whether your watch is on your wrist and unlocked.
So like watchOS 1, your WK user notification interface controller is run when a notification is received for your app while it is inactive. When this happens, one of these calls, did receive remote notification or did receive local notification, will be called on your controller, and it's up to you to call the completion handler in a timely manner. If you take too long, your default interface will be shown, which is a little bit less lively. So it's up to you to do this in a reasonable amount of time.
Next up, I would like to talk about local notifications. Local notifications must be fired on the phone by your iPhone app, but you can message your iPhone app from your WatchKit extension and ask it to fire the local notification. The same logic will be applied as for remote notification, where the phone will decide whether to display the notification itself or whether to send it back to the watch to be displayed there.
So let's walk through an example of how you would go about doing that. Here, I'm going to use watch connectivity send message call to send the message from the watch to the phone app. When my iPhone app receives that message, it can fire the local notification to kick off the normal logic to determine where the display the alert. Relatively simple code.
So now we have the alert visible on your watch. I would like to talk about launching your application from your notification UI. The app can be launched in two ways from here. Either you can launch from a notification action button which in this case I have set up with the reply button. The user can also launch the application for notification by tapping on the application iCon in the upper left corner.
When one of these actions happens or when the user does one of these things, one of these calls will be made on your WK extension delegate, either the handle with identifier call will be made either for remote or local notification. You will receive the identifier of the action that was tapped. If they tapped on the application iCon you will receive a nil for that, and in the remote case, you will get a dictionary containing notification payload.
In the local case, you will receive the UI notification object that you created in the beginning to fire your notification to begin with. So that's launching your app from a notification. Next, I would like to talk about inline notification text replies. In iOS 9 and watchOS 2 we have added third party support for inline notification text replies.
In this example, the reply action has been designated as having text input behavior. When the user taps this action, instead of launching the app or sending a signal back to your iPhone app, the user will be presented with text input UI. So you can see in this UI the user is able to tap on the microphone in order to dictate the response, they are able to tap on the emoji iCon in order to enter from the emoji picker, or pick from a list of suggestions which you as the app developer will be able to supply. And the way you will do that is by implementing suggestions to response to actions identifier call on your WK user /notification interface controller.
So once the user has selected, or has provided their text input either through one of your selections or one of the other methods, your application will be launched, and you will receive this handle action with identifier call on your WK or your WK extension delegate. You will note that this variant has a new parameter, the response info, and the response info will contain the text that the user entered in the UI user notification action response typed text key.
So another new feature I like to talk about is on the fly language selection. Here you can see we have standard text input. Any time the user has the standard text input UI up, they can choose to do a Force Touch to bring up a language chooser. The language chooser will offer an option of all languages that are available.
These are selected based on the keyboards you have made available on your iPhone. So I have made English and Spanish available. So I will go ahead and switch to Spanish which will switch the text input UI to Spanish and allow it to populate with a list of Spanish suggestions instead of English ones. So that's on the fly language selection.
Next up I would like to talk about another way to handle notifications. In watchOS 1, whether your app was active or not, we would always instantiate your WK user notification interface controller, and the system would display that over the top of whatever was on the screen including your own app. In watchOS 2, if your app is active, we will no longer be doing that. Instead the WK extension delegate will receive a did receive notification calls.
It will be up to your app to handle, to display the contents of the notification as appropriate. So, for example, if you are writing a chat app, you might append the contents to the end of the chat transcript. Again, the system will no longer be putting up the alert for you so if you want the user to know the notification happened, you need to present it in your UI. Next I would like to talk about modal alerts.
In WatchOS 1, you could unhide a hidden group to show an alert. In watchOS 2 we have allowed present alert controller with title which should make it much easier to present modal alerts to users. There are three variants on this call. The first one is alert. This is a simple call for telling the user that something has happened.
Next we have the side by side buttons alert. This is presenting either-or options to the user and we think the third-party developers will want to use this in their application as well. And finally, we have the action sheet. The action sheet can have up to four main actions plus a customizable cancel action.
You can see the never mind, the action I have customized to be never mind. Actiions can be optionally marked destructive which I have done with the delete button. Currently that means they will be displayed in red. Once the user selects from the cancel or one of the four main actions you will receive a call back indicating which one was selected.
So in summary, we have got a new architecture it watchOS 2, we've added WK extension delegate to help you manage your application's lifecycle. We have got a whole host of new APIs. And, of course, there is more to come. If you -- for further info on the things Andrew and I have talked about here, please check out the documentation, the sample code and if you have specific inquiries, please contact Jake Behrens at this address and there is a whole host of related sessions including the next session in here which is part 2 of this talk. So thank you all very much for coming.
[ Applause ]