Search code examples
performancewatchkitapple-watch-complicationwatchos-3

Updating the complication gradually degrades the Apple Watch app performance in watchOS3


I've been stressing over this issue for about a week now, trying to pin point the source of a slow but steady Apple Watch app performance degradation. Over the course of about two days, my app's UI would become progressively more sluggish. I've narrowed it down to a complication update code. Even if I strip down the complication update to an absolute bare minimum, this problem still happens, albeit slower than if I update the complication with some actual data. I update the complication every 10 minutes. Once the new data comes, I simply execute

for (CLKComplication *comp in [CLKComplicationServer sharedInstance].activeComplications) {  
     [[CLKComplicationServer sharedInstance] reloadTimelineForComplication:comp];  
}  

which in turn calls:

- (void)getCurrentTimelineEntryForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimelineEntry * __nullable))handler {  
...  
}  

This works fine, the new data displays, but when repeated a few dozen times, the UI responsiveness of the main app begins to noticeably degrade, and when it's repeated about a hundred times (which happens in less than a day with 10 minute updates) the UI really slows down significantly.

I have nothing fancy going on with the complication structure - no time travel, just display the current data, and everything is set up for that. To make sure I'm not looking at the wrong place, I've made a test that reloads the timeline every second, and in this test, my getCurrentTimelineEntryForComplication looks like this:

- (void)getCurrentTimelineEntryForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimelineEntry * __nullable))handler {  
     handler(nil);  
}  

so there's literally nothing going there, just send back the empty handler. Yet, even in this scenario, after a hundred or so timeline reloads, the main app's UI slows down visibly.

Some other things to note:

  • If I'm not updating the complication, the app's UI performance never degrades, no matter how many times I open it, or how long I use it, or how many times the data fetching code runs in the background.

  • When testing this in the simulator, I can't get the performance degradation to happen, but I can consistently see that there's a small, but steady memory leak coming from the complication update (again, this happens no matter how simple update I do inside the getCurrentTimelineEntryForComplication method.

Has anyone else noticed this, and is there any hope to deal with it? Am I doing something wrong? For the time being I make sure only to update the complication if the data has changed, but that just postpones the problem, rather than solving it.

Oct 24 edit

I've done more careful testing on a real watch, and while before for some reason I didn't notice the memory leak associated with this on a real watch, I have now definitely seen it happen. The real device mirrors the issue seen on the simulator completely, just with a different initial amount of memory allocation.

Again, all I do is call reloadTimelineForComplication on a constant loop, and the complication is updated with a single line of text from a cached data object, and the complication controller is otherwise stripped to a bare minimum. When the complication is removed from the watch face, memory leak predictably stops.

My main project is written in ObjectiveC, but I have repeated the test with a test project made in Swift, and there are no differences. Also, the problem persists with the latest XCode 8.1 GM and the watchOS 3.1 beta that's supplied with the simulator that comes with it, as well as running it on a real watch with watchOS3.1 installed.

Jan 24, 2017 edit

Sadly, the issue persists in watchOS 3.1.3, completely unchanged. In the meantime I've contacted Apple's code-level support, sent them sample code, and they've confirmed that the problem exists, and told me to file a bug report. I did file a bug report about two months ago, but up until now it remains unclassified, which I guess means no one looked at it yet.

Jan 31, 2017 edit

Apple has fixed the problem in watchOS3.2 beta 1. I've been testing it both in the simulator and on real watch. Everything's working great, no memory leaks or performance degradation anymore. In the end there were no workarounds for this, until they decided to fix it.


Solution

  • Apple has fixed the problem in watchOS3.2 beta 1. I've been testing it both in the simulator and on real watch. Everything's working great, no memory leaks or performance degradation anymore. In the end there were no workarounds for this, until they decided to fix it.