WCSession.sendMessage的成功率为50/50。

11

最近,我正在处理一个关于Watch/iPhone通讯的项目。但我的代码有时候能正常工作,有时候就不能工作,这对我来说有点奇怪,因为我认为代码应该要么能工作,要么不能工作,不可能是五五开。所以,我不知道出了什么问题。

在 iPhone 上设置 WCSession:

class WatchCommunicationController: NSObject, WCSessionDelegate {

    var session : WCSession?

    override init(){

        //  super class init
        super.init()

        //  if WCSession is supported
        if WCSession.isSupported() {    //  it is supported

            //  get default session
            session = WCSession.defaultSession()

            //  set delegate
            session!.delegate = self

            //  activate session
            session!.activateSession()

        } else {

            print("iPhone does not support WCSession")
        }
    }

    ... ...
}

在Watch上进行类似的WCSession设置:

class PhoneCommunicationController: NSObject, WCSessionDelegate {

    var session : WCSession?

    override init(){

        //  super class init
        super.init()

        //  if WCSession is supported
        if WCSession.isSupported() {    //  it is supported

            //  get default session
            session = WCSession.defaultSession()

            //  set delegate
            session!.delegate = self

            //  activate session
            session!.activateSession()
        } else {

            print("Watch does not support WCSession")
        }
    }

    ... ...
}

在Watch上发送消息:

func sendGesture(gesture : GKGesture){

//  if WCSession is reachable
if session!.reachable {     //  it is reachable

    //  create the interactive message with gesture
    let message : [String : AnyObject]
    message = [
                "Type":"Gesture",
                "Content":gesture.rawValue
              ]

    //  send message
    session!.sendMessage(message, replyHandler: nil, errorHandler: nil)
    print("Watch send gesture \(gesture)")

} else{                     //  it is not reachable

    print("WCSession is not reachable")
}

}

相关的枚举:

enum GKGesture: Int {
    case Push = 0, Left, Right, Up, Down
}

在 iPhone 上收到消息:

func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {

        //retrieve info
        let type = message["Type"] as! String
        let content = message["Content"]

        switch type {

        case "Gesture":
            handleGesture(GKGesture(rawValue: content as! Int)!)
        default:
            print("Received message \(message) is invalid with type of \(type)")
        }

    }

    func handleGesture(gesture : GKGesture){

        print("iPhone receives gesture \(gesture)")

        var notificationName = ""

        switch gesture {

        case .Up:
            notificationName = "GestureUp"
        case .Down:
            notificationName = "GestureDown"
        case .Left:
            notificationName = "GestureLeft"
        case .Right:
            notificationName = "GestureRight"
        case .Push:
            notificationName = "GesturePush"
        }

        NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil)

    }

我无法在Xcode上调试我的Watch应用程序,调试会话无法附加。我不知道原因。因此,我只能使用iPhone进行单边调试。

有时我会收到“接收手势”打印输出,有时则不会。通知也是如此。


你尝试过“重置内容和设置”,并重新启动你的手表/ iPhone 模拟器和 Xcode 吗?这是获取调试会话附加的提示。告诉我它的效果如何,然后我可以尝试帮助你解决 WatchConnectivity 问题。 - Philip
我使用我的iPhone和Apple Watch进行调试。 - Jay Hu
你为可达性做了什么特殊的事情吗?我的代码几乎完全相同,但是当检查可达性时,我的检查总是返回false - prawn
我不这么认为。我认为只要iPhone连接了WiFi,打开了蓝牙并与Apple Watch配对,它们就应该可以互相访问。 - Jay Hu
3个回答

6
我不确定在WCSession传输过程中,Int是否会被包装成NSNumber。如果是这样,那么当我使用Int作为枚举的基类时,它就不起作用,而使用String作为基类时则能够工作。
连接性已知问题:使用NSNumber和NSDate对象与WCSession API一起使用可能会导致应用程序崩溃。
解决方法:在调用WCSession APIs之前将NSNumber或NSDate对象转换为字符串。在接收端进行相反的转换。
参考链接:Watch OS 2 Beta 4 release note

2

我猜测在sendMessage调用失败的情况下,你没有实现错误处理程序,因此返回了一个错误。在你开始运行时,你可以通过仅打印错误来解决问题,但如果这是发布的代码,你真的应该处理相应的错误:

//  send message
session.sendMessage(message, replyHandler: nil, errorHandler: { (error) -> Void in
    print("Watch send gesture \(gesture) failed with error \(error)")
})
print("Watch send gesture \(gesture)")

我通过将枚举从Int更改为String来解决了这个问题。现在每次都能正常工作。你能想到为什么吗?更多信息:通过更改,每当我按下手表上的按钮来触发此sendMessage时,它会在状态栏上显示微小的progressIndicator。但是更改后不再显示。 - Jay Hu
不好意思,我不太明白您的意思 :) 我可能需要查看实际代码并自己运行它才能为您提供更多答案。 - ccjensen

1
你的流程是正确的,但难点在于如何理解调试方法:
调试监视器:
1. 运行 iPhone 目标,等待完成后点击停止按钮。
2. 在模拟器中手动运行 iOS 应用程序(不要从 Xcode 中运行),并让其挂起。
3. 切换到 Watch 目标(yourAppName WatchKit App),设置相关断点并运行。
4. iOS 应用程序将自动被放置到后台,然后您就可以使用 sendMessage 方法(在 Watch 目标中)发送任何需要的内容。如果您在 iOS 应用程序中有一个 replayHandler,您甚至会在 sendMessage 中收到与之相关的消息(即 InterfaceController)。
小型 Swift 示例:
从 Watch 发送字典到 iOS 应用程序:
    if WCSession.defaultSession().reachable == true {
        let requestValues = ["Send" : "From iWatch to iPhone"]
        let session = WCSession.defaultSession()

        session.sendMessage(requestValues,
            replyHandler: { (replayDic: [String : AnyObject]) -> Void in
                print(replayDic["Send"])

            }, errorHandler: { (error: NSError) -> Void in
                print(error.description)
        })
    }
    else
    {
        print("WCSession isn't reachable from iWatch to iPhone")
    }

从手表接收消息并从应用程序发送回复:

    func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {

      print(message.values)

      var replyValues = Dictionary<String, AnyObject>()

      replyValues["Send"] = "Received from iphone"
      // Using the block to send back a message to the Watch
      replyHandler(replyValues)
     }

调试 iPhone:

这是调试手表的完全相反。

此外,@sharpBaga 的回答有一个重要的考虑因素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接