I want to give the user the option to toggle iCloud sync on and off.
So I'm switching between NSPersistentCloudKitContainer and NSPersistenttContainer
But...
When I switch off the CloudKit synchronization and update the container, the sync is still active.
After restarting the app manually the sync is deactivated.
This is the same problem some people are describing in the comments here...
https://stackoverflow.com/questions/65355720/coredatacloudkit-on-off-icloud-sync-toggle
I wasn't able to find a solution.
Here is my code:
MyApp.swift
@main
struct MyApp: App {
@StateObject private var persistenceContainer = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(CoreBluetoothViewModel())
.environment(\.managedObjectContext, persistenceContainer.container.viewContext)
}
}
}
PersistenceController
import CoreData
class PersistenceController: ObservableObject{
static let shared = PersistenceController()
lazy var container: NSPersistentContainer = {
setupContainer()
}()
init() {
container = setupContainer()
}
func updateContainer() {
saveContext()
container = setupContainer()
saveContext()
}
private func setupContainer() -> NSPersistentContainer {
let iCloud = UserDefaults.standard.bool(forKey: "iCloud")
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.cloudKitContainerOptions = nil
}
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")
}
private func saveContext() {
do {
try container.viewContext.save()
} catch {
let error = error as NSError
fatalError("ERROR: \(error)")
}
}
}
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 = "LogModel"
if iCloud {
return NSPersistentCloudKitContainer(name: name, managedObjectModel: try model(name: name))
} else {
return NSPersistentContainer(name: name, managedObjectModel: try model(name: name))
}
}
}
Does anyone have any idea how I could solve this problem?
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm trying to use the NSUbiquitousKeyValueStore to store a value, but I am getting this error down below.
The local UserDefaults are working, but the iCloud Key-Value-Store isn't.
Console Output
2023-02-14 13:28:02.666612+0000 MyApp[32459:8679108] [Connection] BUG IN KVS: Tried to access store that is unknown to the system (<TeamID>.dev.LuegM.MyApp). Please send this bug to NSUbiquitousKeyValueStore.
2023-02-14 13:28:02.710684+0000 MyApp[32459:8679452] [Connection] Error synchronizing with cloud for store <(<TeamID>.dev.LuegM.DiveApp)>: Error Domain=SyncedDefaults Code=101010 "Tried to access unknown store <TeamID>.dev.LuegM.DiveApp" UserInfo={NSLocalizedDescription=Tried to access unknown store <TeamID>.dev.LuegM.DiveApp}
MyApp.entitlements
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.dev.luegm.MarineM.Cloud</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
</dict>
Is this perhaps related to the fact that I am using this configuration as a CloudKit container?
iCloud Capability
I'm working on a Core Data / CloudKit public Database.
Apple recommends not to delete the object but to set a variable e.g. isTrashed to true.
After that a predicate should be set for the FetchRequest so that this object is no longer displayed.
So in my view I set this variable to true when an object is deleted and this is also set correctly. Here is the query directly from the dashboard:
Unfortunately the predicate doesn't work as it should and the object is still displayed. Can anyone tell me what I am doing wrong?
Fetch Request
static var productsByName: NSFetchRequest<Product> {
/// create Request
let request: NSFetchRequest<Product> = Product.fetchRequest()
/// sortDescriptor
request.predicate = NSPredicate(format: "isTrashed = false") // <-- predicate for isTrashed
request.sortDescriptors = [NSSortDescriptor(keyPath: \Product.name, ascending: true)]
return request
}
ContentView.swift
struct ContentView: View {
@SectionedFetchRequest(fetchRequest: ContentView.productsByName, sectionIdentifier: \Product.manufacturer?.name)
var products: SectionedFetchResults<String?, Product>
var body: some View {
NavigationStack {
List {
ForEach(products) { section in
Section(header: Text(section.id ?? "unassigned")) {
ForEach(section) { product in
NavigationLink(destination: productDetailView(product: product)){
Text(product.name ?? "na")
}
}
.onDelete { rows in
for row in rows {
let product = section[row]
if storageProvider.persistentContainer.canDeleteRecord(forManagedObjectWith: product.objectID) {
storageProvider.deleteProduct(product)
} else {
product.isTrashed = true // <-- here the value for isTrashed is set
do {
try storageProvider.persistentContainer.viewContext.save()
} catch {
storageProvider.persistentContainer.viewContext.rollback()
print("Failed to save context: \(error)")
}
}
}
}
}
}
}
}
}
}
info 1
additional info:
I added this code to the detailView of the products to check if the value is set correct:
Text("is trashed: \(product.isTrashed ? "true" : "false")")
... and yes it is:
info 2
I also tried these for the query:
request.predicate = NSPredicate(format: "isTrashed = 0")
request.predicate = NSPredicate(format: "isTrashed = FALSE")
request.predicate = NSPredicate(format: "isTrashed = NO")
request.predicate = NSPredicate(format: "isTrashed = 0")
request.predicate = NSPredicate(format: "isTrashed == FALSE")
request.predicate = NSPredicate(format: "isTrashed == NO")
request.predicate = NSPredicate(format: "%K = %@", argumentArray: [#keyPath(Product.isTrashed), false])
request.predicate = NSPredicate(format: "%K == %@", argumentArray: [#keyPath(Product.isTrashed), false])
So I don't think the query itself is the problem :/
info 3
but for example this predicate directly on the view is working:
.onReceive(productName.$debounced) { query in
/// don't filter when searchbar is empty
guard !query.isEmpty else {
products.nsPredicate = nil
return
}
/// set filter when someone searches
products.nsPredicate = NSPredicate(format: "%K CONTAINS[cd] %@", argumentArray: [#keyPath(Product.name), query])
}
That's why I don't understand why the predicate on the FetchRequest isn't working.
Does anyone have an idea?