NSPersistentCloudKitContainer - загрузка данный из CloudKit


#1

Кто пользуется нативной синхронизацией core data с CloudKit при помощи NSPersistentCloudKitContainer?

Проблема: где ловить окончание загрузки данных из CloudKit? Она происходит автоматом при чтении данных из CoreData в блоке do-cath. А вот как поймать окончание загрузки данных из облака неочень понятно.

P.S. Для тех, кто не знает, то с iOS13 для синхронизации ваших данных из CoreData c CloudKit достаточно изменить NSPersistentContainer на NSPersistentCloudKitContainer и включить поддержку CloudKit в конфигурации CoreData - все сущности в CloudKit создадутся автоматом при сохранениии контекста - очень удобно.
Подробнее здесь


#2

Коллеги профи и Алексей с Иваном, что за год существования ни кто не работал с NSPersistentCloudKitContainer? :frowning: Классная же штука! :smile:


#3

‘NSPersistentCloudKitContainer’ is only available in iOS 13.0 or newer

От профи бизнес требует поддержку iOS9/10 в лучшем случае 11. Мне допустим он не интересен (негде применить), а когда он будет интересен может ещё что-то лучшее придумают (устареет) :slight_smile:


#4

Ясно :slight_smile: буду думать )))


#5

Для синхронизации необходимо не только поменять контейнер, но и правильно настроить контейнер. Конечно не забываем в Capabilities включить Remote notification. В CoreData в конфигурации включить поддержку iCloud.

Давайте по порядку:

  1. Для правильной синхронизации с iCloud необходимо настроить историю отслеживания изменений, приёма и отправки уведомлений о изменении в контейнере

    guard let description = container.persistentStoreDescriptions.first else {
        fatalError("###\(#function): Failed to retrieve a persistent store description.")
    }
    
    description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
    description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
    
  2. Необходимо установить правильную политику синхронизации и объеденения новых данных

    container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
    container.viewContext.automaticallyMergesChangesFromParent = true
    
  3. Для отслеживания окончания загрузки данных или каких-либо изменений в БД на другом устройстве (или другим пользователем, если контейрнер публичный), нужно добавить наблюдателя по нужному ключу

    NotificationCenter.default.addObserver(self, selector: #selector(storeUpdate),  name: .NSPersistentStoreRemoteChange, object: nil)
    

В методе селектора удобно подтягивать обновлённые данные из КорДаты try localContext.fetch(request) и после обновлять все интерфейсы.

P.S. Для примера код инициализации контейнера в AppDelegate

lazy var persistentContainer: NSPersistentCloudKitContainer = {
        
        let container = NSPersistentCloudKitContainer(name: "YourContainer")
        
        guard let description = container.persistentStoreDescriptions.first else {
            fatalError("###\(#function): Failed to retrieve a persistent store description.")
        }
        description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
        
        container.loadPersistentStores(completionHandler: { (_, error) in
            if let error = error {
                fatalError("###\(#function): Failed to load persistent stores:\(error)")
            }
        })
        
        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        container.viewContext.automaticallyMergesChangesFromParent = true
        
        do {
            try container.viewContext.setQueryGenerationFrom(.current)
        } catch {
            fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
        }
        
        NotificationCenter.default.addObserver(self, selector: #selector(storeUpdate),  name: .NSPersistentStoreRemoteChange, object: nil)
        
        return container
    }()

#6

Письмо от Эппл

Бизнесу пора поторопиться )))


#7

Это не про это)



#8

Почему. Если только для корпоративных задач, которые не выкладываются в апстор. ))) Покапался в доках Эппл: это требования для всех новых приложений с 30.06.2020. Единственное нет конкретики про обновления старых приложений.


#9

Версия SDK != версия iOS. В последнем Xcode можно собрать приложение для iOS 8

Требование это для поддержки новых SDK, например uisegmentedcontrol в приложении собранном в iOS SDK 12 на iOS 13, будет выглядеть как в iOS 12

Если приложение пересобрать в SDK 13, будет соответствовать дизайну iOS 13.


#10

AAaaaAaaaa ))) спс, сразу не понял ))
Ну зато актуально к постоянным темам какой мак выбрать )))


#11

всем привет! я тоже использую NSPersistentCloudKitContainer, но у меня другая немного проблема. Есть приложение уже в App Store и я хочу в следующей версии сделать поддержку Today Widget, для этого я добавляю App Groups для обоих таргетов (основного и экстеншена) и переписываю NSPersistentCloudKitContainer, указав там url общего контейнера. Так вот, когда я запускаю приложение, то все данные стираются и появляются через 10-25 секунд, видимо происходит миграция данных из обычного в общий контейнер. Как можно избежать такой проблемы?

class PersistentContainer: NSPersistentCloudKitContainer {

override class func defaultDirectoryURL() -> URL{

return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: “group.com.sharingForTodayExtension”)!

}

override init (name: String, managedObjectModel model: NSManagedObjectModel) {

super .init(name: name, managedObjectModel: model)

}

}


#12

Обновление для работы со SwiftUI

Настройка отправки уведомлений об изменении записей в сторе также перед загрузкой данных из стора (loadPersistentStores)

container.persistentStoreDescriptions.forEach {
    $0.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
}

ловим уведомления

.onReceive(NotificationCenter.default.publisher(for: .NSPersistentStoreRemoteChange), perform: { _ in
    //обновляем представления если необходимо
})