1

What I want to achieve is the following:

  1. Complication(s) get updated in the background at intervals of 30 minutes
  2. Complication(s) get updated whenever the watch app runs and receives its own updated data
  3. Complication(s) get updated whenever the iOS app runs and the user changes a setting which affects the watch data (such as changing location of weather observations, or display units)

Items 1. and 2. seem to be straightforward, and nicely addressed here: What is the flow for updating complication data for Apple Watch?

However, for item 3, in the iOS app, I set up a WCSession instance and call transferCurrentComplicationUserInfo, sending the new settings as NSDictionary. In the watch extension, this invokes didReceiveUserInfo in WCSessionDelegate.

- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *,id> *)userInfo {
    // Code here to apply the new settings
    // ....
    // Invoke a NSUSRLSession-based web query to get new data
    [self queryForNewDataWithCompletionHandler:^(NCUpdateResult result) {
        if (result == NCUpdateResultNewData) {
            // Have new data from web to display
            CLKComplicationServer *server = [CLKComplicationServer sharedInstance];
            for (CLKComplication *complication in server.activeComplications) {
                [server reloadTimelineForComplication:complication];
            }
        }
        // Set date for next complication update to 30 mins from now
        // ...
    }];
}

The problem I am having is that watchOS is calling requestedUpdateDidBegin in a separate thread, shortly after it invoked didReceiveUserInfo and this starts executing BEFORE I have a chance to obtain updated data using the new settings in the newly received UserInfo dictionary from the app.

Consequently, the complications get updated twice in short succession - once by the WatchOS having called requestedUpdateDidBegin, which simply re-updates the complication with existing (stale) data, before I very soon after receive new data from the web and then have to update them again in my own code.

This seems unnecessary and a waste of resources, not to mention the limited budget of updates that Apple allows (supposedly 2 per hour).

Am I doing anything wrong here? How can I prevent watchOS2 from calling requestedUpdateDidBegin before I have had a chance to acquire new data from the web?

Community
  • 1
  • 1
gpdawson
  • 713
  • 6
  • 15
  • 1
    Actually it's not even this simple. I had this more or less working ok in the simulator. But when I run it in a TestFlight build, requestedUpdateDidBegin gets called before didReceiveUserInfo does, which makes it really hard to figure out any kind of workaround. Seems to me the whole complications API was not built to cope with presenting data updates obtained from web - it seems much better suited to calendar type data. – gpdawson Nov 14 '15 at 02:44

1 Answers1

2

The purpose of transferCurrentComplicationUserInfo is to immediately pass current complication data to the extension. In your code, you are passing settings, however you are not including any weather data.

The issue you're seeing stems from trying to asynchronously fetch new data within the extension (which is returning before the data is available).

To handle this, you should fetch the current weather data on the phone based on the new settings, then pass (the new settings along with) the weather data in the current complication user info.

- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *,id> *)userInfo {
    // Code here to apply the new settings for future updates
    // ....
    // Code here to update cache/backing store with current weather data just passed to us
    // ....

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

This way, the complication server can immediately update the timeline using the current complication data you just transferred to the watch.

No stale data, no unnecessary second update.

  • yeah but how do you want the complication to be updated everyday without the iphone app being opened to call the async code? – SwiftiSwift May 13 '21 at 09:44