Одна вещь, которую я пытался реализовать, — это возможность миграции из локальных Core Data в контейнеры CloudKit.
Поскольку этот пакет будет использоваться на разных приложения на разных стадиях разработки, я не могу просто обновить NSPersistentContainer до NSPersistentCloudKitContainer, как показано в видео WWDC. Это приведет к тому, что некоторые приложения начнут использовать CloudKit, хотя они еще не готовы к этому, когда я начну над ним работать.
Моей целью было иметь логическое значение при настройке имени .xcdatamodeld. для моего init, в котором указано useCloud.
На данный момент он «работает», но создает две отдельные базы данных — локальную версию и версию CloudKit. .
Я имею в виду, что если я установлю: CoreDataManager.setupSharedInstance(withModelName: "CDModel", useCloud: false), я смогу создавать данные и сохранять их в локальная база данных.
Если я отредактирую эту строку на CoreDataManager.setupSharedInstance(withModelName: "test", useCloud: true), похоже, база данных будет повторно инициализирована. Затем я могу сгенерировать больше данных, которые сохранятся, и их можно будет увидеть на информационной панели.
Однако, если я снова переключу логическое значение обратно на false, я теперь увижу старые исходные данные.
Я не совсем понимаю, где в коде я напутал, и надеюсь, что свежий взгляд сможет помочь.
Я пропустил функции CRUD, так как все они, похоже, находятся в init, где и заключается моя проблема. Версия CloudKit создана таким образом, чтобы я мог использовать CKShare и некоторые области общедоступных баз данных.
Код: Выделить всё
public final class CoreDataManager {
static internal var _modelName: String!
static public var shared: CoreDataManager!
private var _privatePersistentStore: NSPersistentStore?
private var _sharedPersistentStore: NSPersistentStore?
private var _publicPersistentStore: NSPersistentStore?
public static func setupSharedInstance(withModelName modelName: String, useCloud: Bool = false) {
_modelName = modelName
shared = CoreDataManager(modelName: modelName, useCloud: useCloud)
}
internal var container: NSPersistentContainer!
internal var context: NSManagedObjectContext { container.viewContext }
private init(modelName: String, useCloud: Bool) {
if useCloud {
setupCKContainer(modelName: modelName)
} else {
setupCDContainer(modelName: modelName)
}
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
do {
try context.setQueryGenerationFrom(.current)
} catch {
fatalError("Failed to pin viewContext to the current generation: \(error)")
}
}
private func setupCKContainer(modelName: String) {
container = NSPersistentCloudKitContainer(name: modelName)
guard let privateStoreDescription = container.persistentStoreDescriptions.first else {
fatalError("Unable to configure iCloud stores: No store descriptions available.")
}
let storeDescriptionUrl = privateStoreDescription.url?.deletingLastPathComponent()
let privateStoreURL = storeDescriptionUrl?.appendingPathComponent("private.sqlite", conformingTo: .database)
privateStoreDescription.url = privateStoreURL
let sharedStoreURL = storeDescriptionUrl?.appendingPathComponent("shared.sqlite", conformingTo: .database)
let publicStoreURL = storeDescriptionUrl?.appendingPathComponent("public.sqlite", conformingTo: .database)
guard let sharedStoreDescription = privateStoreDescription.copy() as? NSPersistentStoreDescription,
let publicStoreDescription = privateStoreDescription.copy() as? NSPersistentStoreDescription else {
fatalError("Copying the private store description returned an unexpected value.")
}
sharedStoreDescription.url = sharedStoreURL
publicStoreDescription.url = publicStoreURL
guard let containerIdentifier = privateStoreDescription.cloudKitContainerOptions?.containerIdentifier else {
fatalError("Unable to get containerIdentifier")
}
let sharedStoreOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: containerIdentifier)
let publicStoreOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: containerIdentifier)
sharedStoreOptions.databaseScope = .shared
publicStoreOptions.databaseScope = .public
sharedStoreDescription.cloudKitContainerOptions = sharedStoreOptions
publicStoreDescription.cloudKitContainerOptions = publicStoreOptions
container.persistentStoreDescriptions.append(sharedStoreDescription)
container.persistentStoreDescriptions.append(publicStoreDescription)
container.loadPersistentStores { loadedStoreDescription, error in
if let error = error as NSError? {
fatalError("Failed to load persistent stores: \(error)")
}
if let cloudKitContainerOptions = loadedStoreDescription.cloudKitContainerOptions {
guard let loadedStoreDescritionURL = loadedStoreDescription.url else {
return
}
loadedStoreDescription.setOption(
true as NSNumber,
forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey
)
loadedStoreDescription.setOption(
true as NSNumber,
forKey: NSPersistentHistoryTrackingKey
)
switch cloudKitContainerOptions.databaseScope {
case .private:
let privateStore = self.container.persistentStoreCoordinator.persistentStore(for: loadedStoreDescritionURL)
self._privatePersistentStore = privateStore
case .shared:
let sharedStore = self.container.persistentStoreCoordinator.persistentStore(for: loadedStoreDescritionURL)
self._sharedPersistentStore = sharedStore
case .public:
let publiceStore = self.container.persistentStoreCoordinator.persistentStore(for: loadedStoreDescritionURL)
self._publicPersistentStore = publiceStore
@unknown default:
fatalError("New database scope not accounted for")
}
}
}
}
private func setupCDContainer(modelName: String) {
container = NSPersistentContainer(name: modelName)
container.loadPersistentStores { loadedStoreDescription, error in
if let error = error as NSError? {
fatalError("Failed to load persistent stores: \(error)")
}
loadedStoreDescription.setOption(
false as NSNumber,
forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey
)
loadedStoreDescription.setOption(
false as NSNumber,
forKey: NSPersistentHistoryTrackingKey
)
}
}
public var ckContainer: CKContainer {
let description = container.persistentStoreDescriptions.first
guard let identifier = description?.cloudKitContainerOptions?.containerIdentifier else {
fatalError("Unable to get container identifier")
}
return CKContainer(identifier: identifier)
}
public var privatePersistentStore: NSPersistentStore {
guard let privateStore = _privatePersistentStore else {
fatalError("Private store is not set")
}
return privateStore
}
public var sharedPersistentStore: NSPersistentStore {
guard let sharedStore = _sharedPersistentStore else {
fatalError("Shared store is not set")
}
return sharedStore
}
public var publicPersistentStore: NSPersistentStore {
guard let publicStore = _publicPersistentStore else {
fatalError("Public store is not set")
}
return publicStore
}
}
Подробнее здесь: https://stackoverflow.com/questions/782 ... vice-versa