[Ios] Swift3: пустая выборка при доступе к основным данным из Watch Extension через AppGroups



Answers

Question

в настоящее время я работаю над обновлением для уже существующего приложения (переход на Swift 3). У меня есть цели для Today-, Search-, Message- и Watch Extensions. Каждая цель должна получить доступ к базовой модели данных моего приложения, поэтому я создал 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 и следующий класс находятся в целевом членстве по всем указанным целям. Кодексу сущностей присвоено определение 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 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
    }

Немного больше контекста для лучшего понимания: POI Entitie содержит широту и долготу местоположения. При запуске приложения я предварительно вычисляю расстояние до каждой точки в базе данных с позиции текущего пользователя. Когда Today-Extension пытается получить ближайшие POI-объекты x (в моем случае 8) в текущую позицию пользователя, выборка всех 15-килобайтовых POI и вычисление их расстояния - это разумная память. Поэтому мне пришлось предварительно рассчитать расстояние (сохраненное в locationDistanceCalculated ), затем извлечь в заданном радиусе (статический SETTINGS_MAXIMUM_FETCH_DISTANCE ) и вычислить точное расстояние во время процесса выборки (сохраненное в locationDistanceTransient переходного свойстваDistanceTransient).

В классе ExtensionDelegate Watch App реализована 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)
            }
        }
    }
}

Он был протестирован в Simulator и на устройстве. context.fetch всегда возвращает пустой массив (Да, данные ядра содержат значения и да, я протестировал его без предиката). Я пропустил что-то новое в Core Data, которое я еще не рассматривал, или это их ограничения в WatchOS3, почему это не работает? Кто-нибудь знает? Спасибо за вашу помощь.

Обновление: при использовании целевого объекта Watch Framework для доступа к основным данным, как описано в этом проекте , выборка остается пустой. Возможно, это может быть правильный путь, но Watch Framework - неправильный выбор. Будет держать вас в курсе.

Обновление 2: Я уже проверил руководство по программированию приложений для WatchOS и функцию transferFile:metadata: в ссылках на API, но, похоже, это не подходит для отправки этих больших объемов данных в AppleWatch. Я просто не могу полагаться на то обстоятельство, что пользователь проверяет приложение чаще, чем он выходит из радиуса SETTINGS_MAXIMUM_FETCH_DISTANCE а для местоположений с высокой плотностью POI эти данные еще более обширны.

Обновление 3: Я переопределил функцию «Ближайшие функции для POI» для получения только в заданном радиусе. Если это решает проблему для вас, ознакомьтесь с этим публичным Gist .