WCSession的transferUserInfo在watchOS 2.2和iOS 9.3中不再可靠。

9

我有一个现成的iOS 9.2和watchOS 2.1应用程序,它使用sendMessagetransferUserInfo将数据从iPhone发送到Apple Watch。如果sendMessage失败,我会使用transferUserInfo将数据排队以便稍后传送:

// *** In the iOS app ***
self.session.sendMessage(message, replyHandler: nil) { (error) -> Void in
    // If the message failed to send, queue it up for future transfer
    self.session.transferUserInfo(message)
}

// *** In the watchOS app ***
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    // Handle message here
}

func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
    // Handle message here
}

不更改任何代码并在真实设备上运行iOS 9.3和watchOS 2.2的应用程序(模拟器没有相同的问题),只要手表在范围内且屏幕开启,sendMessage将数据传递到Apple Watch。这是预期的并且以前的工作方式。然而,如果屏幕关闭并且sendMessage失败,则当屏幕重新开启时,transferUserInfo不再向Apple Watch传递数据。
为了找出错误发生的位置,我添加了以下WCSessionDelegate方法,以查看iOS应用是否未能发送数据:
func session(session: WCSession, didFinishUserInfoTransfer userInfoTransfer: WCSessionUserInfoTransfer, error: NSError?) {
    // Called when self.session.transferUserInfo completes
}

在调用transferUserInfo方法后,此方法会被调用,但是没有返回错误,iOS应用程序似乎表明传输成功。

起初,我认为数据传输所需的时间已经增加,但是将设备独自留给其自己一整天后,数据仍然未传输。现在我有些怀疑这可能与新的多手表API有关,并且也许iOS应用程序需要知道要发送到哪个特定的手表,尽管我只曾经配对了一个手表。是否有人有任何想法,可以解释发生了什么变化以及如何正确使用transferUserInfo


2
我们遇到了类似的问题,从手表发送 userInfo 到手机时,有时候委托方法根本没有被调用。在 9.3 和 2.2 之前是可以正常工作的。 - Ultrakorne
2
请查看此帖子 https://forums.developer.apple.com/thread/43596 - Ultrakorne
我是第二个用户评论 :) 那篇文章让我对新的多观察API更加怀疑。 - lehn0058
1
我只是在玩转 transferCurrentComplicationUserInfo,注意到了同样的问题。这明显是苹果引入的新 bug。 - lehn0058
1
当我在手机上删除应用程序并重新安装后,有些东西会出现问题。之后,transferCurrentComplicationUserInfo:似乎无法成功地将userInfo有效载荷传输到手表。直到我重新启动手表,事情才开始正常工作。 - ospr
2个回答

3

我认为我现在已经解决了这个问题。首先,我不得不将新的WCSessionDelegate方法添加到我的iOS应用程序中:

@available(iOS 9.3, *)
func session(session: WCSession, activationDidCompleteWithState activationState: WCSessionActivationState, error: NSError?) {
    if activationState == WCSessionActivationState.Activated {
        NSLog("Activated")
    }

    if activationState == WCSessionActivationState.Inactive {
        NSLog("Inactive")
    }

    if activationState == WCSessionActivationState.NotActivated {
        NSLog("NotActivated")
    }
}

func sessionDidBecomeInactive(session: WCSession) {
    NSLog("sessionDidBecomeInactive")
}

func sessionDidDeactivate(session: WCSession) {
    NSLog("sessionDidDeactivate")

    // Begin the activation process for the new Apple Watch.
    self.session.activateSession()
}

同样地,我的watchOS应用程序也是如此:

@available(watchOSApplicationExtension 2.2, *)
func session(session: WCSession, activationDidCompleteWithState activationState: WCSessionActivationState, error: NSError?) {
    if activationState == WCSessionActivationState.Activated {
        NSLog("Activated")
    }

    if activationState == WCSessionActivationState.Inactive {
        NSLog("Inactive")
    }

    if activationState == WCSessionActivationState.NotActivated {
        NSLog("NotActivated")
    }
}

