Apple Watch complications are not reliably updated
Asked Answered
S

3

7

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?)
Skeens answered 18/2, 2019 at 15:6 Comment(3)
I am not sure what your update means: Is this the solution to your problem? Or is the problem the same after the steps are executed in right order?Withers
It is not the solution. I meant to show that I have discovered that my app is running functions in the wrong order, and I don't know why.Skeens
Did you solve it?Alive
S
0

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

Skeens answered 1/3, 2019 at 21:3 Comment(0)
F
1

Try the following:

func reloadComplicationTimeline() {
    #if os(watchOS)
    let server = CLKComplicationServer.sharedInstance()
    if let activeComplicationFamilies = server.activeComplications {
    for comp in activeComplicationFamilies {
        server.reloadTimeline(for: comp)
    }
   #endif
}



func sendComplication(complication: Complication) {
        guard WCSession.default.activationState == .activated 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 os(iOS)
        if WCSession.default.isComplicationEnabled {
            let userInfoTranser = WCSession.default.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.")
        }
        #endif
    }

Here is a good example from Apple that could help you more: source

Fornax answered 27/2, 2019 at 15:33 Comment(0)
A
0

As you describe it in your update, your iPhone app calls session.transferCurrentComplicationUserInfo(context) before the watch sets up its WCSession. But the docs say:

[transferCurrentComplicationUserInfo(_:)] can only be called while the session is active - that is, the activationState property is set to WCSessionActivationState.activated. Calling this method for an inactive or deactivated session is a programmer error.

Thus I suggest that you implement (if you haven’t done so already) the WCSessionDelegate function session(_:activationDidCompleteWith:error:) (see here), and transfer complication data only after the session has been activated.

Abut answered 25/2, 2019 at 9:53 Comment(0)
S
0

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

Skeens answered 1/3, 2019 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.