SwiftUI CoreData boolean

Viewed 151

I'm having some confusion with CoreData using SwiftUI, I'm just doing an example project for a Toggle. I set the newly created item in PersistenceController but the toggle always remains off. I don't have much experience using CoreData, my previous question I about CoreData was incorrect and I was misinformed about a fix.

PersistenceController

static var preview: PersistenceController = {
    let result = PersistenceController(inMemory: true)
    let viewContext = result.container.viewContext
    for _ in 0..<10 {
        let newItem = Item(context: viewContext)
        newItem.bool = false
    }
    do {
        try viewContext.save()
    } catch {
        let nsError = error as NSError
        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
    }
    return result
}()

ContentView

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.bool, ascending: true)], animation: .default)
    private var items: FetchedResults<Item>

    var body: some View {
        VStack {
            Toggle("", isOn: Binding<Bool>(
                get: { self.items.first?.bool ?? false },
                set: {
                    self.items.first?.bool = $0
                    try? viewContext.save()
                }
            ))
            .labelsHidden()
            .frame(width: 100, height: 100, alignment: .center)
        }
    }
}
1 Answers
  1. As it goes from the name of variable, preview is only used in preview, at least in newly generated project. So on real run your database if empty
  2. Using sortDescriptors your list of items is sorted ascending by bool. This means that as soon as you set first item bool to true, it gets moved to the end of the list, and you're reading bool value from an other object

To create a new Item at start when your database is empty, you need to wait persistent store load, fetch items and check that there're no items, in this case - create a new one. Replace PersistenceController init with following:

init(inMemory: Bool = false) {
    container = NSPersistentContainer(name: "SwiftUICodeDataPlayground")
    if inMemory {
        container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
    }
    container.loadPersistentStores(completionHandler: { [self] (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
        if (try? container.viewContext.fetch(Item.fetchRequest()).isEmpty) != false {
            let newItem = Item(context: container.viewContext)
            newItem.bool = false
        }
    })
}
Related