但是对我来说,transferUserInfo仍然无法正常工作,特别是当Apple Watch的屏幕关闭时。以下是我在iOS 9.2/watchOS 2.1中在iPhone和Apple Watch之间发送信息的方法:

func tryWatchSendMessage(message: [String : AnyObject]) {
    if self.session != nil && self.session.paired && self.session.watchAppInstalled {
        self.session.sendMessage(message, replyHandler: nil) { (error) -> Void in
            // If the message failed to send, queue it up for future transfer
            self.session.transferUserInfo(message)
        }
    }
}

我曾以为当Apple Watch屏幕关闭时,从iPhone发送消息到Watch会导致transferUserInfo无法传输,因为它在sendMessage的错误处理程序中。当Watch屏幕开启时,sendMessage也按预期工作。然而,似乎即使请求失败,sendMessage的错误回复处理程序并不总是被调用,如果您的手表屏幕关闭,这与之前的操作系统版本有不同的行为。这也似乎引起了级联效应,导致后续的消息即使条件适当也失败了。这就是让我相信transferUserInfo有问题的原因。
我发现要可靠地通过我的消息,我需要检查reachable和activationState。由于我还想继续支持早期版本的iOS和watchOS,我的tryWatchSendMessage方法变成了以下内容:
func tryWatchSendMessage(message: [String : AnyObject]) {
    if #available(iOS 9.3, *) {
        if self.session != nil && self.session.paired && self.session.watchAppInstalled && self.session.activationState == .Activated {
            if self.session.reachable == true {
                self.session.sendMessage(message, replyHandler: nil) { (error) -> Void in
                    // If the message failed to send, queue it up for future transfer
                    self.session.transferUserInfo(message)
                }
            } else {
                self.session.transferUserInfo(message)
            }
        }
    } else {
        // Fallback on earlier versions
        if self.session != nil && self.session.paired && self.session.watchAppInstalled {
            if self.session.reachable == true {
                self.session.sendMessage(message, replyHandler: nil) { (error) -> Void in
                    // If the message failed to send, queue it up for future transfer
                    self.session.transferUserInfo(message)
                }
            } else {
                self.session.transferUserInfo(message)
            }
        }
    }
}

对这些更改进行的修改似乎已经解决了我所看到的问题。我很想知道这些更改是否有助于解决其他人的问题,或者与transferUserInfo不起作用还存在相关问题。


1
好发现...虽然我现在对你最初的问题有点困惑。看起来你没有改变与transferUserInfo相关的逻辑,除了在9.3上正确保护sendMessage? - Cobra
我在想线程是否存在问题。我的理解是WCSession是一个单例。在这种情况下,回复和错误处理将在后台线程上发生。如果您在这些块内部发出sendMessage,则可能会与主线程并行执行。您是否尝试将sendMessage排队到主线程而不是像这样在后台运行它? - Cobra
tryWatchSendMessage 对我来说总是在主线程上调用,因此 sendMessage 已经在主线程上被调用。errorHandler 可能不是这样,但我理解从非主线程调用 transferUserInfo 是安全的。 - lehn0058
我的回复处理程序是nil。问题在于错误处理程序甚至没有被调用,因此我甚至无法在后台线程上调用transferUserInfo。 - lehn0058
您已经确定与发送但未传递的 transferUserInfo 无关。 (可能需要更新开发者论坛线程以澄清该报告!)虽然您的新代码避免了首先触发“可达”错误,但错误处理程序应捕获它:“在发送消息时,最常见的错误是未能到达配对设备,但也可能出现其他错误。”有趣。也许您可以提出一个新问题,特别是确定为什么您的 iOS 错误处理程序块没有被调用? - user4151918
显示剩余3条评论

0

自从上周2.2更新以来,我一直遇到与通信相关的非常相似的问题 - 但对我来说,我无法传输应用程序上下文或文件。

像其他人一样,在模拟器中可以工作,但在设备上却不能。

今天我注意到我正在尝试从后台线程发送数据 - 我将所有调用都包装在dispatch_async(dispatch_get_main_queue())中,突然间一切都正常了。


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