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

в настоящее время я работаю над обновлением уже существующего приложения (переход на 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.


person cr0ss    schedule 16.02.2017    source источник


Ответы (1)


Начиная с Watch OS3, вы не можете получить доступ к базе CoreData с помощью трюка группы приложений (потому что часы должны работать без телефона.

Поэтому вам нужно использовать WatchConnectivity для получения ваших данных.

person CZ54    schedule 23.02.2017
comment
Спасибо за ваш намек на WatchConnectivity, это близко к возможному решению, но не к тому, что я намеревался реализовать. Может быть, я попробую и оберну вокруг него остальную часть реализации. Вы получите награду. - person cr0ss; 25.02.2017