首页 > 解决方案 > 如何将带有 transferUserInfo 的字典传输到 Apple Watch?

问题描述

我正在尝试将我的 Apple Watch 应用程序的一部分放在付费专区后面。为此,无论内容是否购买,iOS 应用都会自动创建一个具有真/假值的字典。问题是,无论我如何尝试,我都无法将其传递给 Watch。

这是我的 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()
        }
    }
    
    
}

这就是我在 watchOS 的接口控制器上所拥有的:

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()
            }
    }
    
    
}

标签: iosswiftwatchkitwatchconnectivitywcsession

解决方案


更新

查看您的代码后,我注意到两个主要问题:

  1. 您没有将您的设置InterfaceControllerWCSession代表。需要从两端激活连接。
class InterfaceController: WKInterfaceController, WCSessionDelegate {

    private var session: WCSession = .default
    
    override func awake(withContext context: Any?) {
        session.delegate = self
        session.activate()
    }

}
  1. 为了能够从对方设备接收消息,您需要实现session(_:didReceiveMessage:replyHandler:)方法。将这些方法添加到您的InterfaceController
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
    print("watchOS - didReceiveMessage", message)
}

func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
    replyHandler(["does it work?": "yes sir"])
    print("watchOS - didReceiveMessage", message)
}

如您所见,我还实现了第二个函数,它可以replyHandler通过调用 a 来响应 a 传递一些数据。这在调试时很有用。

更新您的按钮操作和sendMessage呼叫。如果设备已经可以访问,则无需重新激活连接,还可以传递回复句柄以确保手表返回数据。

@IBAction func button(_ sender: UIButton) {
    if session.isReachable {
        session.sendMessage(watchInAppPurchases, replyHandler: { reply in
            print(reply)
        }, errorHandler: nil)
    } else if WCSession.isSupported() {
        session.delegate = self
        session.activate()
    }
}

初步答案

不要尝试在调用后直接同步数据,activate()因为不能保证连接已经建立。文档明确指出:

此方法异步执行并在完成时调用委托对象的 session(_:activationDidCompleteWith:error:) 方法。

由于您确实设置self为委托,因此请尝试将transferUserInfo调用移至session(_:activationDidCompleteWith:error:)实现。

func session(
    _ session: WCSession,
    activationDidCompleteWith activationState: WCSessionActivationState,
    error: Error?
) {
    switch activationState {
    case .activated:
        session.transferUserInfo(watchInAppPurchases)
    default:
        // handle other states
        break 
    }
}

此外,在使用 Swift 时,请确保不要CapitalizedCamelCase为属性、函数等使用名称。仅将这种表示法用于类型。我已将原始代码转换为WatchInAppPurchases上面watchInAppPurchases的代码示例。

如果您的调用transferUserInfo仍然不起作用,请尝试调用sendMessage(_:replyHandler:errorHandler:)代替

switch activationState {
case .activated:
    session.sendMessage(watchInAppPurchases, replyHandler: nil, errorHandler: nil)
default:
    // handle other states
    break 
}

并在您的监视扩展中监视会话(_:didReceiveMessage:replyHandler :)是否有任何传入消息。


推荐阅读