System Frameworks • watchOS • 57:24
Getting information to your Watch app is crucial. Learn how to leverage NSURLSession to get your data from the Internet. Tap into the new WatchConnectivity framework for device to device communication and transferring of data. See real-world examples and learn best practices to understand when to use your WatchKit extension and when to leverage your iPhone app.
Speakers: Chris Jensen, Alex Ledwith
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
[Applause]
[Chris Jensen]
Good morning. Welcome to Introducing WatchConnectivity. My name is Chris. Today I'm joined by my coworker Alex, who will join me on stage later. We are excited to show you what we have been working on for both watchOS 2 and iOS 9. We think what we are going to talk about today is going to help you make more responsive and better user experiences for your Watch apps.
To do a brief recap of where we are coming from, let's look at what the world looked like in watchOS 1. Your iOS app and your WatchKit extension were both living on the iPhone, and we were taking care of the communication to the WatchKit app, and they could both share a data store.
In watchOS 2, we've moved the WatchKit extension over to the Watch, and now your app is running natively on the Watch. This has a lot of benefits, but it also means they now each have their own data store. The obvious next question is going to be: How do we get data over to the Apple Watch? That's what we will discuss today.
So we are going to show you two main ways to deal with this problem, of getting the data to the Watch. There's the new WatchConnectivity framework, which we are introducing in watchOS 2 and iOS 9. And then there's the NSURLSession APIs available in Foundation. These are still available to your WatchKit extension, and it's now available native on watchOS 2.
We think the topics we are going to discuss today apply to most Watch apps, and we think it's going to apply to most of you. This is exemplified by the amount of examples that we will be using in our presentation today. You can see there's a wide range here, and we hope that every one of you will be able to identify with at least one of these.
So let's get started with discussing WatchConnectivity. So this is the new framework we introduced in watchOS 2 and iOS 9. It's available in both platforms, and pretty much all of the APIs are available in both sides. There are couple of iPhone-specific APIs that we will get into. The first thing you want to do when you are starting to adopt WatchConnectivity is you want to go through the setup.
We recommend that you set this up very early in your app's life cycle on both sides, both on the WatchKit extension, running on the Watch and in your iOS 9 apps. You want to make sure you do this setup in a code path that will be executed even if you are being background launched.
So, don't put it inside, like, a view controller's View Did Load, because when you're being launched in the background, those won't get called. So the first thing you want to do in your iOS app is you will check to see if the WCSession is supported. You might have a universal iOS app. Which means this code may be executing on an iPad, where WatchConnectivity is not available.
So check this up front before you do any Watch-specific work because we don't want you to waste a bunch of CPU doing work that won't be used anywhere. The next thing you want to do is create an instance of our object, and you do that by calling Default Session. And then you want to set a delegate on the session object, and then finally you want to call Activate. This will go and set up the WCSession object, initialize all the properties, and once this call returns, all the properties will be updated with the correct initialization values.
And also at this point, any delegate callbacks, any cued-up content can start coming in. That's another reason why you want to do this very early and always. There might be content waiting to be delivered to your app. So make sure you do these steps up front. The next thing is you are going to want to do, once you completed that step, is look at the session states. This will inform your app about what the current relationship between the iOS app and the Watch is.
So these properties are only available on the iPhone app. It's informing the iOS app what its relationship with the Watch is. So you don't need to use this in your Watch app, and they are mostly not available. So for this example, we will use a news app. Something that pulls down the most recent interesting articles.
And it's going to first go through the setup process that we just discussed. It will do this early in its life cycle. Next, it will want to check, is this device paired with a Watch? If it's not paired, then it's almost as if this is running on an iPad, there's nothing more to do.
There's no one else to talk to. So at that point you might as well stop doing any Watch-specific work. But the user will go out and buy a new Watch, and he will go through the pairing process. He will launch the Apple Watch app. Work his way through the pairing, setting these devices up.
And now, when your app launches, you will get a delegate callback. The session Watch state did change. When you check the value of the paired property, you will see it will return True, because the devices are now paired. So this is a trigger for you to go ahead and check this next property, which is, is your Watch app installed? If it is not installed, then there's no one to talk to, your work is done.
But -- and by default, the Watch app will be default -- but the user might choose to uninstall it for some reason. In this case, the user will go ahead and reinstall it. He will go back into the Apple Watch app and he will flip that switch, and now if your Apple is running, you will again get the delegate callback and you can check this properties value, and you would see that Watch App Installed is now True. Now you do have someone to communicate with. This should be a trigger for you to start communicating with your Watch app. It will need content that only you can provide for it.
Whenever the Watch App Installed switches to True, there's this other property that's going to be available, Watch Directory URL. You will find that this, whenever Watch App Installed is True, Watch Directory URL will have a non-nil value. It will be a path to a directory that we create in your container.
Let's discuss this a little bit more in depth. So the directory and its contents, its lifetime is tied to the Watch App Installed property. This means whenever Watch App Installed switches from True to False, this directory and all of its contents goes away. Whenever it switches back to True, the directory will be present, but this time it will be empty.
We recommend you only use it for data relevant to the specific instance of your Watch app. What do I mean by instance? Well, things like last queued item marker would be a good thing to store there. If the user uninstalls and reinstalls your app, that Watch app is starting with a clean container.
Therefore, this directory will start clean, and you will need to sync up where your app is communicating. Other things you can put in is something like preferences. When the user is running the iOS app for the first time, you might like him to set up what he would like his Watch app experience to be. Maybe he doesn't want to show the full content but the top ten news items for a particular topic like international news or sports.
This would be a good place to store them. Also if you are taking your full-sized assets and generating Watch-specific assets, you are compressing images, audio, video, this would be a good place to store those while they are cued for transfer, using the WatchConnectivity APIs that we will discuss later.
So that's the Watch directory. We suggest you store content in there, because then we will clean it up, if the Watch goes away, if he unpairs his watch, we will automatically clean up this content, so you don't have to micromanage all of this. The final property that's part of the session state is Complication Enabled. Currently, the user does not have the complication enabled, but he will go in and edit his Watch face and he will enable it and you will get the same delicate callback, session or stated change. And when you check this property, it will now be True.
Now you have set up your WatchConnectivity session. You have figured out what the state of the world is, what your relationship between your iOS app and your Watch app is. The next thing you will want to do is start communicating information over to the Watch or from the Watch to the iOS app. And to do that, I will hand over to Alex, who will talk about the communication APIs. [Applause]
ALEXANDER LEDWITH: Thank you. Okay. So like Chris said, you set up your session. You have checked that devices are paired, your Apple Watch app is installed. Now let's start talking about how you can communicate between these two devices. We have a couple of different categories for communication. First category is background transfers. Background transfers are meant for content that is not needed immediately on the receiving side.
Because the content is not needed immediately, this means the system can do more intelligent things when transferring that content. In addition to background transfers, we are also going to talk about interactive messaging. Interactive messaging is meant for communicating between two apps with live communication. So both apps are up and running. They are sending messages back and forth, request response, that kind of thing.
Some examples of when you might want to use interactive messaging. Say you have a game where the user is using both apps at the same time. Or you are on your Apple Watch, and you need to trigger something to happen on the iOS side. Like you want to trigger the iOS device to start tracking the user's location.
So let's dig into this first one, background transfers. The first thing to talk about for background transfers is the type of content your apps have and how the user is going to interact with these two devices. So let's take that news app example again, this news app has some content. It's fetching some more content from the server, and it determines some of this content could be useful for your Watch.
Now, the user isn't using the Watch at this point in time. So the content isn't needed immediately on the Watch side. Rather, the iOS app, the news app, wants to just pick some of that content and queue it up with the system, and then allow the system to pick the right conditions to transfer that content across.
The system will look at things like power, performance, when the user is actually using the receiving side, in this case the Apple Watch. When conditions are right, that content will transfer across, and it will wait on the receiving side, in this case the Apple Watch, until the user launches the receiving app. When the receiving app is launched, then that content will be delivered and the app can update its state.
So this is what background transfers provide. It allows you to queue up content. The system is going to transfer the content for you. This allows the sending side, the sending side app, to exit. The system will handle the rest. It allows the system to pick the opportune time to transfer that content, and it allows the system to store the information on the receiving side and wait for the receiving app to launch. For a lot of the content your apps have, we definitely recommend that you use background transfers.
The reason is most of that content will not be needed immediately on the receiving side. Rather, it will be needed when the receiving app actually launches. So let's get into some nuts and bolts. We have three different types of background transfers. The first type is the application context. The application context represents a single set of the most interesting information your app has to offer to the other side.
So, for example, let's say on the iOS side you have an app that tracks the user's location, and based on that location, the app picks a restaurant in that location and wants to recommend it to the user. In addition to sharing restaurant on the iOS side, you also want to show that restaurant in the Apple Watch app. So you could package up that suggestion into the application context, and that will get transferred across, and then the next time the user launches the app on the Apple Watch side, the content will be there, that suggestion will be there to show.
Another example of when you might want to use application context, say you have a social networking app on the iOS side, it fetches a bunch of posts and determines that there's a top 10 set of really interesting posts that it wants to show on the Apple Watch side. That way the user can look at the Apple Watch and see interesting information right away. You can also package up of those top 10 posts into an application context, which will get sent across.
Now application context is the simplest way of transferring content in the background, but if you need something a little more complex, or you need to queue up more than a single set of information, we are offering two ways to do that. The first way is user info transfer.
This allows you to transfer user info dictionaries, in-memory content that you want to pack up. An example of this is let's say you have a game on the Apple Watch side. The user progresses through levels, and as the user progresses through levels, you want to sync back that progression to the iOS app. The iOS app will show some nice graphs on how the user did in each level.
In addition to the user info transfer, we are also offering file transfer. This is very similar. It allows you to queue up content, except in this case, the content is a file. One example that we are going to use in this presentation for file transfer is let's say you have an iOS app that allows the user to edit images and after they edit those images, the user can pick their favorites. And those favorited images are the ones you want to show in Apple Watch. You can use file transfer to transfer across those favorited images. So they're available on Apple Watch so the user can show them off to their friends, that kind of thing.
So let's dig into these individually. We will start with application context. The example we will use for application context is the social networking app example. I mentioned before, this app on iOS fetches all the posts from the social networking site and then it picks the most interesting ones to send over to the Watch app. The first thing to talk about with application context are these two properties.
The first property is Application Context. It is the property that stores the latest content on the sending side, and then on the receiving side, there's Received Application Context, which will store the latest received content on the receiving side. So let's say this iOS app has fetched a bunch of posts, and it's packaged up the most interesting ones for the Watch. It's going to want to call Update Application Context. This method takes a dictionary, representing the latest, most interesting state you want to send across.
This content, we take this content after Update Application Context is called, and we push it down into the Application Context property. And this content will sit here, and the system will determine a good time to transfer that content across, maybe when the user starts actually using their Watch.
Now, in the meantime, this iOS app could fetch more content and determine that there's a newer set of interesting information that it wants to send over to the Watch. In this case, it's going to want call Update Application Context again. And then we're going to push that content down into the Application Context property. This is going to bump out the old relevant state and put in the new relevant state because what we really care about is the most interesting, latest set of data.
Now, this new content is going to sit here, again, waiting for the system to pick a good time to transfer that content across. When the system does pick a good time, that content will come across. It will sit on the Apple Watch side. And it will wait for the user to launch the app, the Apple Watch app. When that app is launched, we will deliver that content to your WatchKit extension, the place where all of your code is executing on the Apple Watch side.
So that's the flow of application context. Now, let's take a look at some code. The first thing you want to do is you want to package up your context dictionary, representing the latest state that you want to send across. And then you will call Update Application Context with that dictionary.
The last thing to mention about this code example is that the call to Update Application Context is wrapped in a Do Catch block, and the call is prepended by a Try. This is new error handling in Swift. Update Application Context can return an error. And if an error is returned, the Catch block will be invoked, and we strongly suggest that you handle your errors appropriately.
So that's the sending side for application context. Now, let's take a look at the receiving side. On the receiving side, the receivers will get this delegate callback, Did Receive Application Context. It's going to pass through the dictionary that the sender packaged up. And at this point, the receiver can take that content and update its app state.
One thing to know about this delegate callback and all delegate callbacks in our API is that they're returned on a non-main serial queue. If you need to do something on the main queue because maybe you're updating some UI, you're going to need to dispatch over to the main queue to do that updating of your UI based on this content or something else.
So that's application context. It's the most interesting relevant content that your app has for the other side. It does have overriding behavior, and this is because you should treat the latest content as the content that the receiving side cares about and anything that's not latest isn't relevant anymore. Application context takes a dictionary. This dictionary takes property list types. Property list types are basic object types such as numbers, strings, basic collection types, dictionaries, arrays. Apple has some great documentation online if you want a refresher on property list types.
So we have some specific recommended use cases for application context. Application context works really well for many Apple Watch apps because many Apple Watch apps show a subset of the information the iOS app has. So, if your app works like this, we suggest that you put that subset of the information into the application context and let it get sent across to the Apple Watch side. In addition to those apps, application context also works really well for glances.
Glances take the single most interesting piece of data your apps have to offer. So we suggest you put that piece of data into an application context on the iOS side, so that it gets across to the Watch side. Then when the user swipes up on the clock face to show your glance, that data will be available.
So moving on from application context, now we are going to talk about user info transfer. An example here that we are going to use is you have a game on the Apple Watch side. The user progresses through levels in this game, and as the user passes one level, you are going to sync back the progress that they made to the iOS side so that the iOS app can show some nice graphs on how the user did in that level. The first thing to talk about for user info transfer is the outstanding user info transfer queue. This holds on to all the content that's waiting to be transferred across.
The current state of the world as the user progressed through two levels. The progression is sitting in the outstanding user transfer queue. And currently the user is working on level three. Once they finish level three, you are going to want to package up that content and call Transfer User Info and pass through a dictionary that represents their progress.
This will take that dictionary, and it will package it up and it will put it into the outstanding user input transfer queue. Now this content will wait here until the system determines it's a good time to transfer that content, based on power considerations or maybe when the user starts using their phone.
The content will then transfer, and like the previous API, we are going to wait until the app on the iOS side launches. And when it does, we will deliver that content, and now the iOS app can update those graphs to show the progress the user made in their Apple Watch game. So that's the flow for user info transfer. Let's take a look at some code.
First thing you want to do is package up your user info dictionary with all the content that represents this current state that you want to send across. And then you want to call Transfer User Info with that dictionary. Transfer user info returns a user info transfer object. This object contains the dictionary that's being sent across, and it allows you to cancel this transfer if the transfer is still in the outstanding queue.
In addition to this transfer object being returned, we also offer a way to get all of the outstanding user info transfers that are in the queue. This returns an array, and you can iterate over the array, look at all the contents, and potentially cancel if you need to. So that's the sending side for user info transfer.
Let's take a look at the receiving side. On the receiving side, you will get this call, Did Receive User Info Transfer. Like application context and all the other delegate callbacks, this is returned on a non-main serial queue. Once you get that call, you can take that content, that dictionary content, and you can update your app state.
That's user info transfer. It takes user input dictionaries. These dictionaries, like the application context dictionary, take property list types. It's good for in-memory content, like game progression. And we give you access to the outstanding user info transfers in the queue. Next, let's talk about file transfer. An example we will use for file transfer is this image-editing app. The user can edit images on the iOS side, and then they can select their favorites, and those favorites are the ones we want to transfer across to Apple Watch.
So the first thing to talk about is the outstanding file transfer queue. This is where all the file transfers will sit when they are waiting to be sent across. And then on the receiving side, the files will be put into the Documents/Inbox folder while they are waiting to be delivered to the receiving side app.
The state of the world is the user has favorited two pictures, two images, that are sitting in the queue, and they are working on a third. Once they have completed that third and have selected it as a favorite, you will want to call Transfer File. And you will pass in a file URL pointing to the file that you want to transfer, and we are offering a way to transfer additional metadata in the form of a dictionary. One example of when you might want to add some metadata is if you want to group some of these files together by putting an identifier in each metadata dictionary. That way the receiving side can pull out that identifier, group the incoming files.
So the user's favorited this image. We called it transfer file. Now we will take that packaged-up content, and we will put it into the outstanding file transfer queue. And it will wait here until the system determines a good time to transfer the content. When the system does determine a good time to transfer that content, it will move that content across and it will wait for the receiving side to launch and take care of that content.
One thing to note about files, files can be a little bit larger. And the larger the file, the longer it's going to take to transfer across. Potentially you might hit power conditions, performance conditions while those are transferring. Just be aware if you have large files that are trying to transfer across, they may take longer than the transferring in some of the other APIs. Now the receiver will launch, and we will deliver these images. And now the Apple Watch app can show those images off.
So that's the flow of file transfer. Now let's take a look at the code. First thing you want to do is you want to get your URL to the file that you want to transfer. Then you want to package up your metadata and finally, you want to call Transfer File, passing through that URL and that metadata dictionary. This returns a file transfer object, the file transfer object contains the URL, the metadata dictionary, and also gives you the ability to cancel any file transfers that are outstanding.
Just like user info transfer, we offer you the ability to get the array back of all the outstanding file transfers. You can iterate over this array, check the contents, and cancel if need be. So that's the sending side for file transfer. Now let's take a look at the receiving side. On the receiving side, you are going to get this delegate callback, Did Receive File. There's a few things to mention about this delegate callback that are slightly different than the previous two.
First, you are going to be getting this WCSession file object. This object just contains the file URL and the metadata. The second thing to talk about with this callback is that the file is now in the Documents/Inbox folder of your app's container. But to take control of this file, you need to move that file out of the Documents/Inbox folder into a more permanent location.
So the main reason you need to move this file is that the Documents/Inbox folder will be cleaned up after this delegate returns. This means the file will be deleted out of there along with any additional content. So it's really important that you move this file into a more permanent location inside this delegate callback. One thing to keep in mind if you are dispatching to a different queue because this is returned on a non-main serial queue, you will need to move the file before you do that dispatching, if that dispatching is async.
So that's file transfer. It's very similar to user info transfer, except it allows you to transfer files or queue up files. We do offer the ability to access the outstanding files in the queue. And we provide the ability to transfer additional metadata. We suggest you keep this metadata small, and this metadata dictionary, like the other dictionaries we talked about, takes property list types.
So those are the three background transfer modes. Use these if the receiver does not need the content immediately. If, however, you need to send messages back and forth in a live fashion, you can use interactive messaging. And interactive messaging is meant for that live communication, both apps are up and running and they are sending messages back and forth.
Like I mentioned before, some examples of when you might want to do this. Let's say you have a game where both UIs are up and you want the user to be interacting with both. Or if you are on the Apple Watch side and you need to trigger the iOS app to do something, like start tracking the user's location.
Now, there are certain conditions that need to be met for interactive messaging to be used. So let's talk about those conditions. It all relates to this idea that we are introducing called reachability. And what reachability means is that the other app is available to receive content. It is required that the other app is available, the other app is reachable, to use interactive messaging. And the way that you check that the other side is reachable is we have this property on the default session, Reachable, that you can look at.
Now, the conditions for reachability are slightly different depending on what slide you are on, whether your code is executing in your iOS app or it's executing in the WatchKit extension. So let's look at those individually. We will start on the iPhone side. The first condition that needs to be met for Reachable to be True is that the devices needs to be connected. This is connected over Bluetooth or over Wi-Fi, but if the user leaves their Watch at home, takes their phone with them to work, the devices won't be connected and interactive messaging is not going to work in this case.
The second condition that needs to be met for Reachable to be True on the iOS side is that the Watch app must be foreground. This means the user must be interacting with their Watch app for interactive messaging to work from the iOS side. Once these two conditions are true, the Reachable property will be True in your iOS app.
So that's the iPhone side. Now let's talk about the Apple Watch side. The first condition for Reachable to be True in your WatchKit extension is that once again devices must be connected. This means that if the user goes for a run and leaves their phone at home and takes their Watch with them, Reachable will not be True. The devices will not be connected. The second condition is that the WatchKit extension needs to be foreground.
We mentioned the WatchKit extension here being foreground because there are certain cases where the WatchKit extension can run in the background. They mainly relate to complications, and we will talk about this a little later in the talk. For now, when the user is using your app, your WatchKit extension is going to be running and your WatchKit extension will be foreground, which means you can use interactive messaging and the Reachable property will be True.
One other thing to note about this diagram. We are not saying that the iOS app is running. The iOS app has to be running to respond to messages coming in, to send its own messages. So how do we get into a state where the iOS app running in addition to the WatchKit extension? Well, for this direction only, sending messages from the Watch to the phone or allowing the iOS app to be launched in the background upon receiving a message. So let's take this example. You have a run tracker app and it needs to send a message over to the iPhone side to talk to CoreLocation to start tracking the user's location.
So this app is going to package up a message that tells its iOS app to start using CoreLocation, and it's going to send that message across. When the system receives this message, we are going to launch the iOS app in the background and deliver that message. Now, both apps are running, and now they can do communication. This app in this example can start tracking the user's location.
So that's kind of flow of interactive messaging. Kind of how it relates to reachability, when you can use it. Now let's get into the nuts and bolts of how you use it in your code. We are offering two different types of messages. The first type takes a dictionary and you use this call, Send Message, which takes that dictionary, plus a reply handler and an error handler. This dictionary, like the dictionaries we talked about before, takes property list types.
In addition to dictionaries, we are also introducing a way to send data. You can send data by calling Send Message Data. This takes that data plus that same reply handler and that same error handler. For sending data, we suggest you use this if you have custom data that you're storing your information in or if you have your own serialization format. If you are using your own serialization format, we strongly suggest you use one that's quick and compact. This way the user experience is faster because the content is transferring faster.
One thing I want to point out about these calls is replying. You probably noticed the previous two calls have reply handler. This handler is optional. However, we do recommend in most cases you use it. The reason is that this allows the receiver to confirm the incoming message. The receiver can confirm that it received the message. The message contained the right content, and it was able to process that content. And then this way the sending side knows that it doesn't have to send anything else. It doesn't have to send anything new because it sent the wrong stuff.
The other part to talk about for the replying is what happens on the receiving side. What happens if the sender says, "I want to reply, so I'm going to supply a reply handler," versus if the sender says, "I don't want to reply, I'm not going to supply a reply handler." In these cases, we have separate delegate callbacks the receiver is going to get, depending on whether or not it should supply a reply.
So let's talk about those delegate callbacks. In the first case, the sender says, "I do need a reply, I'm giving the system a reply handler." This means that the receiving side will get this delegate callback, Did Receive Message, it has a Reply block that you can call after the receiver has received the message and processed it, and the receiver can then determine if it wants to send back some content or maybe send back an error if the message is wrong. Now, on the other hand, if the sender doesn't supply a reply handler, the receiver is going to get this delegate callback, Did Receive Message. It doesn't have a Reply block. The receiver can process the incoming content and they are done.
The last thing to note about these two delegate callbacks is they pass a dictionary through. This means that the sender used the send message, sending on the sending side, to send a dictionary. If instead the sender used send message data to send data, there is analogous callbacks on the receiving side that pass through data.
So now that we kind of have a feel for interactive messaging, let's put it all together and code for the sending side. The first thing you want to do is you want to check reachability, make sure the other side is actually reachable. Then if Reachable is True, then you can package up your message.
And once you have your message, you can call Send Message with that dictionary. We expect a reply. So we will supply the reply handler, and we want to handle our errors, so we will implement an error handler. So those are the different ways to transfer content using WatchConnectivity. So let's sum up what we've talked about for WatchConnectivity.
The first thing you want to do is you want to set up your session. To do this, you set your delegate, and you call Activate. You want to do this early in the lifetime of the app so the app has the ability to start receiving content and the ability to start checking properties. To check those properties, you look at the session state. And once everything is okay, once you know there's a paired Watch, once you know your Apple Watch app is installed, you can start communicating. The first type of communication is background transfers.
We offer three types. The first type is application context. This is for the single set of really interesting information that your app has for the other side. Or if you need to queue up content, you can use user info transfer or file transfer. In addition to background transfers, you can use interactive messaging for live communication.
So that's WatchConnectivity. It allows device-to-device communication between your apps. And we are excited to see what you do with this API to get content back and forth and provide better user experiences. Next, we're going to talk about NSURLSession briefly. So what is NSURLSession? It's an existing foundation class. It allows you to make HTTP requests to your servers to fetch content.
It's available in watchOS 2, and we strongly suggest you use it if your servers have content that needs to be fetched. And it takes advantage of the Tetherless Wi-Fi feature. The Tetherless Wi-Fi feature allows Apple Watch to connect to known Wi-Fi networks when the phone is not around. If the Apple Watch does connect to known Wi-Fi networks, you can use NSURLSession to go over that Wi-Fi to connect to your servers and fetch content.
So what do you want to use NSURLSession? You want to use it any time your server has new content. This is very similar to how you might be doing stuff in your iOS apps. We do suggest, however, that you tailor the content that's being delivered to Apple Watch based on how Apple Watch works.
So if you have images on your server, we suggest you scale those images for the screen size of Apple Watch, or if you are a news app and you are only going to show some of article, maybe just the text, on Apple Watch, we suggest you only fetch the parts you need.
So that's a very brief introduction to NSURLSession. There's a great WWDC session on this API as well as great online resources. So we definitely suggest you check those out. Now, the last thing we want to talk about for NSURLSession is using NSURLSession with WatchConnectivity. So once again, we have the example of our news app.
This news app has fetched a bunch of content from its server, and it knows that the Apple Watch app probably will have to fetch this same content the next time the user launches the Apple Watch app. Instead of making the Apple Watch app refetch that content, we will use application context to transfer across the content from the iOS side to the Watch side.
That content is going to come in, and it will be delivered to the Apple Watch app the next time it launches. And now the Apple Watch app has the ability to show the same content that was seen on the iOS side, and it provides a more cohesive experience.
Now the next time the user launched the Apple Watch app could be a couple of hours later, which means the server has even newer content that might want to be fetched. So we suggest that in addition to taking in the application context that was sent over, you use an HTTP request with NSURLSession to fetch the absolute latest content from your server.
But this way, while the user is waiting for that content to come down, they will see the same content they saw in the iOS side and they will have a better experience. So that's NSURLSession, NSURLSession and WatchConnectivity. Now, we want to take these two APIs and we want to show you how to use them to get data to your complication. And to do so, I will bring Chris back up to the stage to talk about this. [Applause]
[Chris Jensen]
Thank you, Alex. That's some cool stuff, right? I think it's going to be great to see what you guys end up doing with the WatchConnectivity APIs and the NSURLSession APIs. Now let's discuss complications. But before we dig in too deep, let's make sure we are all on the same page. These are three Watch faces, three clock faces on the Apple Watch. If you remove the timepiece, the remaining pieces are complications.
They provide small snippets of information every time the user looks at their clock face. And this will allow them to get sort of the most important piece of information really quickly. So when you are implementing your complication, there's two primary tasks that you will have to solve. You will have to figure out how to update the clock face, and the second one is you need to get the content to use to update the clock face.
So let's discuss how you would update the clock face briefly. This is covered in depth at other sessions. For this example, we will use a weather app that has a moon phase complication. The moon phase complication doesn't need any external data. It already has all the information it needs because it just needs the date and time. So all it needs to concern itself with is how to update the clock face. To do that, it's going to use the new ClockKit API, the ClockKit framework introduced in watchOS 2. The way the flow works is we're going to launch our WatchKit extension in the background.
When this happens, you are going to want to get an instance of CLK complications server. You call Shared Instance to do that. And you will call Extend Timeline For Complication, and you pass in the complication that you are updating. The next thing that will happen, that will trigger our process with ClockKit and they will start asking you a couple of questions. They will ask for the current timeline entry. This is the one that's going to be shown right now.
They will ask for previous timeline entries, future timeline entries, and finally, they are going to ask for a suggestion of when you think this data will be stale. This is a suggestion to the system so that we can know when you think that you need to get launched again. So you can update your timeline further. That was a quick summary of updating the clock face. You would use the ClockKit framework to do so. You will be able to provide content for past, present, and future.
Your WatchKit extension will get launched in the background to do these updates, and you will be given a chance to specify when the content provided is going to be stale. One thing to keep in mind is that all the work that your WatchKit extension is doing on behalf of updating the complication is budgeted, so you want to try to keep this as fast and efficient as possible so that you can keep getting launched throughout the day to update your complication.
As I mentioned, there's a great talk dedicated to this topic, Creating Complications with ClockKit. We want you to check that out if you haven't done so. The next you will have to deal with is how to get content to your complication. There's a very special instance, which is the initial activation.
The first time the user goes into his clock face, he's going to go in and edit it and then he's going to enable your complication; in this case, the news app complication. At this point in time, that complication very likely has very little or no data to populate its timeline. So it has a large need for a lot of content. So what's going to happen is we are immediately going to launch your WatchKit extension in the background. And now you have a couple of different ways of getting that content so you can initialize that timeline.
You could use NSURLSession to communicate with your servers to get that content, or you could choose to use WatchConnectivity. If you are going to use WatchConnectivity in this very special circumstance, and the devices are connected, you will find that Reachable is True. This is what Alex was referring to earlier, where this property might be True in certain special circumstances. This is one of those.
What you are going to be able to do in this case is you will be able to call Send Message, which will send a message across to the iPhone. And we will wake up the weather app in the background, and at this point, the weather app on the iOS side can use any of the WatchConnectivity APIs to communicate the information back to populate that timeline.
So in summary, when you are going through the initial activation, your WatchKit extension will get launched in the background. You can use NSURLSession or, because this is a very special circumstance, you will be able to use the WatchConnectivity APIs to wake up the iOS app because Reachable is True. We suggest you use this to populate as much of the ClockKit timeline as possible because it's starting with nothing.
The next issue is how to stay current. Your timeline is now populated, and now future updates will be happening. How do you keep updating your complication? So there's a couple of different ways that you could use to update your complication. You could get content pushed to your complication.
This makes sense if you have an external source such as a web server that knows specific times when there is new content and it's not a regular cadence. What you are going to want to do then is have the content push from the clouds to the iPhone, and then it gets relayed over to the Apple Watch.
An example of where we think this makes sense is something like a sports app, where the complication is showing game scores. Most of the time, those scores are going to be happening in a short period of time during the day, and it will be very rapid updates. So then we think it makes more sense to use the pushed approach.
The other one is what we're calling requested interval fetch. This is more for when you know there's a regular cadence where you can keep updating your complication. And then you could use something like NSURLSession to go directly to the cloud. Something like a surfing app with a tide complications that shows what the tidal patterns will be so you know when to go out surfing. So let's take a look at this case first.
In this case, you will want to use NSURLSession and ClockKit to update the complication. As you can see in the corner of clock face, the surfing complication is already enabled. The flow is going to go a little bit like this, where the WatchKit extension gets launched in the background. You're going to want to generate an NSURLSession request and you send that up to your servers to get content.
The server will produce a response and it will get delivered back down to the WatchKit extension. Now you want to turn around and update ClockKit. So you will request them to extend your timeline. They will start asking you these questions, and you're going to give them the timeline updates, both past, present, and current, and finally, you are going to suggest a time for when you should be launched again.
And the last thing that will happen is when you provide that time for when you next should be launched, that's a hint to the system that your work is done, and your WatchKit extension will get killed. So now, let's pretend that some time passes, and the system has decided based upon your hint and system conditions that now is a good time to relaunch your complication.
Again, you are going to get launched in the background. You are going to produce a request, using NSURLSession. You are going to send it up to the servers. The servers are going to produce a response, and you are going to turn around and update your complication using ClockKit.
So in summary, and a couple of tips is that we suggest that you use NSURLSession background session if possible. This is because the NSURLSession request might not complete until the next time the extension runs. Using the background session enables it to deliver the content the next time you run.
The requested time that you are providing is just a suggestion to the system. It's not a guarantee. We'll try to get you close to that time, but conditions apply and it may not always be exact. We suggest that you keep the runtime as short as possible, and you will use ClockKit to update the clock face.
You want to keep runtime as short as possible and make sure that you make your next requested update time as far out as possible because these are budgeted and you don't want to run out of budget before the day is over. The other approach to getting the content, which was in our example of the sports app, was to get the content pushed.
We will look at this in a couple of stages because it uses two very distinct processes. The first one is you are going to use PushKit to get the content from the cloud to the iPhone. The second part is using WatchConnectivity to get that content from the iPhone across to your Apple Watch. So let's look at those separately. So part number one is where you are using PushKit to get the content to your iPhone. We have updated the PushKit framework to add support for these complication pushes. The way you use it is you create an instance of PKPushRegistry.
Next, you will want to set yourself as a delegate so that you are ready to receive callbacks. And finally, you will set the decide push types and pass in the new PK push type complication that that was added in iOS 9. Once this is done, you will get a delegate callback with a new Push token, which you're going to want to upload to your servers, which is going to enable your servers to send pushes to this device. Finally, when the server does send the push, you will get the Did Receive Incoming Push With Payload callback. And this when you turn around and use the WatchConnectivity APIs to send the content over to the iPhone.
So for the second part, this is where you are going to use WatchConnectivity to get the content across to the Apple Watch app now that you received it in your iOS app. The first thing you are going to want to use is the transfer user info API that Alex discussed earlier. This will allow you to queue up timeline entries for both the past and the future that your iOS -- sorry, your Watch complication might need.
Once you have queued up all the timeline entries, the last thing you want to do before you are done with your work is called a special API. It's also part of WatchConnectivity. It's called transfer current complication user info. This is a special version of the transfer user info, and at any point in time, there can only be one current complication user info.
If you call this twice, only the most recent call is the one that is tagged as the current complication user info. When you call this, this is a hint to the system that the work is done, and on the receiving side all of these callbacks will produce this delegate callback, Did Receive User Info.
So let's look at what this all would look like from a full flow. All right? So the user has launched a sports app for the very first time. Your app early in its life cycle will want to set up the PK push registry and set the desired types. This will register this push -- this device with the Apple push servers. So that will get pushed up to the Apple servers. It will turn around and produce a Push token, and you will get that delegate callback in your iOS app.
You will want to take that token and upload it to your servers, enabling your servers to send pushes to this device in the future. At that point, the initial setup of the PushKit is done and your app can go away. Let's say the game starts, and the server decided it needs to update the complication. It will send a push down to your device.
That will get received on the device, and we will wake up the sports app in the background and deliver the push. That was that other delegate callback. At this point, you are going to want to look at the data in the push payload and figure out what needs to get sent across using WatchConnectivity.
You will call Transfer User Info to queue up the timeline entries, both past and future, and that will go into the outstanding user info transfers queue. And then you call the special transfer current complication user info with the one that's most important, and that's the one that should be shown on the Watch face right now.
That will also go into the outstanding user info transfers queue, but it will skip to the front because it's the most important one. And because this is considered urgent, we will try to get that across to the Apple Watch right away, and wake up the WatchKit extension in the background and deliver that current complication user info. Given conditions, some of other content might also transfer at this point in time and you will get those other timeline entries, but at least the most important one made it across. Finally, you want to use ClockKit to update your complication.
So there we go. You have updated your complication using PushKit. We have added the new PK push type complication to enable you to very quickly update your complication using information that's on your servers. There's a couple of restrictions to be able to use these push types. The complication must be active on the current clock face, otherwise these pushes will not be delivered, and there will be a limited number of pushes per day, so use these sparingly. Roughly one to two per hour on average, but the sports app may use them all in a short period of time.
You would use transfer user info to queue up your timeline entries. And finally, you would use transfer current complication user info to queue up the current or present timeline entry. Finally, you would use ClockKit to update the clock face. Keep in mind that a lot of these things are budgeted. Any work you do on behalf of complication update, both on the iOS and on the WatchKit side, counts against this budget.
So we recommend that any information you need to update your complication is included in that push. The complication push type has a 4 K payload, which is larger than the standard, so that should enable you to put most of the content you needed. If you receive one of these pushes and you turn around in your iOS app and do an NSURLSession request, you will run out of budget much faster. So make sure you include all the content you need in the pushes.
That brings us to the end of our session. Briefly going to discuss what we talked about today. We went through the WatchConnectivity framework and APIs. We look forward to seeing what you guys can do with these APIs. We briefly discussed NSURLSession and its use. There's other sessions that go more in depth on what and how to use NSURLSession. And finally, we discussed how to get data to your complications, which is a more advanced topic.
There's a lot of other great resources to check out. We have some great sample code, and we have our evangelist that's ready to answer any of your questions. As far as other related sessions, we especially suggest you check out Creating Complications with ClockKit and Networking with NSURLSession, as those are closely related to the content we discussed today. Thank you very much. [Applause]