“邮件回复花了太长时间。” - 观察Watch OS 3的连接问题 [英] "Message reply took too long." - Watch Connectivity Issues with Watch OS 3
问题描述
在我的项目中,我使用 Watch Connectivity
向Watch和iPhone发送消息。我可以向手机发送消息并在启动应用程序时收到一串字符串,但是在使用操作时我收到以下错误;
In my project, I use Watch Connectivity
to send messages to and from the Watch and iPhone. I can send a message to the phone and receive an array of strings when launching the app, however when using actions I get the following error;
错误域名= WCErrorDomain代码= 7012邮件回复花了太长时间。
Error Domain=WCErrorDomain Code=7012 "Message reply took too long."
以下是设置方式;
首先,手表向手机发送信息,然后手机发送一个数组字符串显示在 WKInterfaceTable
中。这有时适用于加载应用程序。 (我获取所有名为 Items
的NSManagedObjects并使用他们的 title
字符串属性存储在数组中
名为 watchItems
。
First, the watch sends a message to the phone and then the phone sends an array of strings to display in a WKInterfaceTable
. This sometimes works when loading the app. ( I fetch all NSManagedObjects called Items
and use their title
string properties to store in an array
called watchItems
.
但是,我在手表上有一个动作要删除全部数组中的项目并使用新数据刷新表。
However, I have an action on the watch to delete all items in the array and refresh the table with the new data.
手表上的操作使用 sendMessage
函数将项目
发送到手机以从阵列中删除,然后手机将新更新的阵列发送到手表,手表更新表格。但是,我要么得到相同的数组返回或错误。
The action on the watch uses a sendMessage
function to send the item
to the phone to delete from the array, then the phone sends the newly updated array to the watch and the watch updates the table. However, I either get the same array back or an error.
非常简单,所以在Swift 3和Watch OS3 / iOS 10之前一切正常工作;整个应用程序曾经工作过。
Pretty simple right, so everything actually worked fine before Swift 3 and Watch OS3/iOS 10; the entire app used to work.
以下是我设置所有内容的方法;
Here's how I have everything set up;
电话应用代表
import WatchConnectivity
class AppDelegate: UIResponder, UIApplicationDelegate, WCSessionDelegate {
var session : WCSession!
var items = [Items]()
func loadData() {
let moc = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
let request = NSFetchRequest<Items>(entityName: "Items")
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
request.predicate = NSPredicate(format: "remove == 0", "remove")
do {
try
self.items = moc!.fetch(request)
// success ...
} catch {
// failure
print("Fetch failed")
}
}
//WATCH EXTENSION FUNCTIONS
//IOS 9.3
/** Called when the session has completed activation. If session state is WCSessionActivationStateNotActivated there will be an error with more details. */
//HAVE TO INCLUDE
@available(iOS 9.3, *)
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?){
print("iPhone WCSession activation did complete")
}
@available(iOS 9.3, *)
func sessionDidDeactivate(_ session: WCSession) {}
func sessionWatchStateDidChange(_ session: WCSession) {}
func sessionDidBecomeInactive(_ session: WCSession) {
}
//APP DELEGATE FUNCTIONS
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
//Check if session is supported and Activate
if (WCSession.isSupported()) {
session = WCSession.default()
session.delegate = self;
session.activate()
}
return true
}
}
//DID RECIEVE MESSAGE
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Swift.Void) {
loadData()
func loadItems() {
watchItems.removeAll()
for a in self.items {
watchItems.append(a.title)
}
}
var watchItems = ["1","2","3","4","5"]
let value = message["Value"] as? String
//This is called when user loads app, and takes some time when using refresh action, sometimes times out
if value == "HELLOiPhone/+@=" {
print("Hello Message Recieved")
loadItems()
//send a reply
replyHandler( [ "Items" : Items ] )
}
//Not sure if receiving but does not delete array and send back to watch
if value == "removeALL@+=-/" {
for index in self.items {
index.remove = 1
//Saves MOC
}
loadData()
loadTasksData()
//send a reply
replyHandler( [ "Items" : Items ] )
}
else {
for index in self.items {
if index.title == value {
index.remove = 1
//Saves MOC
}
}
loadData()
loadTasksData()
//send a reply
replyHandler( [ "Items" : Items ] )
}
}
观看
import WatchConnectivity
class SimplelistInterfaceController: WKInterfaceController, WCSessionDelegate {
/** Called when the session has completed activation. If session state is WCSessionActivationStateNotActivated there will be an error with more details. */
@available(watchOS 2.2, *)
public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
//Fetch data is a function which sends a "HELLOiPhone/+@=" message to receive the array and displays in the table. This works
fetchData()
}
var session : WCSession!
var items = ["Refresh Items"]
override func didAppear() {
fetchData()
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
//Check if session is supported and Activate
if (WCSession.isSupported()) {
session = WCSession.default()
session.delegate = self
session.activate()
}
fetchData()
}
override func awake(withContext context: Any?) {
super.awake(withContext: context)
fetchData()
}
@IBAction func refresh() {
print("Refresh")
//Works but sometimes message is delayed
fetchData()
}
@IBAction func removeAll() {
print("Remove All Items is called")
if WCSession.default().isReachable {
let messageToSend = ["Value":"removeALL@+=-/"]
print("\(messageToSend)")
session.sendMessage(messageToSend, replyHandler: { replyMessage in
if let value = replyMessage["Items"] {
self.items = value as! [String]
Not receiving message
print("Did Recieve Message, items = \(self.items)")
}
}, errorHandler: {error in
// catch any errors here
print(error)
})
}
fetchData()
}
}
推荐答案
-
您不应该从一个目标(iOS)向第二个目标(watchOS)发送或接收自定义类对象,而应该以字典格式发送/接收数据,例如[String:Any],此字典应包含您的自定义对象需要简单字典中键值对中的属性。这可以很容易地在手表端解码。
You should not send or receive custom class objects from one target(iOS) to second target(watchOS) instead you should send/receive data in dictionary format such as [String: Any] and this dictionary should contain array of your custom objects required properties in key value pair in simple dictionary. This could easily be decodable at watch side.
你应该创建一个解耦类来扩展WCSessionDelegate,如下所示,这样这个类不仅可以在ExtensionDelegate中使用,而且也可以在任何WKInterfaceController中。
You should make a decoupled class extending WCSessionDelegate such as below so that this class could be used not only in ExtensionDelegate but also in any WKInterfaceController.
class WatchSessionManager: NSObject, WCSessionDelegate {
static let sharedManager = WatchSessionManager()
private override init() {
super.init()
self.startSession()
}
private let session: WCSession = WCSession.default
func startSession() {
session.delegate = self
session.activate()
}
func tryWatchSendMessage(message: [String: Any], completion: (([String: Any]) -> Void)? = nil) {
print("tryWatch \(message)")
weak var weakSelf = self
if #available(iOS 9.3, *) {
if weakSelf?.session.activationState == .activated {
if weakSelf?.session.isReachable == true {
weakSelf?.session.sendMessage(message,
replyHandler: { [weak self] ( response ) in
guard let slf = self else {return}
//Get the objects from response dictionary
completion?(response)
},
errorHandler: { [weak self] ( error ) in
guard let slf = self else {return}
print ( "Error sending message: % @ " , error )
// If the message failed to send, queue it up for future transfer
slf.session.transferUserInfo(message)
})
} else {
self.session.transferUserInfo(message)
}
}else{
self.session.activate()
self.session.transferUserInfo(message)
}
} else {
// Fallback on earlier versions
if self.session.activationState == .activated {
if self.session.isReachable == true {
self.session.sendMessage(message,
replyHandler: { ( response ) in
//Get the objects from response dictionary
completion?(response)
},
errorHandler: { ( error ) in
print ( "Error sending message: % @ " , error )
// If the message failed to send, queue it up for future transfer
self.session.transferUserInfo(message)
})
} else {
self.session.transferUserInfo(message)
}
}else{
self.session.activate()
self.session.transferUserInfo(message)
}
}
}
}
现在您可以轻松地向您的iOS应用发送消息,以唤醒并从中获取数据(例如从CoreData获取)使用上述内容任何WKInterfaceController中的函数和完成块都会有你需要的数据,比如
Now you could easily send a message to your iOS app to wake up and get data from there (e.g from CoreData) using the above function in any WKInterfaceController and the completion block will have your required data such as
let dict: [String: Any] = ["request": "FirstLoad"]
WatchSessionManager.sharedManager.tryWatchSendMessage(message: dict,completion:{ (data) in print(data)})
同样的方式您应该在iOS端使用此WatchSessionManager并接收请求,并根据请求的密钥,您应从核心存储/ db中获取数据,并在didreceiveMessage函数的replyHandler中以简单的键值字典模式发送自定义对象列表,如下所示。 / p>
Same way you should use this WatchSessionManager on iOS side and receive the request and as per the requested key you should take data from core storage/db and send list of custom objects in simple key-value Dictionary pattern within replyHandler of didreceiveMessage function such as below.
func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) {
var dict: [String: Any] = [String: Any]()
replyHandler(dict) //This dict will contain your resultant array to be sent to watchApp.
}
有些时候,iOS App App(Killed状态)无法访问WatchApp,无法解决那个问题你应该在Timer中以大约3秒的间隔调用tryWatchSendMessage。当您从watchApp获得连接时,您应该使计时器无效。
Some time iOS App(Killed state) is not reachable to WatchApp, for solving that problem you should call "tryWatchSendMessage" within Timer of around 3 sec interval. And when you get connection from watchApp then you should invalidate the timer.
WatchConnectivity的sendMessage功能非常强大,可以唤醒您的应用程序。您应该以优化的方式使用它。
The sendMessage functionality of WatchConnectivity is so powerful to wake your app up. You should use it in optimized manner.
这篇关于“邮件回复花了太长时间。” - 观察Watch OS 3的连接问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!