iosswiftwatchkitwatchconnectivitywcsession

How do I transfer a dictionary with transferUserInfo to Apple Watch?


I am trying to put a part of my Apple Watch app behind a paywall. For that, the iOS app automatically creates a dictionary with a true/false value, whether the content is purchases of not. The problem is, is no matter how I try, I cannot pass it to the Watch.

Here is my iOS ViewController:

import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate {
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    
    //The dictionary to be passed to the Watch
    var dictionaryToPass = ["product1": 0, "product2": 0]
    
    
    //This will run, if the connection is successfully completed.
    //BUG: After '.activate()'-ing the session, this function successfully runs in the '.activated' state.
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("WCSession - activationDidCompleteWith:", activationState, "and error code:", error as Any)
        switch activationState {
            case .activated:
                print("WCSession - activationDidCompleteWith .activated")
                //session.transferUserInfo(dictionaryToPass)
            case .inactive:
            print("WCSession - activationDidCompleteWith .inactive")
            case .notActivated:
            print("WCSession - activationDidCompleteWith .notActivated")
            default:
                print("WCSession - activationDidCompleteWith: something other ")
                break
            }
    }
    
    
    func sessionDidBecomeInactive(_ session: WCSession) {
        print("WCSession - sessionDidBecomeInactive")
    }
    func sessionDidDeactivate(_ session: WCSession) {
        print("WCSession - sessionDidDeactivate")
    }
    
    
    //Pushing the button on the iOS storyboard will attempt iOS-watchOS connection.
    @IBAction func tuiButton(_ sender: UIButton) {
        let session = WCSession.default
        if session.isReachable {
            session.transferUserInfo(dictionaryToPass)
        } else if WCSession.isSupported() {
            session.delegate = self
            session.activate()
        }
    }
    @IBAction func sendmButton(_ sender: UIButton) {
        let session = WCSession.default
        if session.isReachable {
            session.sendMessage(dictionaryToPass, replyHandler: { reply in
                print(reply)
            }, errorHandler: nil)
        } else if WCSession.isSupported() {
            session.delegate = self
            session.activate()
        }
    }
    
    
}

And that's what I have on the watchOS's Interface Controller:

import WatchConnectivity

class InterfaceController: WKInterfaceController, WCSessionDelegate {
    
    //The text label on the Watch Storyboard. Helps with debugging.
    @IBOutlet weak var helloLabel: WKInterfaceLabel!
    
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("watchOS - activationDidCompleteWith:", activationState)
    }
    
    
    //Whatever arrives, it will get printed to the console as well as the 'helloLabel' will be changed to help the debugging progress.
    //BUG: This is the part, that never gets run, even tough the WCSession activated successfully.
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        print("watchOS - didReceiveUserInfo", userInfo)
        helloLabel.setText("didReceiveUserInfo")
    }
    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
        print("watchOS - didReceiveMessage", message)
        helloLabel.setText("didReceiveMessage")
    }
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
        replyHandler(["does it work?": "yes sir"])
        print("watchOS - didReceiveMessage", message)
        helloLabel.setText("didReceiveMessage")
    }
    
    
    //Setting the Interface Controller as WCSession Delegate
    private var session: WCSession = .default
    override func awake(withContext context: Any?) {
        session.delegate = self
        session.activate()
    }
    
    
    //Activating the session on the watchOS side as well.
    override func willActivate() {
        if WCSession.isSupported() {
                let session = WCSession.default
                session.delegate = self
                session.activate()
            }
    }
    
    
}


Solution

  • Turns out it was the watchOS simulator that was buggy. Quite a pity one from Apple.

    Further reading on Apple's forum: https://developer.apple.com/forums/thread/127460

    If anyone else is in the same shoes, I recommend running the code on a physical device, it works perfectly there. The final working code can be found here if anyone from Google results is looking for that.