【问题标题】:WCSession's transferUserInfo no longer reliably working in watchOS 2.2 with iOS 9.3WCSession 的 transferUserInfo 在 iOS 9.3 的 watchOS 2.2 中不再可靠地工作
【发布时间】:2016-03-29 13:58:09
【问题描述】:

我有一个现有的 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
}

无需更改任何代码并在带有 watchOS 2.2 的 iOS 9.3 上运行应用程序(模拟器没有相同的问题),只要手表在范围内并且屏幕开启。这是预期的,它以前是如何工作的。但是,如果屏幕关闭并且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 有任何想法吗?

【问题讨论】:

  • 我们遇到了类似的问题,将用户信息从手表发送到手机,有时委托根本没有接到电话。在 9.3 和 2.2 之前,这是可行的。
  • 我是那里的第二个用户评论 :) 那篇文章让我对新的多手表 API 更加怀疑。
  • 我只是在玩 transferCurrentComplicationUserInfo 并注意到同样的事情。这绝对看起来像是 Apple 引入的一个新错误。
  • 在我删除手机上的应用程序然后重新安装后,事情就中断了。在那之后,transferCurrentComplicationUserInfo: 似乎永远不会成功地将 userInfo 有效负载传输到手表。直到我重新启动手表,事情才重新开始工作。

标签: ios9 watchkit watchos-2 wcsession ios9.3


【解决方案1】:

我想我现在有这个工作。首先,我必须将新的 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)
        }
    }
}

我曾假设在手表屏幕关闭时从 iPhone 向 Apple Watch 发送消息会导致 transferUserInfo 失败,因为它位于 sendMessage 的错误处理程序中。当屏幕打开时,sendMessage 也按预期工作。但是,如果您的手表屏幕关闭,即使请求失败,看起来 sendMessage 的错误回复处理程序也不会总是被调用。这是与以前的操作系统版本不同的行为。这似乎也导致了级联效应,即使条件合适,后续消息也会失败。这就是让我相信 transferUserInfo 是罪魁祸首的原因。

我发现为了让我的消息可靠地通过,我需要同时检查reachable 和activationState。由于我还想继续支持早期的 iO​​S 和 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 无法正常工作相关的问题。

【讨论】:

  • 很好的发现......虽然我现在对你原来的问题有点困惑。除了在 9.3 上正确保护 sendMessage 之外,您似乎没有更改与 transferUserInfo 相关的逻辑?
  • 我想知道线程是否真的存在问题。我的理解是 WCSession 是一个单例。在这种情况下,回复和错误处理将在后台线程上进行。如果您从这些块中发出 sendMessage ,则它们可能与主线程并行执行。您是否尝试过将 sendMessage 排队到主线程而不是像这样在后台运行它?
  • tryWatchSendMessage 总是在我的主线程上被调用,所以 sendMessage 已经在主线程上被调用了。 errorHandler 可能不是,但据我了解,从主线程外部调用 transferUserInfo 是安全的。
  • 您应该查看 sendMessage 文档。块处理程序在后台线程上运行(至少根据 Apple 而言),这将包括您的 replyHandler 代码,对吧?
  • 我的回复处理程序为零。问题是错误处理程序甚至没有被调用,所以我什至无法在后台线程上调用 transferUserInfo。
【解决方案2】:

自上周更新 2.2 以来,我在进行通信方面遇到了非常相似的问题 - 但对我来说,我无法获取应用上下文或文件来传输。

和其他人一样,它在模拟器上工作,但在设备上却不行。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多