核心数据+CloudKit | On/off iCloud 同步开关
CoreData+CloudKit | On/off iCloud sync toggle
我想为用户提供打开和关闭 iCloud 同步的选项。
经过一段时间的研究,我发现实现这一目标的一种方法是设置 cloudKitContainerOptions
。
所以如果我不想同步我的数据库,我会将其设置为 nil
。
if(!UserDefaultsManager.shared.iCloudSyncOn) {
description.cloudKitContainerOptions = nil
}
一切正常,但我还没有找到在运行时执行此操作的方法。
我尝试在用户切换时重新初始化我的容器,因此我的容器根据选择有不同的cloudKitContainerOptions
。
但这只会 return 我在保存上下文时出错,说:Thread 1: "Illegal attempt to establish a relationship 'addEntries' between objects in different contexts ...
,我认为这是由于重新初始化造成的。
我想我必须将新创建的上下文传递到我的整个视图层次结构,任何缓存 moc 的内容?
这里是我的 CoreDataStack 的简化片段:
func setupContainer() -> NSPersistentContainer {
let container = NSPersistentCloudKitContainer(name: "...")
guard let description = container.persistentStoreDescriptions.first else { ... }
...
if(!UserDefaultsManager.shared.iCloudSyncOn) {
description.cloudKitContainerOptions = nil
}
container.loadPersistentStores(completionHandler: { ... })
...
return container
}
当用户切换时,setupContainer()
被调用。
任何帮助都会很棒,当然也欢迎其他方式!
谢谢。
我已经成功了!
我的具体问题是,在重新初始化 persistenceContainer(它创建了一个新的上下文)之后,我没有更新我已经获取的对象(使用旧的上下文)。
因此,在调用 setupContainer()
之后,对我的所有对象进行一次简单的提取(使用新上下文)就足够了。
self.container = setupContainer()
CoreDataManager.shared.fetchAllItem()
补充
由于重新初始化,我又遇到了一个问题,这是一个警告,多个 NSEntityDescriptions
声称 NSManagedObject Subclass
我的实体。
答案帮我解决了。
最终代码
也许这可以帮到你。对我来说很好用。 (iOS 14.2)
稍作修改。
PS:我没有设置 cloudKitContainerOptions
,而是在 NSPersistentCloudKitContainer
和 NSPersistenttContainer
之间切换。
lazy var container: NSPersistentContainer = {
setupContainer()
}()
func updateContainer() {
saveContext()
container = setupContainer()
CoreDataManager.shared.fetchAllItems()
}
private func setupContainer() -> NSPersistentContainer {
let iCloud = UserDefaultsManager.shared.settingICloudSynch
do {
let newContainer = try PersistentContainer.getContainer(iCloud: iCloud)
guard let description = newContainer.persistentStoreDescriptions.first else { fatalError("No description found") }
if iCloud {
newContainer.viewContext.automaticallyMergesChangesFromParent = true
newContainer.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
} else {
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
}
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
newContainer.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") }
}
return newContainer
} catch {
print(error)
}
fatalError("Could not setup Container")
}
final class PersistentContainer {
private static var _model: NSManagedObjectModel?
private static func model(name: String) throws -> NSManagedObjectModel {
if _model == nil {
_model = try loadModel(name: name, bundle: Bundle.main)
}
return _model!
}
private static func loadModel(name: String, bundle: Bundle) throws -> NSManagedObjectModel {
guard let modelURL = bundle.url(forResource: name, withExtension: "momd") else {
throw CoreDataModelError.modelURLNotFound(forResourceName: name)
}
guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
throw CoreDataModelError.modelLoadingFailed(forURL: modelURL)
}
return model
}
enum CoreDataModelError: Error {
case modelURLNotFound(forResourceName: String)
case modelLoadingFailed(forURL: URL)
}
public static func getContainer(iCloud: Bool) throws -> NSPersistentContainer {
let name = "YOUR APP"
if iCloud {
return NSPersistentCloudKitContainer(name: name, managedObjectModel: try model(name: name))
} else {
return NSPersistentContainer(name: name, managedObjectModel: try model(name: name))
}
}
}
我想为用户提供打开和关闭 iCloud 同步的选项。
经过一段时间的研究,我发现实现这一目标的一种方法是设置 cloudKitContainerOptions
。
所以如果我不想同步我的数据库,我会将其设置为 nil
。
if(!UserDefaultsManager.shared.iCloudSyncOn) {
description.cloudKitContainerOptions = nil
}
一切正常,但我还没有找到在运行时执行此操作的方法。
我尝试在用户切换时重新初始化我的容器,因此我的容器根据选择有不同的cloudKitContainerOptions
。
但这只会 return 我在保存上下文时出错,说:Thread 1: "Illegal attempt to establish a relationship 'addEntries' between objects in different contexts ...
,我认为这是由于重新初始化造成的。
我想我必须将新创建的上下文传递到我的整个视图层次结构,任何缓存 moc 的内容?
这里是我的 CoreDataStack 的简化片段:
func setupContainer() -> NSPersistentContainer {
let container = NSPersistentCloudKitContainer(name: "...")
guard let description = container.persistentStoreDescriptions.first else { ... }
...
if(!UserDefaultsManager.shared.iCloudSyncOn) {
description.cloudKitContainerOptions = nil
}
container.loadPersistentStores(completionHandler: { ... })
...
return container
}
当用户切换时,setupContainer()
被调用。
任何帮助都会很棒,当然也欢迎其他方式!
谢谢。
我已经成功了!
我的具体问题是,在重新初始化 persistenceContainer(它创建了一个新的上下文)之后,我没有更新我已经获取的对象(使用旧的上下文)。
因此,在调用 setupContainer()
之后,对我的所有对象进行一次简单的提取(使用新上下文)就足够了。
self.container = setupContainer()
CoreDataManager.shared.fetchAllItem()
补充
由于重新初始化,我又遇到了一个问题,这是一个警告,多个 NSEntityDescriptions
声称 NSManagedObject Subclass
我的实体。
最终代码
也许这可以帮到你。对我来说很好用。 (iOS 14.2) 稍作修改。
PS:我没有设置 cloudKitContainerOptions
,而是在 NSPersistentCloudKitContainer
和 NSPersistenttContainer
之间切换。
lazy var container: NSPersistentContainer = {
setupContainer()
}()
func updateContainer() {
saveContext()
container = setupContainer()
CoreDataManager.shared.fetchAllItems()
}
private func setupContainer() -> NSPersistentContainer {
let iCloud = UserDefaultsManager.shared.settingICloudSynch
do {
let newContainer = try PersistentContainer.getContainer(iCloud: iCloud)
guard let description = newContainer.persistentStoreDescriptions.first else { fatalError("No description found") }
if iCloud {
newContainer.viewContext.automaticallyMergesChangesFromParent = true
newContainer.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
} else {
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
}
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
newContainer.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") }
}
return newContainer
} catch {
print(error)
}
fatalError("Could not setup Container")
}
final class PersistentContainer {
private static var _model: NSManagedObjectModel?
private static func model(name: String) throws -> NSManagedObjectModel {
if _model == nil {
_model = try loadModel(name: name, bundle: Bundle.main)
}
return _model!
}
private static func loadModel(name: String, bundle: Bundle) throws -> NSManagedObjectModel {
guard let modelURL = bundle.url(forResource: name, withExtension: "momd") else {
throw CoreDataModelError.modelURLNotFound(forResourceName: name)
}
guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
throw CoreDataModelError.modelLoadingFailed(forURL: modelURL)
}
return model
}
enum CoreDataModelError: Error {
case modelURLNotFound(forResourceName: String)
case modelLoadingFailed(forURL: URL)
}
public static func getContainer(iCloud: Bool) throws -> NSPersistentContainer {
let name = "YOUR APP"
if iCloud {
return NSPersistentCloudKitContainer(name: name, managedObjectModel: try model(name: name))
} else {
return NSPersistentContainer(name: name, managedObjectModel: try model(name: name))
}
}
}