Add sample data
So. The current timeline entry isn’t showing up in this list.
The CLKComplicationDataSource provides another technically optional, but not practically optional method called localizableSampleTemplate(for:)
func localizableSampleTemplate(
for complication: CLKComplication
) async -> CLKComplicationTemplate? {
}
watchOS calls this method when the Apple Watch needs to display the complication selector in the list.
This sample template can also be handy when your app needs to perform an expensive operation or run something asynchronously to update the timeline data. You can use the sample data to make the display happen immediately, and avoid potential side effects in your app.
First, again, make sure the family is one that you support. And here we’ll also make sure there is a default image available to display in the complication preview.
guard
complication.family == .graphicCircular,
let image = UIImage(named: "tide_rising")
else {
return nil
}
Then we’ll generate some sample tide data. The app includes a Tide Core Data model we can use here. So, let’s set a date, height, and type.
let tide = Tide(entity: Tide.entity(), insertInto: nil)
tide.date = Date()
tide.height = 24
tide.type = .high
By inserting nil here, you’re preventing actual Core Data updates from occurring.
For the template, display the image you loaded in the guard statement on top, and the tide height on the bottom.
return CLKComplicationTemplateGraphicCircularStackImage(
line1ImageProvider: .init(fullColorImage: image),
line2TextProvider: .init(format: tide.heightString())
)
The sample project has several handy methods already set up on Tide in Tide+Extension.swift, including this heightString(unitStyle:) method to localize and format the height property.
We’re just focusing on how to update your complication properly, here, and not how to use Core Data. But feel free to investigate this file as you please!
OK! Build and run.
This time, when you try to select the complication, you’ll see a much better display:
We did enter the height as 24, but the app uses meters to store heights. So with the heightString method using MeasurementFormatter behind the scenes, we’ve got a localized display here on the watch face!
You want to surf in a 78.7-foot high wave, right?
Tap the row to select the complication and then go back to the home screen.
You’ll see your complication, but still with no data, here.
Updating the complication’s data
When people first learn about complications, the missing “Ah-ha!” moment is that the Apple Watch will only attempt to update the complication on the watch face when you specify that new data is available.
Imagine the battery drain if watchOS had to query your complication every second to see if a new data point was available?
Telling watchOS there’s new data
Open CoOpsApi.swift, and you’ll see getLowWaterHeights(), the method the app calls when it needs to download new tide data.
Using the Combine framework allows for a very clean data download pipeline, here!
At the top of the file, add an import for ClockKit:
import ClockKit
Then, back down in getLowWaterHeights(), scroll to add(predictions:to:in:).
The app calls this method when new data has been successfully downloaded and decoded from the network.
With some guaranteed data ready, that seems like a good time to tell watchOS you’ve got new data.
DispatchQueue.main.async {
let server = CLKComplicationServer.sharedInstance()
server.activeComplications?.forEach(server.reloadTimeline)
}
So, once your Core Data model updates, you tell watchOS that it needs to reload the whole timeline for any complication currently on the watch face.
Depending on your app and its data model, reloading the entire timeline might not be the most efficient option, but it will work for our example.
If the existing data in your complication’s timeline is still valid, and you’re simply adding new data, you should call extendTimeline(for:), instead.
Note: If you’ve already exceeded your app’s budgeted execution time, then calls to either method won’t perform any action.
Providing data to the complication
Ok! Switch back to ComplicationController.swift
Now we can update currentTimelineEntry(for:) to be a bit more helpful.
First, in the guard statement, if watchOS asks for a family you don’t support, or there’s no current data to display, then return nil.
guard
complication.family == .graphicCircular🟩,
let tide = Tide.getCurrent()🔴
else {...
In the second step, use the image and heightString in a Graphic Circular Stack Image template, just like you did for the sample data.
let template = 🟩CLKComplicationTemplateGraphicCircularStackImage(
line1ImageProvider: .init(fullColorImage: tide.image()),
line2TextProvider: .init(format: tide.heightString())
)
And finally, you can specify the date of the data you’re providing for the timeline, as well as the template.
return .init(date: 🟩tide.date, complicationTemplate: template)
Build and run again.
Give it a moment to download data from the network.
And if you’ve left everything as it was since the last time, like I did, you’ll see real data displayed right there in your complication!
Tap it open the app. And select another location. Note the height! Now if you go back to the watch face… You should see data for the new location!