Your next career begins with
Save 50% off your seat in our next iOS Bootcamp. Limited time only. Sessions start April 3.
watchOS: Complications
Feb 7 2023 Swift 5.6, watchOS 8.5, Xcode 13
Part 1: Introduction to Complications
2. Create Your First Complication

Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
About this episode
Learn the process of setting up your first complication, starting with a data source.
Instructors
Contributors
Instructor
Illustrator
Over 300 content creators. Join our team.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Watch this episode for free — or access the full course as a Kodeco Subscriber. Learn more about the benefits of subscribing here
This video Create Your First Complication was last updated on Feb 7 2023
Explore the sample
To start, we’ll build a complication for this TideWatch app, so let’s see what we’ve got to work with.
If you build and run from this episode’s starter materials, and wait a moment, you’ll see the current tide conditions at the Point Reyes tide station in California.
You can tap the station name to pick a new location, and see the conditions there.
Even though the app is quite useful, as designed, our customers would have to open the app to find out what the current water level is.
We can put that information right on the watch face with a complication!
Complication data source
When you create a watchOS project, Xcode will generate ComplicationController.swift. For the sample project, I’ve moved it into the Complications folder, here.
The generated file will also come with a lot of boilerplate code, but the only bit that’s required is this CLKComplicationDataSource
method: currentTimelineEntry(for:)
.
The current timeline entry
When watchOS wants to update the data displayed for your complication, it calls this method. You’re expected to return either the data to display right now or nil
if you can’t provide any data.
What happens if you can’t provide a data point for the current time?
If you take a look in your extension’s Asset catalog, you’ll find a Complication folder. If you return nil from that currentTimelineEntry
method, then watchOS will look in here for an appropriately named image to use, instead
For our first complication, we’ll try something that works on the simulator’s default watch face: Meridian.
It uses the .graphicCircular
complication family for most configurable complications.
If the Apple Watch is showing a complication family type you don’t support, then you return nil
.
guard complication.family == .graphicCircular else {
return nil
}
Then, you create a complication template of the appropriate type and configure the text to display.
let template = CLKComplicationTemplateGraphicCircularStackText(
line1TextProvider: .init(format: "Surf's"),
line2TextProvider: .init(format: "Up!")
)
Notice this template takes two text providers. Each template uses different textual or graphical elements, so check the documentation for the various templates to find one that best matches your needs
Finally, return a CLKComplicationTimelineEntry
that specifies the time of the data point and the template to display.
return .init(date: Date(), complicationTemplate: template)
The date specified should never be in the future, but it may be in the past.
To check out our work, switch the active scheme to TideWatch –> WatchKit –> App –> (Complication) and then build and run again.
Using the complication scheme ensures that your supported families are used without caching. The scheme will also launch the simulator directly to the watch face and give your app a small amount of background processing time.
Tap and hold on the watch face, and the editor will appear:
Note: The simulator sometimes has issues bringing up the editor. If the edit button doesn’t appear, quit and restart the simulator, or use a physical device instead.
Tap Edit, then swipe left two times so you can pick the complication you want to replace:
We’ve set up a circular complicaiton, so select any of the small circular ones to replace. Now we can see the list of complications to choose from:
Scroll until you see your app listed… but our app is not listed! So, what did we do wrong?
CLKComplicationDataSource
has an optional method named complicationDescriptors()
.
It is only optional in the sense that your app will compile without it.
func complicationDescriptors() async -> [CLKComplicationDescriptor] {
}
It is not actually optional if you want your complication to be listed.
Note: The previous version of watchOS looked in Info.plist for the supported complications. That’s why the method is optional. Don’t use the Info.plist anymore, per Apple’s recommendation.
As you can see from the return type, we need to provide and array of CLKComplicationDescriptor
items. Each descriptor will appear in the list of complications to choose from.
A complication descriptor needs three bits of information from you:
[
.init(
identifier: ~,
displayName: ~,
supportedFamilies: ~
)
]
}
First, an identifier! Each complication you support should have a unique name. The names should be deterministic, and shouldn’t change between app launches. We’re only going to set up one, so we can use the same sort of reverse-domain naming scheme that we use for our app bundle IDs.
identifier: "com.yourcompany.TideWatch",
The displayName
is what the user sees when choosing a complication from the list that your app supports.
displayName: "Tide Conditions",
And finally, an array of the families this complication supports. For us, that’s just graphic circular.
supportedFamilies: [.graphicCircular]
Build and run again. Tap and hold on the watch face, Edit… And this time, when you scroll, you’ll see your complication listed as an option to pick!
The app name displayed here is based on the Display Name set on your TideWatch WatchKit App target.
So, we’ve made some progress, there’s our complication! But what’s up with the empty circle? Why isn’t it showing the “Surf’s Up!” message we specified in the timeline?
If I select this, I can see our message on the watch face. So, what’s going on? Ahhh it’s cliffhanger! A wavehanger? I don’t really know surf lingo. In any case, we’ll solve this mystery in the next episode.
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development — plans start at just $19.99/month! Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more