How to correctly implement transfer of settings from iOS app to watchOS2 complication

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?

  • How to prevent the Apple Watch from going to sleep?
  • Reading NSUserDefaults in watchOS 2 (I know App Groups doesn't work)
  • Passing Dictionary to Watch
  • requestedUpdateDidBegin just called once
  • The anatomy of getting chat server data from iOS to WatchOS?
  • Call ExtensionDelegate to create/refresh data for Complication
  • 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?

    Solutions Collect From Internet About “How to correctly implement transfer of settings from iOS app to watchOS2 complication”

    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.