watchkitapple-watchwatchos-2apple-watch-complicationclockkit

With a Watch Complication and Time Travel, getTimelineEntriesForComplication is called (too) often


From the data below, ClockKit generates future CLKComplicationTimelineEntry items once, but for past points in time, 24 calls are made! Why is this?

More details:

I'm noticing a curious behavior in my Apple Watch complication.

It supports Time Travel -- I supply data for 1 day past, 4 days into the future. I'm currently trying to be a good citizen and make fewer calls to regenerate my complication data.

To get an idea how often certain calls are made, I put a simple print inside both getTimelineEntries(for:before:limit:withHandler:) and getTimelineEntries(for:after:limit:withHandler:) that outputs the before/after parameters.

When I launch the App/Complication in Simulator, I get the following output:

generate future timeline entries (after date:2016-07-23 10:33:31 +0000)
generate past timeline entries (before date:2016-07-23 10:33:31 +0000)
generate past timeline entries (before date:2016-07-23 09:33:31 +0000)
generate past timeline entries (before date:2016-07-23 08:33:31 +0000)
generate past timeline entries (before date:2016-07-23 07:33:31 +0000)
generate past timeline entries (before date:2016-07-23 06:33:31 +0000)
generate past timeline entries (before date:2016-07-23 05:33:31 +0000)
generate past timeline entries (before date:2016-07-23 04:33:31 +0000)
generate past timeline entries (before date:2016-07-23 03:33:31 +0000)
generate past timeline entries (before date:2016-07-23 02:33:31 +0000)
generate past timeline entries (before date:2016-07-23 01:33:31 +0000)
generate past timeline entries (before date:2016-07-23 00:33:31 +0000)
generate past timeline entries (before date:2016-07-22 23:33:31 +0000)
generate past timeline entries (before date:2016-07-22 22:33:31 +0000)
generate past timeline entries (before date:2016-07-22 21:33:31 +0000)
generate past timeline entries (before date:2016-07-22 20:33:31 +0000)
generate past timeline entries (before date:2016-07-22 19:33:31 +0000)
generate past timeline entries (before date:2016-07-22 18:33:31 +0000)
generate past timeline entries (before date:2016-07-22 17:33:31 +0000)
generate past timeline entries (before date:2016-07-22 16:33:31 +0000)
generate past timeline entries (before date:2016-07-22 15:33:31 +0000)
generate past timeline entries (before date:2016-07-22 14:33:31 +0000)
generate past timeline entries (before date:2016-07-22 13:33:31 +0000)
generate past timeline entries (before date:2016-07-22 12:33:31 +0000)
generate past timeline entries (before date:2016-07-22 11:33:31 +0000)

Solution

  • There's no stipulation that your datasource method will only be called once. You simply need to be prepared to handle the request.

    Why does it handle past entries differently than future entries?

    I believe this is an optimization designed to prioritize upcoming timeline entries (over recent past entries).

    Back in watchOS 2.0.1, the complication server first filled the timeline with future entries, before proceeding to backfill the timeline with past entries. It's certainly understandable how Apple might then choose to batch past entries to prioritize more recent entries over more distant ones.

    While Apple has certainly tweaked its code since then, it likely that the same level of attention and detail continues to apply in the current version of watchOS.

    What's if it's an expensive operation for me to complete?

    If you have to do more than simply use existing complication data at hand, and your data retrieval is tied to the specific request that the system makes, you should refactor that code to move it outside that method.

    In general, you want to have prefetched data already on hand that can simply directly be turned into timeline entries.

    This becomes far more significant in watchOS 3, when you are not merely updating a complication, but also updating your app in the background. Even if you still need to support watchOS 2 now, you should design how data is fetched and complications are updated, with a view to how watchOS 3 could accomplish updating your entire app.

    Why does it call my datasource method more than once?

    Apple uses a variety of techniques to optimize how the complication server (or other parts of the operating system) works. All you can do is trust that the system is tuned to reduce its memory and energy usage, and it does what it does for a good reason.

    This has actually been a standard practice for many years, and is commonly seen in UITableViewDataSource, where a method like numberOfSectionsInTableView might be called several times.

    As another poster has written, the number of times a method is called can change between different versions of the OS.

    What if it's a bug?

    It's likely working as intended, but if you think it's a bug, you can create a minimal sample project and submit it with a bug report to Apple.

    An unrelated note about time travel

    I noticed that you want to offer 4 days of future time travel entries. This will exceed the time travel sliding window.

    For efficiency's sake, you may want to consider latestTimeTravelDate:

    When constructing your timeline, do not create any entries after this date. Doing so is a waste of time because those entries will not be displayed right away.