Swift3:通过 AppGroups 从 Watch Extension 访问核心数据时的空获取
Swift3: Empty fetch when accessing Core Data from Watch Extension via AppGroups
目前我正在为现有的应用程序进行更新(迁移到 Swift 3)。我有 Today-、Search-、Message- 和 Watch Extensions 的目标。每个目标都需要访问我的应用程序的核心数据模型,因此我创建了一个 AppGroup 并为每个目标启用了 Capability。尽管我已经子class编辑了 NSPersistentStoreCoordinator,所以所有内容都存储在一个共享文件夹中:
import CoreData
class PFPersistentContainer: NSPersistentContainer {
override open class func defaultDirectoryURL() -> URL {
if let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "add-groupname-here") {
return url
}
// Fallback
return super.defaultDirectoryURL()
}
}
这个 class 文件,以及我的核心数据模型和下面的 class 都是上述所有目标的目标成员。实体的代码生成设置为 Class Definition
。到目前为止,我使用的是核心数据堆栈的默认实现:
class DataManager {
/**
* Singleton Implementation
*/
internal static let sharedInstance:DataManager = {
let instance = DataManager()
return instance
}()
// MARK: - Core Data stack
lazy var persistentContainer: PFPersistentContainer = {
let container = PFPersistentContainer(name: "Data")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
现在奇怪的是:从 Today- 和 Message-Extension 访问这些数据似乎工作得很好(我假设 Search-Extension 也在工作,但就开发人员而言,我无法测试这个)。尝试使用来自 Watch App 扩展的相同请求获取它会导致一个空数组。这是我的获取代码:
internal func getLimitedPOIsWithCalculatedDistance(andCurrentLocation currentLocation:CLLocation) -> Array<POI> {
var poiArray:Array< POI > = []
guard let context = self.backgroundContext else { return [] }
do {
// Initialize Fetch Request
let request:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "POI")
// Create Entity Description
let entityDescription = NSEntityDescription.entity(forEntityName: "POI", in: context)
// Configure Fetch Request
request.entity = entityDescription
// Configure Predicate
let predicate = NSPredicate(format: "(disabled == NO) AND (locationDistanceCalculated <= \(SETTINGS_MAXIMUM_FETCH_DISTANCE))")
request.predicate = predicate
if let result = try context.fetch(request) as? Array<POI> {
for poi in result {
let poiLocation = CLLocation(latitude: poi.locationLatitude, longitude: poi.locationLongitude)
let distance = currentLocation.distance(from: poiLocation)
poi.locationDistanceTransient = Double(distance)
}
poiArray = result.filter({ (poi) -> Bool in
return poi.locationDistanceTransient > 0.0
})
poiArray.sort { (first, second) -> Bool in
return first.locationDistanceTransient < second.locationDistanceTransient
}
}
} catch {
print("Error in WatchDataInterface.getLimitedPOIsWithCalculatedDistance(): \(error.localizedDescription)")
}
return poiArray
}
A bit more context for better understanding: the POI Entitie contains latitude and longitude of a location. When starting the app, I'm pre-calculating the distance to each point in the database from the current user's position. When the Today-Extension tries to get the nearest x (in my case 8) POIs to the current users position, fetching all 15k POIs and calculating their distance is to much memory wise. So I had to pre-calculate the distance (stored in locationDistanceCalculated
), then fetch in a given radius (the static SETTINGS_MAXIMUM_FETCH_DISTANCE
) and calculate a precise distance during the fetch process (stored into a transient property locationDistanceTransient
).
在 Watch App 的 ExtensionDelegate
Class 中实现了 CLLocationManagerDelegate
并调用了代码,当用户的位置更新时:
extension ExtensionDelegate: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
OperationQueue.main.addOperation{
if let watchDataManager = self.watchDataManager {
// getNearestPOIs() calls the getLimitedPOIsWithCalculatedDistance() function and returns only a numberOfPOIs
self.nearbyPOIs = watchDataManager.getNearestPOIs(numberOfPOIs: 4)
}
}
}
}
已在模拟器和设备上进行测试。 context.fetch
总是 returns 一个空数组(是的,核心数据包含值,是的,我已经在没有谓词的情况下对其进行了测试)。我是否遗漏了 Core Data 中的任何新内容,我还没有考虑过,或者它们在 WatchOS3 中的任何限制为什么这不起作用?有人知道吗?感谢您的帮助。
更新: 当使用 Watch Framework 目标访问核心数据时,如 in this project 所述,提取一直为空。也许这可能是正确的道路,但 Watch Framework 是错误的选择。会让你了解最新情况。
更新 2: 我已经检查了 API 参考资料中的 App Programming Guide for WatchOS and the transferFile:metadata:
function,但它似乎不是一个合适的方式将这些大量数据发送到 AppleWatch。我只是不能依赖这种情况,即用户检查应用程序的频率高于他离开 SETTINGS_MAXIMUM_FETCH_DISTANCE
半径的频率,并且对于具有高密度 POI 的位置,此数据甚至更广泛。
更新 3: 我已经重新实现了 POI 的附近功能,以便仅在给定的半径范围内获取。如果这能为您解决问题,请查看此 public Gist.
自 Watch OS3 起,您无法使用 App Group 技巧访问 CoreData
基地(因为手表应该在没有 phone.
的情况下工作
所以你必须使用WatchConnectivity
来获取你的数据
目前我正在为现有的应用程序进行更新(迁移到 Swift 3)。我有 Today-、Search-、Message- 和 Watch Extensions 的目标。每个目标都需要访问我的应用程序的核心数据模型,因此我创建了一个 AppGroup 并为每个目标启用了 Capability。尽管我已经子class编辑了 NSPersistentStoreCoordinator,所以所有内容都存储在一个共享文件夹中:
import CoreData
class PFPersistentContainer: NSPersistentContainer {
override open class func defaultDirectoryURL() -> URL {
if let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "add-groupname-here") {
return url
}
// Fallback
return super.defaultDirectoryURL()
}
}
这个 class 文件,以及我的核心数据模型和下面的 class 都是上述所有目标的目标成员。实体的代码生成设置为 Class Definition
。到目前为止,我使用的是核心数据堆栈的默认实现:
class DataManager {
/**
* Singleton Implementation
*/
internal static let sharedInstance:DataManager = {
let instance = DataManager()
return instance
}()
// MARK: - Core Data stack
lazy var persistentContainer: PFPersistentContainer = {
let container = PFPersistentContainer(name: "Data")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
现在奇怪的是:从 Today- 和 Message-Extension 访问这些数据似乎工作得很好(我假设 Search-Extension 也在工作,但就开发人员而言,我无法测试这个)。尝试使用来自 Watch App 扩展的相同请求获取它会导致一个空数组。这是我的获取代码:
internal func getLimitedPOIsWithCalculatedDistance(andCurrentLocation currentLocation:CLLocation) -> Array<POI> {
var poiArray:Array< POI > = []
guard let context = self.backgroundContext else { return [] }
do {
// Initialize Fetch Request
let request:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "POI")
// Create Entity Description
let entityDescription = NSEntityDescription.entity(forEntityName: "POI", in: context)
// Configure Fetch Request
request.entity = entityDescription
// Configure Predicate
let predicate = NSPredicate(format: "(disabled == NO) AND (locationDistanceCalculated <= \(SETTINGS_MAXIMUM_FETCH_DISTANCE))")
request.predicate = predicate
if let result = try context.fetch(request) as? Array<POI> {
for poi in result {
let poiLocation = CLLocation(latitude: poi.locationLatitude, longitude: poi.locationLongitude)
let distance = currentLocation.distance(from: poiLocation)
poi.locationDistanceTransient = Double(distance)
}
poiArray = result.filter({ (poi) -> Bool in
return poi.locationDistanceTransient > 0.0
})
poiArray.sort { (first, second) -> Bool in
return first.locationDistanceTransient < second.locationDistanceTransient
}
}
} catch {
print("Error in WatchDataInterface.getLimitedPOIsWithCalculatedDistance(): \(error.localizedDescription)")
}
return poiArray
}
A bit more context for better understanding: the POI Entitie contains latitude and longitude of a location. When starting the app, I'm pre-calculating the distance to each point in the database from the current user's position. When the Today-Extension tries to get the nearest x (in my case 8) POIs to the current users position, fetching all 15k POIs and calculating their distance is to much memory wise. So I had to pre-calculate the distance (stored in
locationDistanceCalculated
), then fetch in a given radius (the staticSETTINGS_MAXIMUM_FETCH_DISTANCE
) and calculate a precise distance during the fetch process (stored into a transient propertylocationDistanceTransient
).
在 Watch App 的 ExtensionDelegate
Class 中实现了 CLLocationManagerDelegate
并调用了代码,当用户的位置更新时:
extension ExtensionDelegate: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
OperationQueue.main.addOperation{
if let watchDataManager = self.watchDataManager {
// getNearestPOIs() calls the getLimitedPOIsWithCalculatedDistance() function and returns only a numberOfPOIs
self.nearbyPOIs = watchDataManager.getNearestPOIs(numberOfPOIs: 4)
}
}
}
}
已在模拟器和设备上进行测试。 context.fetch
总是 returns 一个空数组(是的,核心数据包含值,是的,我已经在没有谓词的情况下对其进行了测试)。我是否遗漏了 Core Data 中的任何新内容,我还没有考虑过,或者它们在 WatchOS3 中的任何限制为什么这不起作用?有人知道吗?感谢您的帮助。
更新: 当使用 Watch Framework 目标访问核心数据时,如 in this project 所述,提取一直为空。也许这可能是正确的道路,但 Watch Framework 是错误的选择。会让你了解最新情况。
更新 2: 我已经检查了 API 参考资料中的 App Programming Guide for WatchOS and the transferFile:metadata:
function,但它似乎不是一个合适的方式将这些大量数据发送到 AppleWatch。我只是不能依赖这种情况,即用户检查应用程序的频率高于他离开 SETTINGS_MAXIMUM_FETCH_DISTANCE
半径的频率,并且对于具有高密度 POI 的位置,此数据甚至更广泛。
更新 3: 我已经重新实现了 POI 的附近功能,以便仅在给定的半径范围内获取。如果这能为您解决问题,请查看此 public Gist.
自 Watch OS3 起,您无法使用 App Group 技巧访问 CoreData
基地(因为手表应该在没有 phone.
所以你必须使用WatchConnectivity
来获取你的数据