Making your app's information available to users on Apple Watch is even easier in watchOS 2. Learn how to create custom complications for Watch faces using the new ClockKit framework and see how to provide data through numerous templates using text and images.
ELIZA BLOCK: Hi, everybody, I'm Eliza
and with me is Paul Salzman.
We will be telling you about complications, what they are
and how you can make them.
So, complications are small pieces of information
that appear right on your clock face alongside the time.
On these clock faces you see here, if we remove the time
from the equation, everything left is a complication.
So the ones you see here are all built into the OS, but starting
in watchOS 2 you can create your own complications
for the clock face and we will tell you how to do that,
and I'm going to use the example of building a complication
to display the upcoming matches in a soccer tournament.
So let's take a look imagining you already built
such a complication at what it would look like to go select it.
So focusing in on the middle clock face here,
if you were to Force Touch the screen,
you can customize the face, swiping to the right allows you
to start tapping on your different complications,
and turning the Digital Crown allows you to pick one.
So if we scroll all the way to the end of the list,
we will see our not-yet-built soccer tournament complication
and we could select it
and we start seeing the live data displayed
on the clock face.
How are you going to provide the data
to populate this complication?
As you can see from looking
at the face there is a consistent look and feel to all
of our clock faces and that's what part
of what makes them pleasant to interact with so we wanted
to do this while preserving that consistency.
So your extension which is running
on the watch will provide the data for these complications
in the form of text and images.
And then the clock face will actually draw it in a way
that fits with the rest of the face.
So if I were to install this complication on my watch,
I will see it right away every time that I raise my wrist.
And that's a great thing, and it's one of the great things
about building a complication, but it also presents a kind
of a challenge because as time passes
and the information that's displayed
in this complication needs to update, since it's visible
as soon as you raise your wrist, that update needs
to already have happened by the time the screen turns on.
And if you think about it,
there could be five different complications showing
on this single watch face.
There is no way we would have time to launch all of them,
pull for new data, potentially involving a network request
and have all of them come back with data in time for the screen
to come on so to solve this problem,
what we're going do is collect the data for your complications
in the form of a timeline in advance.
So you will give us a timeline of data,
how much of the timeline depends on the density
of the data for your application.
And that way as I glance at my wrist throughout the day,
every time I look at it, the complication has already updated
to display the information that makes sense at that moment.
Now, timelines are a really powerful concept
and Paul will talk about them later in the session,
but I wanted to mention one other thing that this buys us,
which is the Time Travel feature.
So Time Travel is a new feature we introduced in watchOS 2
which allows you to turn the Digital Crown and right
on the clock face you can see what your complications will be
showing at different times of the day.
So you could peek forward to see when does the next match start
or what meeting should I have been at an hour ago and so on.
So to see how that works with this complication,
there is no extra work that you had to do
in order to make this happen.
By providing your data in the form of a timeline,
we can just make Time Travel happen for free.
Getting started, so when you make a new Xcode project
or convert an old project over for watchOS 2, you can choose
to add a complication.
You can check that box, and that will cause Xcode
to automatically generate the files that you need
and set things up so it's super easy.
There is one other thing you need to do.
If you navigate to your WatchKit app extension's general info
pane, you provide the dataSource class which we will talk
about later, and you also provide checkboxes
for the families of complication that you want to support.
So now what are these families of complication?
Complications on watchOS look different ways
on different clock faces and we have divided this
up into five different families
and you can choose individually whether to support each one
of these families so I want
to show you what these families look like.
This is the Modular face,
and it has two complication families on it.
The ModularSmall family gives you these square-shaped small
complications, and the ModularLarge family is the one,
one complication in the middle of the face
that can show a fair amount more data
so that's the one we have been focusing on with this example.
A number of analog faces also support complications.
And these complications are in the Utilitarian families.
There is a UtilitarianSmall which kind of fit in the corners
of various analog faces and then there is UtilitarianLarge
which gets the entire region along the bottom.
And finally, on the Color face, there is a single family
which we have called Circular Small.
Here is what this looks like in code.
It's an enumeration named as you would expect.
So when the user activates your complication on a face,
they are going to choose it for a particular position
that the complication can appear
and that position will be associated
with a particular family.
So you will be told that you are providing data for this family,
and at that point, you need
to decide how you want to lay that data out.
And there is a number of ways that you can lay out your data
for each one of these families and I want
to show you the collection of templates
that our designers have created.
So for the ModularSmall complication family,
we have these templates.
There is a whole bunch of different ones.
You can have small text, two different rows of text, an image
and text, you can do a big piece of text, a big image.
These ones along near the bottom here with a ring filling
up can show you your progress towards something in the form
of a floating point number that can change between 0 and 1
and you can put text or an image inside of that
and finally there is a column template which allows you
to do something like show a sports score.
ModularLarge also has a number
of different ways you can layout your data.
There is a simple standard three-line template
with an optional image.
You can do a column-style template,
two different column-style templates, actually.
And finally you could do a template with a large piece
of text which is suitable for something like a kitchen timer
or a date, many other possible things you could do with that.
UtilitarianSmall, most of these guys are flat
and they have an optional image in the corner,
but you can also have a larger image
and you can also do this ring style with UtilitarianSmall.
UtilitarianLarge, there is just one template for this.
There is an image that's optional
and some text next to it.
And finally, CircularSmall has templates that are similar
to the ModularSmall ones although
with slightly different sizes.
So that's the templates that you have access to in watchOS 2.
I'm going to take a look at what that means in code.
So let's zoom in on our soccer club digital
This has four pieces.
There is a header image that you can provide.
And then there is two lines of body text.
So you may have noticed that there is a lot
of these complication templates.
All of these correspond to a subclass
of CLKComplicationTemplate which is the superclass of all
of them, and you can choose which one you want to use
and fill out its various properties
so it's pretty simple.
So this one is the CLKComplicationTemplate
ModularLargeStandardBody which is a bit of a mouthful,
but it conveys one really important piece
of information right in the name,
which is that this template is
for the ModularLarge complication family.
And it's really crucial when you are asked
to provide complication data for a particular family
that you provide a template that matches that family,
and that's why we have built the name of the family right
into the name of the class,
so there can be no confusion about it.
It obviously wouldn't work to produce a template that looks
like this to appear on the circular face.
Now, you may have noticed something else that's a little
We have an image and three text properties but instead
of UI images and NSStrings in the code,
we have this imageProvider and textProvider business.
So what's going on here?
That brings us to our next section,
how do you provide images and text,
and I want to explain what these classes are for
and what they can do for you.
Let's start with images.
Here is our complication in the color editing screen.
So the user can customize what complications they see
and they can pick your complication
in the course of that.
They can also customize the look and feel of the face.
And that includes changing the color.
So when you provide images for your complications,
these images need to take part in that same color scheme
that the user has selected for their face.
So they need to be able to change color as you see.
So there is our soccer ball obediently turning whatever
color the user is choosing.
So an image provider is a sort of a bundle of properties
that manages to achieve this effect.
So you can provide a background image and you provide it
as a template image, an image
that only contains alpha information
and no color of its own.
It can be -- the pixels can be whatever color you want,
but we are only going to pay attention to the alpha channel.
It will be colored depending on the user's selected color,
but you can do a little bit more with this.
You can also provide a background image
and a foreground image and these will be laid
on top of each other.
The background image will be colored according to the color
of the face and the foreground image will be superimposed on it
so you can get a little bit more detail in your images.
You can also choose to make the foreground image black.
So let's take a look at the code for this.
You give us a background image.
You give us an optional foreground image.
You can choose a background color for your background image.
For the most part the color is going to be determined
by what the user has picked
so this background color represents what the color you
would like your background image to be if you can choose
and there are some contexts in which that would be honored,
but as long as this is appearing on a face
where the user chooses the color is will override the color you
supply here so this is an optional property.
Okay. That's image providers.
So now what about text providers?
These are really cool!
So I'm really excited to get to tell you about them!
When we started out building complications
in watchOS 1 we had challenges which were mostly due
to the fact that complications were tiny compared to all
of the other UI you are used to creating
for all of our platforms.
Some of them are as small as 44 pixels square, and we are trying
to fit information that's of use in this very small space,
but it can be challenging to do that without having all
of your text truncate.
So a good example, so the idea here for text providers is
that because the space is very constrained,
you want to leverage some of the work that we have already done
to figure out how to format and fit the text in the small space.
So we are introducing text providers as a way for you
to declare your intentions to us rather
than always passing us an already formatted string
and then we will handle the details of formatting
and fitting that string for you.
So an example is formatting dates.
There is a CLKDate text provider that does this for you
and I want to show you what that's useful for.
Imagine you wanted to display the date, Wednesday,
September 23rd in one of your complications.
Now, space is really constrained so pretty much
in most contexts you are more likely to see something
that looks like this, which is obviously not very informative.
We have lost kind of the bulk of the information.
So instead of truncating, it would be better
if we could fall back on increasingly narrow renditions
of this string that were still informative.
So, for example, we could start abbreviating some
of the elements.
You could abbreviate more of them,
and if that still didn't fit, we could even start dropping some
of the elements instead of truncating,
preferable to see Sep23 versus Wed dot dot dot.
And finally, if we had way less space to deal with we could drop
down to displaying the day of the month.
This is what CLKDate text provider will do for you.
You have a date you want to display,
you have units you would ideally like to display,
in this case the weekday, the month and the day.
You create a text provider from these pieces and then
that very text provider can be attached to one
of these templates and it will look different depending
on the context.
So here it is,
in the ModularLarge complication displaying one
of the longer renditions of this date.
Here are two of these very same text providers produced
with that very same code displayed with widths
of various degrees of further constraint
and here is the same text provider providing a date in one
of these CircularSmall complications
and there is no truncating anywhere
and there is no work you have
to do beyond the code that you see here.
That's date text providers.
There is other kinds of text providers as well.
The one you are most likely to use most
of the time is the simple text provider this allows you
to provide arbitrary text.
And but it's better than an NSString because in addition
to providing the text you would like to see,
you can also provide a shorter version and we will fall back
on the shorter version before truncating the ideal version.
There is a time text provider
which formats times for you nicely.
You get the nice small caps that match the rest of the system
and will drop the a.m.
/p.m. if there isn't room to fit it.
As you see in the sunrise/sunset complication
which is using this text provider at the bottom.
There is also a time interval text provider.
This text provider is good for formatting ranges of times.
We use it in the calendar complication
and it has some nice formatting features as well.
It will look like this if the first date is in the morning
and the second date is in the afternoon.
If they are both in the afternoon, it's smart,
and it drops the redundant a.m.
/p.m. symbol to make this look nicer.
It will also fall back on narrower versions
if this rendition won't fit.
These are useful and we encourage you to use them.
There is one more I wanted to talk about now, and before I get
to it, let me show you the problem it's solving.
So here you see our moon phase digital
And at the bottom of that, the third row
in that moon phase complication it's telling us the amount
of time until the moonset in terms of hours and minutes.
So the moonset is at 2:19 today, which is in 3 hours
and 1 minutes from 11:18.
So as time ticks by, this string changes at 11:19,
it shows 3 hours, at 11:20 it shows 2 hours 59 minutes,
and so on.
Now, imagine if you were creating a timeline
to populate this complication.
You would need to provide a new template
for every minute of the entire day.
That's a lot of templates.
And it could be even worse
because that's the moon phase complication ticking
down by the minute.
What about the timer which ticks down by the second?
Imagine how many templates that is.
It's more than we could possibly reasonably cache
and it's incredibly wasteful
because these strings are derivable
from two pieces of information.
The date you are counting down to, and the date that it is now.
And we know the date that it is now.
That's what we are doing.
We're a clock.
So we wanted to give you a way to produce these strings
without populating your timeline full of just a massive quantity
of redundant information,
that's what the relative date text provider does.
Here is what it looks like in code if we were trying
to produce the third line of text that you see here.
We would get the date for the moonset.
We would choose the units that we wanted to display
and actually I will show on the next screen there is a lot
of different units you can use here.
We would choose a style, and, again,
I will show you the possible styles.
This one you see here is the natural style.
You would make a relative date text provider
out of these elements.
And then you would just set that text provider
as your body 2 text provider for your template
and the clock face does the rest.
It will always display the relative date to the date
that you gave us at every given moment without you having
to do any further work.
So these are some styles that are available,
and as you can see if you look at the natural and style
and the offset style, you can get either fine grained relative
dates or very course grained relative dates so depending
on the date that you wanted --
depending on how far it is to the date you want to display,
we can show weeks and days, months and years, so on,
all the way down to seconds.
So that's text providers and image providers.
So I want to sum up how it is
that you are giving us your content.
You choose a template from one of the number
of possible templates, choosing one
that matches the complication family you are being asked
to provide data for.
And then you populate that with image providers
and text providers.
And then you are going to hand us a whole bunch of those
in the form of a timeline and to talk about more, to talk more
about how to form one of these timelines I want
to invite up Paul.
Here he is.
PAUL SALZMAN: Awesome!
So as Eliza mentioned we are going to be gathering our data
for your complication in the form of a timeline.
This helps to support two things:
the brand-new Time Travel feature and we are going to able
to present the user with your content immediately
on wrist raise without any delay.
Let's take a look at about how to think
about timelines and complications.
We are going to start
by creating the weather complication up here
on the bottom left corner.
We are showing 57 degrees because right now
when the watch is showing 10:00 a.m., our forecast says
that 57 degrees is the temperature outside
for this location.
And in fact, we actually have a forecast by hour
until 7:00 p.m. today for this location
that we can take advantage of.
For timelines we don't describe a range we associate the data we
want to show with a point in time.
So let's slide these over here.
As the clock progresses throughout the day.
We will show the most recent data you have provided
on the timeline on the watch face.
So as we get past 11:00 a.m.
we will update the forecast
to 55 degrees.
As we inch closer to noon at 11:59
and 59 seconds we are still at 55 degrees.
Once we hit noon we will update the template.
This works similarly for the Time Travel feature.
As the user navigates throughout the day we will show the most
recent data available at that point in your timeline.
So as we get past 1:00 p.m.,
we're going to update your content to 54 degrees.
The other complication is kind of an easy example
because your data matches perfectly
to unblocked time on the timeline.
Let's look at something more complex by trying
to build the calendar complication here.
If you are lucky your calendar has plenty
of gaps throughout the day.
Today I'm going to go have brunch
and later I will get a haircut
and when I look better I will meet
with friends and watch a movie.
So we can be naive about this
and associate the templates we want to display for these events
for they begin and clear them out when the event ends.
Let's see how this works in practice.
So at noon today we will show that we are at a brunch,
but once brunch is over and it's 1:00 p.m. we no longer have
content displaying on the wrist watch.
That's a bad user experience.
What's worse though is we get closer and closer
to 4:00 p.m. I have no idea I need to get
into my car and get a haircut.
So you never want to tick off the person that's doing
So now it's too late and I'm going to get a perm
and it's not going to work well.
Let's fix this.
The first mistake is assuming we should have blocks
for unused time frames in the timeline.
So let's get rid of those.
And showing an event isn't useful
for a calendar complication.
We should put the templates at the end of the previous event
so you have adequate time to get to your next event.
So the first event over here actually,
there is no previous event so it might be useful
to actually tag it at midnight to you get adequate warning
in the morning when you wake up
and on the right we should let users know there is nothing they
should be worried about for the rest of the day
with an indication there is no more events.
How does this look?
At noon just like before we will know we are at brunch
but once brunch is over we have adequate heads-up
to know we need to get a haircut.
We will meet with our friends
because we didn't miss any events
and when we are done watching a movie we can go home
with knowing we didn't miss out on anything else.
So how do you get the data points into your code?
You will use the complication timeline entry.
So Eliza described earlier generating templates using text
and image providers and all we need to associate
with that is an NSState.
We will stuff the objects
into the CLKComplication timeline entry.
When you hand that to the clock face we will inspect the date
and make a note on the timeline so we know
to display the template when you reach that time.
You can see in code what the object looks
like with the Date property and complicationTemplate property.
So how do you actually communicate this data to us?
You will have an object in your project
that implements the CLKComplication
This object is annotated in your Xcode WatchKit extensions target
settings as Eliza showed you during setting up your project.
There are a bunch of callbacks you will get on the object.
They are generally per complication.
And we will pass you a CLKComplication object
that has a Family property you will want to switch on.
At this point you will decide is this ModularLarge?
Which template should I use?
Is this ModularSmall?
Which text providers go with the template I'm choosing for that?
And in addition to passing the complication
that you are interested in providing content for,
we are also going to give you a handler.
And you use this handler to give us the data we have requested
and let us know you are done running.
This is very important because you are going
to have opportunities to refresh your content in the background.
We want to know when you are done running.
So let's start building up our timeline.
You will see we have our clock face on the left, your extension
on the right and the timeline we want to build across the bottom.
Inside of your extension you will have your complication
Now, this is the default object that Xcode will create for you
that implements the ComplicationDataSource protocol.
This is the object we are going to be communicating with.
So to get started, we are going to ask you
which timeline entry should we be displaying right now.
So you will package up a timeline entry and send it
over the wire via the handler, and then we're going to add it
onto the timeline right then.
What's important to note with all
of these questions we are asking you is
that we are basing the next questions
on the information you have given in the previous one.
So we are going
to be incrementally building this timeline out.
We want the data to be super accurate.
You don't want to blindly tag the current NSDate
for this entry.
If it's 10:30 a.m. and we want 10:00 a.m. forecast data you
should tag this complication timeline entry with 10:00 a.m.
So the function we will ask is GetCurrentTimelineEntry
Again, we are going to pass you a CLKComplication.
This has a Family property you should switch on to decide
which template, which text and image providers you want
to supply, and a handler that takes the timeline entry
that you should call when you are done with this callback.
So now we have your current entry and we need
to start fleshing out your timeline
to the future and the past.
Let's start looking to the future.
We are going to ask you incrementally what timelines you
have after a specific date.
This date will generally be based off
of previous data you have handed us
and what's still in our cache.
So we will hand you this date and you will package up an array
of CLKComplication timeline entries that start
after this date non-inclusively.
A good rule of thumb is charting a day's worth of data.
When you send this data over the wire, we will add it
to your timeline and if we feel we need
to cache more data we will ask you again.
In which case if you have more data
to give us you happily provide the array.
Let's say we keep asking you
and there is no more content to show.
You can pass an empty NSArray or nil
and that will give us the hint to leave you alone.
As time progresses all of your entries will be further
and further into the past so it's possible
to increase our cache, we might have
to query you again in the future.
So the future we are calling here is getTimelineEntries
We are always going to pass you the complication we are curious
about, so look at the Family property.
And then, of course, we are going
to give you the after date.
This is the non-inclusive date.
You will package up the adjacent forward-looking timeline entries
from this date.
And to make sure we don't get overloaded with data,
we will pass you a limit parameter here.
So don't put any more content into the arrays
than this limit provided here.
If you do, we will remove them in a way we are not going
to guarantee won't change and you are not going
to find out what that means.
And then, of course, a handler that takes this array.
And corresponding to go into the future we have the before date
feature of this function that helps flesh out the past.
So depending on the type
of complication you are providing your needs
for Time Travel may vary.
Whether a complication that only provides future forecast doesn't
want to Time Travel into the past
and a stock complication can only Time Travel to the past
because we haven't perfected looking
into the future for that.
So we will ask you on setup which directions you support.
So for the case of our weather complication,
we will say we only support the future
and that way our timeline will only look forward.
If the user Time Travels into the past we'll actually dim
out your content so they know there's nothing there
to look at.
Similarly for the stock complication,
you may say you only provide the past.
And we will dim in the other direction.
It's possible that you don't want to support Time Travel,
but you still want to get contents onto your watch face.
In that case, you want to supply none as an option.
We will still show your content but as soon
as we enter Time Travel we'll actually dim it out.
It's important to note that you might know some
of the content you want to show in the future.
We will ask you, even though you're not supporting Time
Travel forward, for data we might possibly be able to cache.
We will never ask you about the past
because right now time doesn't travel backwards.
And if you want to a truly bi-directional Time Travel
experience you can support forward and backward.
So the function we will call
to get this information during setup is getSupportedTimeTravel
We will pass you the complication we are asking
about as well as a handler that accepts these directions.
Now, giving an indication
to what directions you support may not be the full story
for your Time Travel complication.
For instance, our weather complication only had a forecast
up until 7:00 p.m. but Time Travel goes beyond that.
So let's take a look at what happens.
As we Time Travel forward to 4:00 p.m. we still have data.
All of our complications are updating.
But once we get to 7:09, just past 7:00 p.m.,
we don't want our users to think we have valid data
for the temperature in the area so we will dim
out your complication here.
So the way that we figure out when to actually dim
out your complication is by querying you for the end
or beginning of your timeline depending
on the directions you support
so we will ask you during setup how far out into the future
if you are a forward complication,
and you will give us an NSDate back.
If you support Time Travel to the past, similarly,
you can provide us another NSDate to this question
and we will adjust accordingly.
We will adjust accordingly.
So the function we are going to call to find out how far
into the future we should Time Travel is getTimelineAndDate
We will pass you the complication and, of course,
a handler that accepts the NSDate
for the end of your timeline.
Correspondingly, we have getTimelineStartDate to see
when your timeline should begin.
So when a user is customizing the complication, the watch face
and they want to select your complication,
they will select the slot in this case ModularLarge
and use the Digital Crown to end
up on the San Francisco soccer club complication.
You will see here a couple of things
about the state of customization.
There is a caption but your complications entry
that says your application's name
and this is provided by the system.
Now, that we are in the modular large slot we are able
to provide a ModularLarge template
that describes what our users expect to see
and give them the right context
for why they should select our complication.
After they select the complication,
go back to the switcher and back to the live face.
We will start querying you for data and populate the timeline.
We call those templates we present
in customization placeholder templates.
We will query you once on installation and cache
that so we don't have to launch multiple extensions
So during installation, for all
of the complications you support, we will query you
for your placeholder template
to which you provide us a CLKComplicationTemplate.
There is no timeline on the bottom,
this isn't happening live while we are using it.
And we are not using a timeline entry
in this callback right here,
it's just a complication template
because there is no date to associate.
So the function we are calling is getPlaceholderTemplate
ForComplication with a complication and as you are used
to with this pattern, a handler.
So now that you are very comfortable constructing your
timeline, I would like to bring Eliza Block
up to give you a demo of how
to construct this in code [applause].
ELIZA BLOCK: Okay.
So I have got a project here
that I haven't done very much to yet.
We are going to actually build the complication
that we have been showing you pictures
of throughout the session.
So let me just show you a little bit about what's going
on with Xcode project.
I created a new project, I dragged a couple resources in,
including a model that I had previously written
to provide this schedule.
And I configured it to work with complications, so if I navigate
over here to my -- oops!
General settings for my Watch extension.
There it is.
If I zoom in, you can see at the top
that I have got my dataSource class.
It looks a little ugly in Xcode at the moment
but if you click it you can see it is pointing
to our complication class.
And then I have, for now, unchecked all
of the supported families except for ModularLarge just
to make things simpler in the demo we will just focus
on building the ModularLarge complication.
We recommend that you do try to support as many
of these different families as you can
because your users will be interested
in choosing different faces and so the more
of these families you support, the more likely they are
to be able to use your content in their watch face.
So when you created this Xcode project, Xcode and say
that you want to support complications,
Xcode actually makes a complicationController object
with stubs for all of the methods
that you need to implement.
And this is pretty handy.
We can go through it and just flesh it out
and have it give us the information that we need.
So I'm going to start at the bottom here.
I'm going to add some extra space so we can see.
At this getPlaceholder TemplateForComplication.
I want to do this first so that we can actually go ahead
and pick the complication and have it look the way
that Paul just showed in the slides.
So I'm going to remove the boilerplate that Xcode provided.
And we are going to make an actual complication template
of the ModularLargeStandardBody class.
I'm going to give it an image which is my soccer ball image.
Now, so I have got my image as a UI image
and I'm creating an image provider using that image
and I will not bother with the background color for now.
Let me show you what that image looks like.
I have it here in Xcode
and I can actually open it up in the Finder.
And here is Preview.
Let me zoom way in.
So as you can see, this is a template image.
It has alpha, an alpha channel
so this is the format you want your images to be
in so they can get properly colorized.
Okay. So there is our image
and we also need to provide some text.
So my header text provider is going to say "Match schedule"
and my body 1 text provider is going to say
"2015 Women's Tournament."
I will not provide a body 2 text provider,
that's optional for this template.
And my goal here is for the text in the first line to wrap
to the second line which will happen
if you omit the second text provider.
We can build and run this with only that much code added.
And then we should be able
to pick our complication in editing.
So I will switch over to the simulators.
And let me just double check that the installation happened.
Yes, there is our app.
So I'm going to force press on the simulator,
customize, scroll over -- oops!
To the complication pane.
Actually, you know what I'm going to do first --
as Paul mentioned we only call this placeholder template method
once, when your app is first installed,
so if you do something like that, you are going to want
to uninstall it so that then -- oh, this is --
make sure that it's gone from the -- oops!
There we go.
So make sure it gets uninstalled.
And then we will have it show up again.
So that way our code will ask you again
for the placeholder template.
So let's try this again.
So let's have it not be there and then -- all right.
We will add it back in.
And let's try this one more time.
Yay! Okay [applause].
So we have our template and now we can actually select
So I'm going to go ahead and home out of there,
and as you can see the complication, of course,
is not showing, switching to showing live data
because we haven't written the part
that provides the live data.
So the -- so it's just going to stick
with the placeholder template which is all
of the information it's got so far.
Let's go back to the code and we will add the part
that actually implements the rest of the protocol methods.
Okay. So let me show you about, a little bit
about the model that I'm using.
I will just switch to the header.
I have got a model and written my model in Objective-C.
You can mix and match Objective-C and Swift
in projects so you can take code written in an existing app
and pull it over into your new watchOS 2 app and as long
as you include the header for that
in your Swift bridging header,
it will just work, which is cool.
So here is my soccer match model object.
And these guys have a date which is the date
that the match begins.
They have a team description which tells you who is playing,
and what group in the tournament this is.
And also the model can tell me what the first match
in the schedule is, what the last match is
and then each match can tell me the previous and the next.
So with that handy, we can start writing the code that's actually
going to populate this complication.
So I'm going to write two helper methods
to solve the two problems that we need to figure out,
basically the two design questions that we need to figure
out for this complication.
The first one is what should our template actually look like?
And for that, I'm going to write a method
that takes a soccer match
and provides a CLKComplicationTemplate object.
So this is pretty straightforward.
We want to build a -- all is well --
build a ModularLarge standard body template like we did
for the place holder and get
that same soccer ball image provided as an image provider
and then we are going to have three lines
of text for this one.
So the header is going to be the time of the match.
And I'm using a CLKTime text provider for that purpose.
And then the first line of text underneath the header will tell
us which teams are playing and I'm using a simple text provider
with my matches team description, and then finally,
the third line is the group description.
So now we have got a template.
We also have to decide how we are going
to arrange these templates on our timeline.
Now, the naive solution which Paul mentioned in the context
of a calendar complication would be to use the match start date
to be the date of our timeline entry,
but that would have the drawback that it has for the calendar
as well which is you wouldn't be able to look
at your complication to see what game is about to start.
You would only be able to see what game has already started.
So we actually want to do the same thing Paul did
with the calendar and move all
of these entries farther forward.
We will have each entry start at the time
when the previous match ended.
So for that, we have to decide how long a soccer match is,
so I have decided that they are about 90 minutes.
So I'm going to use a constant that we could change later
if we discover we are wrong.
So the match duration we will say is 90 minutes.
And then we can write a method timelineEntryDateForMatch
that goes and figures out for any given match
where that should appear on the timeline.
So what we will do is we will say, okay, match,
what match is before you?
And if there is one, then we are going to use the end
of that match as the timeline date for this match.
So if we have one, we will return its date incremented
by the duration of a match.
And if we don't, that means it's the first match in the schedule
and I have pretty arbitrarily decided that I'm going
to start displaying the first match six hours before it starts
but you could obviously do something different here
depending on your use case.
So those are the two methods that are kind
of doing the meat of the work.
Now, we just need to go through and implement all
of the different protocol methods that Paul described.
So let's start from the beginning.
So here we have the timeline configuration
and the first method is getSupportedTimeTravel
The default Xcode template here is saying
that your timeline extends both forward and backwards
and that's what we want in this case
so we will leave that one alone.
The next thing we have to think about is
when our timeline starts.
So because we have written these helper methods we can do
that pretty easily.
I will get rid of the handler with nil
and instead we will figure out a start date
which will be the entry date of the first soccer match.
And let's not forgot to call the handler.
And then the next thing we need
to do is figure out the end date.
So the end date of the timeline should probably be
after the last match is over.
So we will get the last match.
We will get its date, and we will add the match duration
to that date and that's going to be our timeline end date.
We will call that with a handler.
This next method is something we haven't actually gone
into at all in the session, but it's an important method
if your data is in any way sensitive.
So when your user's Watch is locked, you don't want
to be showing sensitive data on the screen
because that's means they have taken it off their wrist
and somebody else could have found it.
If you are showing sensitive data in your complication,
you can use this method to tell us to be sure to not show
that data when the device is locked.
Now, the schedule of a fictional soccer tournament is not
particularly sensitive so I will leave this at default value
of go ahead and show this on the lock screen no problem.
So next we get to the timeline population.
These are the really important ones.
We need to give the current entry.
We need to tell us -- we need to tell the clock how
to extend the timeline backwards and we need to tell it how
to extend the timeline forwards.
I will start with forwards.
So let's skip down to here.
And what we want to do is construct an array
of entries starting at the date we were given and going forward
into the future from there.
My strategy is going to be to make an array,
and arrange to call the handler when we are done.
And then we want to iterate through all of the matches
until we get one that should start after the date
at which point we are going to start creating the templates
for that and sticking them in this array.
So I will start with the first match in my tournament.
And I have made this an optional,
not because the first match might return nil,
but because we are going to change this
to represent each subsequent match and eventually we will run
out of matches it will eventually take on the value
of nil at which point we will know to stop.
So next we are going to -- oops!
While there is a match here, we are going to get the date
that we should display that match at.
That's the timeline entry date for this particular match.
And now we are going to compare that to the date we were given,
which is the date after which we are supposed
to be populating timeline entries.
If they are order descending we have gotten
into the right section of matches and now we want
to start giving back timeline entries for these.
So we are going to populate a timeline entry.
We make a template for the match.
That was our other helper method.
We create an entry from the entry date
that we computed and that template.
We append it to our array, now we need to be careful not
to make too many of these.
We have been past a limit.
We want to adhere to the limit.
So we will just check now that we have added something
to the array, did the count of the array reach the limit,
and if so, we will stop.
And finally, to make the loop work, we need to go
and grab the next match after the match
that we just dealt with.
So that's going to populate the next N entries in the timeline
after the date that we were given,
and we can do something really similar
to populate the earlier entries.
So I'm just going to copy that code that we just wrote.
And here I'm going to move
up to the getTimelineEntries ForComplication before
Paste it. And we need to make three changes.
Here we are going to do the exact same thing except we are
going to start at the last entry and move forward
or the last match, rather, and move back towards the first.
So we will start with the last match and then down here
where the loop happens we grab the previous one.
Previous. And finally we only want to start using these
when they get before the date we were passed.
So we need to switch from order ascending to order descending.
That's our extension methods.
The last thing we need to write is the getCurrentTimelineEntry
method and we can do something tricky and take advantage
of the method we just wrote
because getting the current entry is basically getting the
entry before a particular date, namely now.
That's the one we want to show currently.
So what I'm going to do is call the GetTimelineEntries
ForComplication before date method
that we just finished writing.
I'm going to pass now as the date.
I'm going to pass a limit of one
because we only need one entry here.
And then when I get my handler invoked, I'm just going
to grab the first entry in that array and pass it back
to the handler for the getCurrentTimelineEntry method.
So we can go ahead and run this, and what I'm going
to do is I'm going to run it and then switch quickly
over to the simulator, which as you can see is still running
here and still showing us the placeholder template we
So as soon as I run this, it will invalidate the timeline
on the simulator, at which point it's going to go requery all
of these methods again and now that we have implemented them,
we will actually get values.
So here we go.
Run and then swap over.
And then we should see our stuff populate.
So there we have actual data.
This is showing us -
So this is showing us the game that started at 11:00,
which is the right behavior if you remember, because we wanted
to see the game that started at 11:00
until 12:30, 90 minutes later.
If we Time Travel forwards and reach 12:30,
we should start seeing this change to the next game.
So Time Traveling is working if we get all the way up to 3:00,
we should start seeing the one after that.
Oops! I went way too far.
Our Time Travel is working.
All we had to do was basically fill
out the three most important methods
and we have a functional complication.
So I will pass it back over to Paul who will tell us more
about how to arrange for your complication to update
as information changes in the world.
PAUL SALZMAN: Thanks Eliza.
So now that we are up and running with our complication.
We want to make sure we are always showing something
that is accurate to the world around us.
So in watchOS 2 there is a lot of ways you can get contents
from the surrounding world into your Watch extension.
You can use the new Watch Connectivity APIs to talk
to your companion iOS app and get data onto your Watch.
Or use NSURLSession directly to talk to your web services
and bring content onto the Watch.
So let's say we have our complication timeline built up
and we go out and talk to our web services
and get a new piece of data.
If that data has invalidated our content, we will need
to tell the clock face we want to reload our timeline.
So within our extension, we can get access
to the CLKComplicationsServer object.
That object is our interface into the clock face.
We can make a request to the clock face
to say please reload my data at which point we are going
to throw away all of your existing content
and start our communication channels over again by finding
out your current timeline and flushing things out from there.
You might already notice
that this is a pretty destructive action.
If you had a stocks complication,
where all of your previous data is still valid.
The clock face isn't querying you right now.
It's actually your responsibility
to let us know we either need to invalidate
or possibly extend your content.
So instead of getting rid
of this content you can make a request to extend.
At which point instead of asking you to reload everything,
we are going to ask you to append data at the end
of the most recent content we have available from you.
So how does this look in your code?
You can get access to the complication,
the CLKComplicationServer shared instance.
On the shared instance, you can actually query for all
of the active complications.
An active complication is actually visible right now
on your watch face if you were to wrist up.
And given a complication, you can actually make a request
to the server to either extend the timeline
or alternatively reload the timeline.
So that's great.
We know it's our responsibility to inform the clock face we need
to update, but when do we actually have an opportunity
to do this?
Well, basically any time you are exception is running you can
talk over the CLKComplicationServer
to the clock face.
This happens in a couple of instances,
like when your watch application is foremost.
But you have some opportunities to run
in the background via a locally requested wake or even
from your iOS companion application using some new Watch
Connectivity APIs you can actually wake the extension
from the phone so it can receive the data you have sent over.
But because the two last calls allow you to run
in the background we have to budget them.
If you do a lot of expensive work
in the background calls you can exhaust your budget
and until your budget is replenished you may not have a
chance to update your complication
until later in the day.
So learn more about the Watch Connectivity APIs please go
to the Introducing Watch Connectivity session.
There is also cool push functionality we have added
in this release to support complication data.
Let's talk a bit more
about locally scheduling background wakes in order
to get your complication data up to date via one more call
on the complicationDataSource protocol.
All you are going to supply to us is one date via the handler.
And we are going to make this call across all
of your complications, not by complication.
When we receive this date, we are going to take this
as a hint, and when budgetary constraints
or system conditions are good we'll launch you
in the background.
At this point your data delegates
from Watch Connectivity and NSURL can come in
and it's your responsibility to verify if anything has changed
and make any requests you need to make to the clock face
to update your content.
At this point that's wrapping up our session on complications.
We hope you have learned that to be comfortable with building
up a timeline and supplying us with templates
and the appropriate providers and to take advantage of all
of the hard work that went into the watchOS to actually form
and fit your content in these text providers,
to be comfortable refreshing your data as the world
around changes you, you will get more opportunity to run
if you are a good citizen and do less work
in your background refreshes.
For more information please refer
to your documentation and sample code.
We have good technical support and fantastic evangelists.
There are great sessions to dig further
into WatchKit, thank you!
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.