Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?)
like image 348
bkSwifty Avatar asked Feb 18 '19 15:02

bkSwifty


People also ask

How do you refresh complications on Apple Watch?

If the battery of the Apple Watch is almost empty, WatchOS stops refreshing complication data unless the battery is charged again. A refresh of watch face complications will stop if the myPWS Watch App is killed and no longer executed. To force a refresh of the complication data, just open the myPWS Watch App.

How often do Apple Watch complications update?

Complications will update roughly at :00, :15, :30, and :45 on the hour; the exact timing is determined by the system. Editing a Complication will immediately sync it to the Watch, but you may need to launch the Watch app for the Complications to update.

Why does my Apple Watch keep saying complications?

In reality, when Apple is talking about "complications" on Apple Watch, it really means widgets. Apple Watch complications allow you to change elements shown on the watch face. You have to Force Touch the display and head into the customisation section where you'll find the complications that are offered.

Can you add more complications to Apple Watch face?

Apple Watch complications display information from apps and can be directly added to Watch faces. You can add complications or change them directly on Apple Watch, or via the Watch app for iPhone. Some third-party apps can be added as complications, enabling even further customization of Apple Watch faces.


1 Answers

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

like image 150
Anton Bärwald Avatar answered Oct 07 '22 00:10

Anton Bärwald