в настоящее время я работаю над обновлением уже существующего приложения (переход на Swift 3). У меня есть цели для расширений «Сегодня», «Поиск», «Сообщение» и «Смотреть». Каждая цель должна иметь доступ к базовой модели данных моего приложения, поэтому я создал AppGroup и включил возможность для каждой цели. Хотя я создал подкласс 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()
}
}
Этот файл класса, а также моя Core-Data-Model и следующий класс находятся в целевом членстве всех упомянутых целей. Codegen сущностей установлен на Class Definition
. Пока я использую реализацию по умолчанию для Core Data Stack:
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 приводит к пустому массиву. Вот мой код извлечения:
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
}
Немного больше контекста для лучшего понимания: Сущность POI содержит широту и долготу местоположения. При запуске приложения я заранее рассчитываю расстояние до каждой точки в базе данных от текущего положения пользователя. Когда Today-Extension пытается получить ближайшие x (в моем случае 8) POI к текущему положению пользователя, извлечение всех 15k POI и вычисление их расстояния занимает много памяти. Поэтому мне пришлось предварительно вычислить расстояние (сохраненное в
locationDistanceCalculated
), затем выбрать заданный радиус (статическийSETTINGS_MAXIMUM_FETCH_DISTANCE
) и вычислить точное расстояние в процессе выборки (сохраненное в переходном свойствеlocationDistanceTransient
).
В классе ExtensionDelegate
приложения Watch реализован 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
всегда возвращает пустой массив (да, основные данные содержат значения, и да, я тестировал их без предиката). Я упустил что-то новое в Core Data, что я еще не рассмотрел, или есть какие-то ограничения в WatchOS3, почему это не работает? Кто-нибудь знает? Спасибо за вашу помощь.
Обновление: при использовании цели Watch Framework для доступа к Core Data, как это описано в этом проекте. выборка остается пустой. Может быть, это и правильный путь, но Watch Framework — неправильный выбор. Будет держать вас в курсе.
Обновление 2: я уже проверил Руководство по программированию приложения для WatchOS и transferFile:metadata:
функция в справочниках по API, но это не кажется подходящим способом отправки таких больших объемов данных на AppleWatch. Я просто не могу полагаться на то обстоятельство, что пользователь проверяет приложение чаще, чем выезжает за пределы SETTINGS_MAXIMUM_FETCH_DISTANCE
радиуса, а для мест с высокой плотностью POI эти данные еще более обширны.
Обновление 3: я повторно реализовал функцию поблизости для POI, чтобы получать информацию только в заданном радиусе. Если это решит вашу проблему, ознакомьтесь с общедоступным Gist.