В настоящее время я синхронизирую основные данные с базами данных CloudKit
private
и public
, как вы можете видеть в приведенном ниже коде, я сохраняю базу данных private
в конфигурации default
в Core Data, а public
в конфигурации под названием Public
все работает нормально, когда NSPersistentCloudKitContainer
синхронизируется, у меня проблема с попыткой сохранить в общедоступном хранилище данных PublicStore
, например, когда я пытаюсь сохранить с помощью func createIconImage(imageName: String)
, изображение сохраняется в хранилище «по умолчанию», а не в конфигурации PublicStore
(Public
).
Что я могу сделать, чтобы функция createIconImage()
сохранялась в базе данных PublicStore
sqlite?
class CoreDataManager: ObservableObject{
static let instance = CoreDataManager()
private let queue = DispatchQueue(label: "CoreDataManagerQueue")
@AppStorage(UserDefaults.Keys.iCloudSyncKey) private var iCloudSync = false
lazy var context: NSManagedObjectContext = {
return container.viewContext
}()
lazy var container: NSPersistentContainer = {
return setupContainer()
}()
init(inMemory: Bool = false){
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
}
func updateCloudKitContainer() {
queue.sync {
container = setupContainer()
}
}
private func getDocumentsDirectory() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
private func getStoreURL(for storeName: String) -> URL {
return getDocumentsDirectory().appendingPathComponent("\(storeName).sqlite")
}
func setupContainer()->NSPersistentContainer{
let container = NSPersistentCloudKitContainer(name: "CoreDataContainer")
let cloudKitContainerIdentifier = "iCloud.com.example.MyAppName"
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)
if iCloudSync{
if description.cloudKitContainerOptions == nil {
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudKitContainerIdentifier)
description.cloudKitContainerOptions = options
}
}else{
print("Turning iCloud Sync OFF... ")
description.cloudKitContainerOptions = nil
}
// Setup public database
let publicDescription = NSPersistentStoreDescription(url: getStoreURL(for: "PublicStore"))
publicDescription.configuration = "Public" // this is the configuration name
if publicDescription.cloudKitContainerOptions == nil {
let publicOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudKitContainerIdentifier)
publicOptions.databaseScope = .public
publicDescription.cloudKitContainerOptions = publicOptions
}
container.persistentStoreDescriptions.append(publicDescription)
container.loadPersistentStores { (description, error) in
if let error = error{
print("Error loading Core Data. \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return container
}
func save(){
do{
try context.save()
//print("Saved successfully!")
}catch let error{
print("Error saving Core Data. \(error.localizedDescription)")
}
}
}
class PublicViewModel: ObservableObject {
let manager: CoreDataManager
@Published var publicIcons: [PublicServiceIconImage] = []
init(coreDataManager: CoreDataManager = .instance) {
self.manager = coreDataManager
}
func createIconImage(imageName: String) {
let newImage = PublicServiceIconImage(context: manager.context)
newImage.imageName = imageName
newImage.id = UUID()
save()
}
func save() {
self.manager.save()
}
}
Да, у меня есть конфигурация «Общедоступная» и конфигурация «По умолчанию». Сущность PublicServiceIconImage
отображается в обеих конфигурациях, но самое смешное, что я не вижу способа удалить ее из конфигурации «по умолчанию», фактически, когда я добавляю новые сущности, они автоматически добавляются в конфигурацию «по умолчанию». даже после перетаскивания объектов в «Общедоступную» конфигурацию. Есть ли способ показать это только в «Публичной» конфигурации?
@TomHarrington - Похоже, вы написали книги о Core Data, у вас случайно нет обновленной книги, которую я могу купить?
После нескольких дней пробования разных вещей я случайно обнаружил, что порядок описаний в массиве контейнеров играет решающую роль в том, как Core Data определяет приоритеты загрузки конфигураций и сохранения сущностей. В моем исходном коде я сначала загружал конфигурацию по умолчанию, как показано ниже...
guard let description = container.persistentStoreDescriptions.first else{
fatalError("###\(#function): Failed to retrieve a persistent store description.")
}
Затем я добавлял общедоступную конфигурацию следующим образом...
container.persistentStoreDescriptions.append(publicDescription)
Оставляя общедоступную конфигурацию в конце массива persistStoreDescriptions, очевидно, что Core Data работает следующим образом: он ищет первую конфигурацию, и если сохраняемый объект существует в первой конфигурации, он сохраняет его в этой конфигурации, в противном случае он продолжает перебирать все конфигурации, пока не найдет объект в одной из конфигураций, но поскольку (по умолчанию) конфигурация Default
содержит все объекты, она всегда сохранялась в конфигурации по умолчанию, поэтому решение состоит в том, чтобы всегда оставлять конфигурацию по умолчанию последней. элемент в массиве.
Вот что я сделал, чтобы решить мою проблему:
Заменил эту строку...
container.persistentStoreDescriptions.append(publicDescription)
с...
container.persistentStoreDescriptions = [publicDescription, description]
Опять же, я фактически добавил общедоступную конфигурацию к первой конфигурации в массиве. Ключевым моментом здесь является всегда оставлять конфигурацию по умолчанию, независимо от того, сколько у вас конфигураций.
К вашему сведению: пример проекта из раздела «Связывание данных между двумя основными хранилищами данных» побудил меня попытаться изменить порядок конфигураций в массиве.
https://developer.apple.com/documentation/coredata/linking_data_between_two_core_data_stores
Включает ли ваша модель данных конфигурацию с именем «Общедоступная»? Если это так, убедитесь, что
PublicServiceIconImage
существует только в этой конфигурации, а не в каких-либо других, и объекты должны размещаться только в общедоступном хранилище.