使用 WatchConnectivity 获取核心数据请求
Core Data fetch request with WatchConnectivity
我目前正在尝试将 CoreData 数据从我的 iOS 应用程序获取到 watchOS 扩展。我正在使用 WatchConnectivity 框架通过 sendMessage(_ message: [String : Any], replyHandler: (([String : Any]) -> Void)?, errorHandler: ((Error) -> Void)? = nil)
函数获取字典。基本连接工作正常。 iOS 应用程序可以访问,如果我尝试回复示例词典,一切正常。
到目前为止一切顺利,但是当我开始在后台对 iOS 应用程序执行提取请求时,Watch 应用程序从未接收到数据。过了一会儿,我收到了这个错误:Error while requesting data from iPhone: Error Domain=WCErrorDomain Code=7012 "Message reply took too long." UserInfo={NSLocalizedFailureReason=Reply timeout occurred., NSLocalizedDescription=Message reply took too long.}
如果我在 iPhone 上打开 iOS 应用程序并重新启动 Watch 应用程序,回复处理程序就会得到结果。但是强制用户在iPhone上主动打开iOS应用是没有用的。
有人可以解释为什么会这样吗?正确的方法是什么?自 watchOS 2 以来,应用组似乎已过时。
我正在使用 Swift 4 顺便说一句…
在 Apple Watch 上:
import WatchConnectivity
class HomeInterfaceController: WKInterfaceController, WCSessionDelegate {
// (…)
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
session.sendMessage(["request": "persons"],
replyHandler: { (response) in
print("response: \(response)")
},
errorHandler: { (error) in
print("Error while requesting data from iPhone: \(error)")
})
}
在 iPhone:
import CoreData
import WatchConnectivity
class ConnectivityHandler: NSObject, WCSessionDelegate {
var personsArray:[Person] = []
// (…)
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
// only using the next line is working!
// replyHandler(["data": "test"])
if message["request"] as? String == "persons" {
fetchAllPersons()
var allPersons: [String] = []
for person in personsArray {
allPersons.append(person.name!)
}
replyHandler(["names": allPersons])
}
}
// this seems to be never executed (doesn't matter if it's in an extra function or right in the didReceiveMessage func)
func fetchAllPersons() {
do {
// Create fetch request.
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: #keyPath(Person.name), ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
personsArray = try DatabaseController.getContext().fetch(fetchRequest)
} catch {
fatalError("Failed to fetch: \(error)")
}
}
遗憾的是,大多数 WatchConnectivity
方法都有时间限制(正如错误告诉您的那样),您的 CoreData 请求似乎花费了太多时间,因此超过了时间限制。根据 this Q&A,您似乎需要采取特定的预防措施在后台执行 CoreData 查询,因此这可能是导致您出现问题的原因。
但是,为了获得最佳用户体验,我建议您停止使用 CoreData
和 WatchConnectivity
框架,因为后者要求您的 iOS 应用 运行至少在后台,因此使 watchOS 应用程序依赖于 iOS 应用程序的状态并降低 watchOS 上的用户体验。我建议您切换到 Realm,因为 Realm
完全支持 watchOS,因此您的 watchOS 应用程序可以完全独立于您的 iOS 应用程序,使用户体验更加流畅,因为用户无需启动 iOS 应用程序并等待使用 WatchConnectivity
框架通过 BLE 传输数据。
在研究这个问题后,我自己找到了解决方案。问题是我使用的是 sendMessage(_:replyHandler:errorHandler:)
协议。这仅用于在两个应用程序都处于活动状态时传输数据。
Use the sendMessage(_:replyHandler:errorHandler:)
or sendMessageData(_:replyHandler:errorHandler:)
method to transfer data to a reachable counterpart. These methods are intended for immediate communication between your iOS app and WatchKit extension. The isReachable property must currently be true for these methods to succeed.
如果您想在后台传输数据,您必须根据需要使用 updateApplicationContext(_:)
或 transferUserInfo(_:)
。这正是我需要的!
Use the updateApplicationContext(_:)
method to communicate recent state information to the counterpart. When the counterpart wakes, it can use this information to update its own state. For example, an iOS app that supports Background App Refresh can use part of its background execution time to update the corresponding Watch app. This method overwrites the previous data dictionary, so use this method when your app needs only the most recent data values.
Use the transferUserInfo(_:)
method to transfer a dictionary of data in the background. The dictionaries you send are queued for delivery to the counterpart and transfers continue when the current app is suspended or terminated.
现在,如果 iPhone 对应的 App 打开 ApplicationContext 或 UserInfo 队列,我可以将数据添加到我的核心数据库中。
我目前正在尝试将 CoreData 数据从我的 iOS 应用程序获取到 watchOS 扩展。我正在使用 WatchConnectivity 框架通过 sendMessage(_ message: [String : Any], replyHandler: (([String : Any]) -> Void)?, errorHandler: ((Error) -> Void)? = nil)
函数获取字典。基本连接工作正常。 iOS 应用程序可以访问,如果我尝试回复示例词典,一切正常。
到目前为止一切顺利,但是当我开始在后台对 iOS 应用程序执行提取请求时,Watch 应用程序从未接收到数据。过了一会儿,我收到了这个错误:Error while requesting data from iPhone: Error Domain=WCErrorDomain Code=7012 "Message reply took too long." UserInfo={NSLocalizedFailureReason=Reply timeout occurred., NSLocalizedDescription=Message reply took too long.}
如果我在 iPhone 上打开 iOS 应用程序并重新启动 Watch 应用程序,回复处理程序就会得到结果。但是强制用户在iPhone上主动打开iOS应用是没有用的。
有人可以解释为什么会这样吗?正确的方法是什么?自 watchOS 2 以来,应用组似乎已过时。
我正在使用 Swift 4 顺便说一句…
在 Apple Watch 上:
import WatchConnectivity
class HomeInterfaceController: WKInterfaceController, WCSessionDelegate {
// (…)
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
session.sendMessage(["request": "persons"],
replyHandler: { (response) in
print("response: \(response)")
},
errorHandler: { (error) in
print("Error while requesting data from iPhone: \(error)")
})
}
在 iPhone:
import CoreData
import WatchConnectivity
class ConnectivityHandler: NSObject, WCSessionDelegate {
var personsArray:[Person] = []
// (…)
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
// only using the next line is working!
// replyHandler(["data": "test"])
if message["request"] as? String == "persons" {
fetchAllPersons()
var allPersons: [String] = []
for person in personsArray {
allPersons.append(person.name!)
}
replyHandler(["names": allPersons])
}
}
// this seems to be never executed (doesn't matter if it's in an extra function or right in the didReceiveMessage func)
func fetchAllPersons() {
do {
// Create fetch request.
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: #keyPath(Person.name), ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
personsArray = try DatabaseController.getContext().fetch(fetchRequest)
} catch {
fatalError("Failed to fetch: \(error)")
}
}
遗憾的是,大多数 WatchConnectivity
方法都有时间限制(正如错误告诉您的那样),您的 CoreData 请求似乎花费了太多时间,因此超过了时间限制。根据 this Q&A,您似乎需要采取特定的预防措施在后台执行 CoreData 查询,因此这可能是导致您出现问题的原因。
但是,为了获得最佳用户体验,我建议您停止使用 CoreData
和 WatchConnectivity
框架,因为后者要求您的 iOS 应用 运行至少在后台,因此使 watchOS 应用程序依赖于 iOS 应用程序的状态并降低 watchOS 上的用户体验。我建议您切换到 Realm,因为 Realm
完全支持 watchOS,因此您的 watchOS 应用程序可以完全独立于您的 iOS 应用程序,使用户体验更加流畅,因为用户无需启动 iOS 应用程序并等待使用 WatchConnectivity
框架通过 BLE 传输数据。
在研究这个问题后,我自己找到了解决方案。问题是我使用的是 sendMessage(_:replyHandler:errorHandler:)
协议。这仅用于在两个应用程序都处于活动状态时传输数据。
Use the
sendMessage(_:replyHandler:errorHandler:)
orsendMessageData(_:replyHandler:errorHandler:)
method to transfer data to a reachable counterpart. These methods are intended for immediate communication between your iOS app and WatchKit extension. The isReachable property must currently be true for these methods to succeed.
如果您想在后台传输数据,您必须根据需要使用 updateApplicationContext(_:)
或 transferUserInfo(_:)
。这正是我需要的!
Use the
updateApplicationContext(_:)
method to communicate recent state information to the counterpart. When the counterpart wakes, it can use this information to update its own state. For example, an iOS app that supports Background App Refresh can use part of its background execution time to update the corresponding Watch app. This method overwrites the previous data dictionary, so use this method when your app needs only the most recent data values.Use the
transferUserInfo(_:)
method to transfer a dictionary of data in the background. The dictionaries you send are queued for delivery to the counterpart and transfers continue when the current app is suspended or terminated.
现在,如果 iPhone 对应的 App 打开 ApplicationContext 或 UserInfo 队列,我可以将数据添加到我的核心数据库中。