zur Zeit arbeite ich an einem Update für eine bereits existierende App (Migration nach Swift 3). Ich habe Ziele für Today-, Search-, Message- und Watch Extensions. Jedes Ziel muss auf das Kerndatenmodell meiner App zugreifen. Daher habe ich eine AppGroup erstellt und die Funktion für jedes Ziel aktiviert. Obwohl ich die NSPersistentStoreCoordinator subclassed, so ist alles in einem freigegebenen Ordner gespeichert:Swift3: Leerer Abruf beim Zugriff auf Core-Daten von Watch Extension über AppGroups
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()
}
}
Diese Klasse-Datei, sowie meine Kern-Data-Modell und die folgende Klasse alle in Ziel Mitgliedschaft aller sind die genannten Ziele. Das Codegen der Entitäten wird auf Class Definition
festgelegt. Bisher habe ich die Standardimplementierung für den Core Data-Stack bin mit:
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)")
}
}
}
}
Nun ist die seltsame Teil: Der Zugriff auf diese Daten aus dem heute- und Message-Erweiterung scheint ordnungsgemäß zu arbeiten (ich gehe davon aus das Suche- Extension funktioniert auch, aber soweit ich als Entwickler das nicht testen kann). Der Versuch, es mit derselben Anfrage von der Erweiterung der Watch-App zu holen, führt zu einem leeren Array. Hier ist mein Abrufcode:
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
}
Ein bisschen mehr Kontext zum besseren Verständnis: Die POI Entitie enthält geografische Breite und Länge eines Orts. Beim Start der App berechne ich die Entfernung zu jedem Punkt in der Datenbank von der aktuellen Benutzerposition aus. Wenn die Today-Extension versucht, die nächsten x (in meinem Fall 8) POIs an die aktuelle Benutzerposition zu bringen, ist das Abrufen aller 15k POIs und das Berechnen ihrer Entfernung zu viel Speicher. Also musste ich den Abstand vorberechnen (gespeichert in
locationDistanceCalculated
), dann in einem bestimmten Radius (dem statischenSETTINGS_MAXIMUM_FETCH_DISTANCE
) abholen und eine genaue Entfernung während des Holprozesses berechnen (gespeichert in einer transienten EigenschaftlocationDistanceTransient
).
In dem ExtensionDelegate
Klasse Watch App ein CLLocationManagerDelegate
implementiert und der Code wird aufgerufen, wenn der Standort des Benutzers aktualisiert:
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)
}
}
}
}
Es in Simulator und auf einem Gerät getestet wurde. Die context.fetch
gibt immer ein leeres Array zurück (Ja, Kerndaten enthalten Werte und ja, ich habe es ohne Prädikat getestet). Fehle ich etwas Neues in Core Data, das ich noch nicht in Betracht gezogen habe, oder sind es irgendwelche Einschränkungen in WatchOS3, warum dies nicht funktioniert? Hat jemand eine Ahnung? Danke für Ihre Hilfe.
Update: Wenn Sie ein Watch Framework-Ziel für den Zugriff auf Core-Daten verwenden, wie beschrieben in this project, bleibt der Abruf leer. Vielleicht könnte dies der richtige Weg sein, aber ein Watch Framework ist die falsche Wahl. Wird dich auf dem Laufenden halten.
Update 2: Ich habe bereits überprüft die App Programming Guide für WatchOS und die transferFile:metadata:
function in den API-Referenzen, aber es scheint nicht ein geeigneter Weg zu sein, um diese großen Datenmengen an den Apple Watch zu senden. Ich kann mich einfach nicht auf den Umstand verlassen, dass der Nutzer die App öfter überprüft als er aus dem Radius SETTINGS_MAXIMUM_FETCH_DISTANCE
fährt und für Orte mit hoher POI-Dichte sind diese Daten noch umfangreicher.
Update 3: Ich habe das in der Nähe befindliche Feature für POIs, die nur in einem bestimmten Radius abgerufen werden sollen, erneut implementiert. Wenn dies ein Problem für Sie löst, lesen Sie diese öffentliche Gist.
Dank holen für Ihre Hinweis auf WatchConnectivity, dies ist in der Nähe einer möglichen Lösung, aber nicht das, was ich implementieren wollte.Vielleicht werde ich es versuchen und den Rest der Implementierung darum wickeln.Sie erhalten die Belohnung. – cr0ss