2

I'm stuck with the following problem: I'm able to start WCSessions on both iPhone and Watch (simulator and real devices) and can use the sendMessage method just fine. I'm aware that updateApplicationContext requires an updated dict to be sent, so I'm adding an uuid for debugging purposes:

context = ["user":id,"messageId": UUID().uuidString]

There's no error thrown when the method is called but on the watch side didReceiveApplicationContext never gets called and the receivedApplicationContext dict stays empty all the time. After reading a lot of similar code examples and the docs I can't see where I'm wrong.

I'm building with XCode 12.5 and iOS 14.5 and watchOS 7.4

Here's the iOS code dealing with the WCSession, context is set in another method and is successfully transferred with sendMessage but not with updateApplicationContext:

import WatchConnectivity

public class CDVSettings : CDVPlugin, WCSessionDelegate {

var wcSession : WCSession! = nil

var didSendMessage:Bool = false

var context: [String : Any] = [:] {
    didSet {
        debugPrint("context didSet:", self.context)
        debugPrint("WCSession.isPaired: \(wcSession.isPaired), WCSession.isWatchAppInstalled: \(wcSession.isWatchAppInstalled)")
        if wcSession.activationState == WCSessionActivationState.activated {
            do {
                debugPrint("updateApplicationContext is called")
                self.didSendMessage=true
                try wcSession.updateApplicationContext(self.context)
            }
            catch let error as NSError {
                debugPrint(error.localizedDescription)
            }
            catch {}
            wcSession.sendMessage(self.context, replyHandler: { reply in
                print("Got reply: \(reply)")
            }, errorHandler: { error in
                print("error: \(error)")
            })
        } else {
            print("activationState is not activated")
            wcSession.activate()
        }
    }
}

public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    print("activationDidCompleteWith activationState:\(activationState) error:\(String(describing: error))")
}

public func sessionDidBecomeInactive(_ session: WCSession) {
    
}

public func sessionDidDeactivate(_ session: WCSession) {

}

@objc(pluginInitialize)
public override func pluginInitialize() {
    wcSession = WCSession.default
    wcSession.delegate = self
    wcSession.activate()
}

[...]

}

And here's the watch part:

    import WatchConnectivity
    
    class ConnectivityRequestHandler: NSObject, ObservableObject, WCSessionDelegate {
    
    var session = WCSession.default

    override init() {
        super.init()
        session.delegate = self
        session.activate()
        debugPrint("ConnectivityRequestHandler started with session", session)
    }
        
    // MARK: WCSession Methods
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        debugPrint(error)
        debugPrint("session is reachable:",session.isReachable)
        debugPrint("last received application context:",session.receivedApplicationContext)
    }
    
    
   func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) {
        debugPrint("didReceiveMessage: \(message)")
        replyHandler(["message received": Date()])
    }
    
    func session(_ session: WCSession, didReceiveMessageData messageData: Data, replyHandler: @escaping (Data) -> Void) {
        debugPrint("didReceiveMessageData: \(messageData)")
    }
    
    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        debugPrint("did receive application context")
        debugPrint(applicationContext)
            if let id = applicationContext["user"] as? [String]{
                debugPrint("\(id)")
                UserDefaults.standard.set(id, forKey: "user")
            }
        }

}

The corresponding logs on iOS

"context didSet:" ["user": "0e4a28b5-f8a0-40a5-942d-5f13b610a93a", "messageId": "8A78246C-91E6-48DC-B55D-4F4EBC761211"] "WCSession.isPaired: true, WCSession.isWatchAppInstalled: true" "updateApplicationContext is called" Got reply: ["message received": 2021-09-03 08:31:54 +0000]

and on watchOS

"ConnectivityRequestHandler started with session" <WCSession: 0x600003be0b40, hasDelegate: YES, activationState: 0> nil "session is reachable:" true "last received application context:" [:] "didReceiveMessage: ["user": 0e4a28b5-f8a0-40a5-942d-5f13b610a93a, "messageId": 8A78246C-91E6-48DC-B55D-4F4EBC761211]"

What am I doing wrong? Thank you so much for your help.

Nicolas Zimmer
  • 424
  • 4
  • 10
  • Where you have print("activationDidCompleteWith ... don't you want to call update app context if the session activation completed successfully? – Shadowrun Sep 03 '21 at 09:22
  • @Shadowrun The context update happens later in the CDVPlugin's life-cycle, but as the messages are sent only after WCSessionActivationState.activated is (still) true, this shouldn't be the source of the problem, I guess... – Nicolas Zimmer Sep 03 '21 at 14:13
  • Are you observing it just on a simulator or on physical devices as well? I did run into a similar problem with simulators in the past. In my case I was able to transfer the application context, but transferUserInfo never worked: https://stackoverflow.com/questions/65530895/watchconnectivity-wcsession-transferuserinfo-succeeds-on-a-watch-but-the-data-n However, on the real devices the same code worked as expected. I wasn't able to find a solution for the simulators. – Vadim Belyaev Sep 04 '21 at 00:29
  • @VadimBelyaev After rebooting the physical devices the code was working as expected! Thanks a lot for pointing me to that, I've read your question while researching but obviously concentrated too much on the sims. Anyways, really odd how WatchConnectivity simulators are working seemingly random. – Nicolas Zimmer Sep 07 '21 at 09:06

2 Answers2

3

For those stumbling over my question while researching, it's no real solution, but an observation that I (now) share with others (see comments above): With real devices it works as expected, but you may need to reboot them in the process. In the simulators it seems that different methods may work while others won't and that even differs from developer to developer.

Nicolas Zimmer
  • 424
  • 4
  • 10
-1

In Xcode 14 this should now also work for Simulator. At least for me it seems to work without problems

Martin
  • 1,112
  • 1
  • 11
  • 31