watchkitapple-watchwatchconnectivityapple-watch-complicationclockkit

Apple Watch complications are not reliably updated


I have an iPhone app that sends data from the iPhone app directly to the watch face to be displayed as a complication.

I use the WatchConnectivity framework to create a WCSession to send the data to the watch from the phone.

My data is stored in a dictionary, and sent to the watch using WCSession's transferCurrentComplicationUserInfo method. (This method can be used something like 50 times a day, and I am aware of this - that is not the issue.)

The transferCurrentComplicationUserInfo method seems to work the first time that I attempt to send data.

My problem is that my iPhone app is meant to call this function several times in a session, and it only reliably works the first time.

When I send a second set of data, the first set remains on the complication. Often, when I send the third set, the second set appears. Sometimes the second set appears permanently, and sometimes it only appears for a brief second before displaying the third set.

It is inconsistent, and that is the issue I am having.

Is there anything that I have set up incorrectly?

Code:

//iPhone code to send data to Apple Watch:

func sendComplication(complication: Complication) {
        guard let session = session else {
            delegate?.failedToSendComplication(reason: "Could not connect to your Apple Watch.")
            return
        }
        guard let context = convertComplicationToDictionary(complication: complication) else {
            delegate?.failedToSendComplication(reason: "Couldn't cast complication to a dictionary.")
            return
        }
        if session.remainingComplicationUserInfoTransfers > 0 {
            session.transferCurrentComplicationUserInfo(context)
            delegate?.didSendComplication()
        } else {
            delegate?.failedToSendComplication(reason: "Due to hardware limitations, you can only send a certain amount of complications in a day. You have exceeded that limit for today. You can still set complications from the Apple Watch app.")
        }
    }

// WatchKit Extension Delegate to receive and handle data sent from iPhone app

import WatchKit
import WatchConnectivity

class ExtensionDelegate: NSObject, WKExtensionDelegate {

    var session: WCSession?

    override init() {
        super.init()
        self.session = newWatchConnectivitySession()
    }

    func newWatchConnectivitySession() -> WCSession? {
        if WCSession.isSupported() {
            let session = WCSession.default
            session.delegate = self
            session.activate()
            return session
        }
        return nil
    }

    func reloadComplicationTimeline() {
        let server = CLKComplicationServer.sharedInstance()
        guard let activeComplicationFamilies = server.activeComplications else { return }
        for comp in activeComplicationFamilies {
            server.reloadTimeline(for: comp)
        }
    }

}

extension ExtensionDelegate: WCSessionDelegate {

    func sessionReachabilityDidChange(_ session: WCSession) {
        if session.activationState != .activated {
            self.session = newWatchConnectivitySession()
        }
    }

    // Receive info from iPhone app
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        // Parse dictionary and update data source
        reloadComplicationTimeline()
    }

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        guard let error = error else { return }
        print(error.localizedDescription)
    }
}

// UPDATE //

Upon further inspection, I now see that steps are happening out of order.

This is the sequence of events:

  1. sendComplication is called from the iPhone app
  2. ExtensionDelegate is initialized on the Watch app, setting up the WCSession
  3. The complication is updated (too early - this is before the WCSession receives the new data)
  4. The WCSession didReceiveUserInfo delegate method is called, data is parsed, and the data source is updated (too late)
  5. The complication is told to reload, but nothing happens (possible budgeting issue?)

Solution

  • Resetting the iPhone, Apple Watch, and Mac fixed the problem.