WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Core Data constraints

Forums > 100 Days of SwiftUI

I am trying to understand Core Data and constraints. I am trying to establish a model with 2 entities: a Wizard which can have multiple spells, and a Spell which can be owned by multiple Wizards.

Each Wizard has has only one attribute, name, and each Spell has only one attribute, name.

I do not want duplicates to be saved so I set contraints on both name attributes in both Spell and Wizard.

I think the appropriate relationship is one Wizard to many Spells

My data model is called Wizards.xcdatamodeld

I am using Paul's instructions to manually unwrap the optionals and sets from the Core Data classes.

The app runs and loads and displays the appropriate example data.

I repeatedly get this error thrown with saving that I cannot understand.

"Thread 1: "This NSPersistentStoreCoordinator has no persistent stores (schema mismatch or migration failure). It cannot perform a save operation."

I searched but can't find an answer, can anyone help? I am attaching my code


struct ContentView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(sortDescriptors: []) var wizards: FetchedResults<Wizard>

    var body: some View {
        VStack {
            List {
                ForEach(wizards, id: \.self) {wizard in
                    Section(wizard.wrappedName) {
                        ForEach(wizard.spellArray, id: \.self) {spell in
                            Text(spell.wrappedName)
                        }
                    }
                }
            }

            Button("Add") {
                let spell1 = Spell(context: moc)
                spell1.name = "Heal"

                let spell2 = Spell(context: moc)
                spell2.name = "Fire"

                let spell3 = Spell(context: moc)
                spell3.name = "Shield"

                let spell4 = Spell(context: moc)
                spell4.name = "Armor"

                let wizard1 = Wizard(context: moc)
                wizard1.name = "Gandalf"

                let wizard2 = Wizard(context: moc)
                wizard2.name = "Harry Potter"

                wizard1.addToSpellBook(spell1)
                wizard1.addToSpellBook(spell2)
                wizard1.addToSpellBook(spell3)

                wizard2.addToSpellBook(spell1)
                wizard2.addToSpellBook(spell3)
                wizard2.addToSpellBook(spell4)

            }

            Button("Save") {
                do {
                    try moc.save()
                } catch {
                    print(error.localizedDescription)
                }
            }

        }
    }
}

import SwiftUI

@main
struct WizardsApp: App {

    @StateObject private var dataController = DataController()

    var body: some Scene {
        WindowGroup {
            ContentView()

            .environment(\.managedObjectContext, dataController.container.viewContext)
        }
    }
}

import CoreData
import Foundation

class DataController: ObservableObject {

    let container = NSPersistentContainer(name: "Wizards")

    init() {

        container.loadPersistentStores { description, error in
            if let error = error {
                print("Core Data failed to load: \(error.localizedDescription)")
                return
            }
        }

        self.container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
    }

}

import Foundation
import CoreData

extension Wizard {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Wizard> {
        return NSFetchRequest<Wizard>(entityName: "Wizard")
    }

    @NSManaged public var name: String?
    @NSManaged public var spellBook: NSSet?

    public var wrappedName: String {
        return name ?? ""
    }

    public var spellArray: [Spell] {
        let set = spellBook as? Set<Spell> ?? []
        return set.sorted {
            $0.wrappedName < $1.wrappedName
        }
    }

}

// MARK: Generated accessors for spellBook
extension Wizard {

    @objc(addSpellBookObject:)
    @NSManaged public func addToSpellBook(_ value: Spell)

    @objc(removeSpellBookObject:)
    @NSManaged public func removeFromSpellBook(_ value: Spell)

    @objc(addSpellBook:)
    @NSManaged public func addToSpellBook(_ values: NSSet)

    @objc(removeSpellBook:)
    @NSManaged public func removeFromSpellBook(_ values: NSSet)

}

extension Wizard : Identifiable {

}

import Foundation
import CoreData

extension Spell {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Spell> {
        return NSFetchRequest<Spell>(entityName: "Spell")
    }

    @NSManaged public var name: String?
    @NSManaged public var wizard: Wizard?

    public var wrappedName: String {
        name ?? ""
    }

}

extension Spell : Identifiable {

}

1      

I assume that the code is in separate files, because there appear to be a lot of repeated import commands.

When you create your CoreData you need to version your models especially if you have changed your models alot.

Coredata uses a SQLite DB, and Xcode / SwiftUI uses a lightweight migration every time you do a small change to models. Essentially, it automatically handles how to migrate your initial model definition (first schema) to the new / changed one (second schema), but only for small and simple changes. Do too many changes, or something complicated, it cannot automatically match the schemas and cannot perform the migration of the model and data from the old version to the new one. In this case you would have to write the merge functionality yourself, and that is a whole lot of other work.

No persistent stores schema mismatch

I think that if you delete the application, and any associated data, it should delete the SQLite DB as well, and you can start again.

1      

Thanks you for the help. All the code files were in separate files. I erased all contents and settings in the simulator and the program ran again without the error.

Is there an up to date tutorial for me to read on how to modify Core Date data models on existing projects? It was hard to find a good one on HWS+. My goal of taking the 100 Days of Swift UI is to create an app to manage books, and gradually add features over time. I am sure I will need to use Core Data and CloudKit eventually. I tried to find an understandable up to date tutorial and was not very successful.

1      

Core Data is one of the more common key topics to get familar with. Paul does an excellent explanation on how he views and uses it. It does require a more in-depth look to really start to understand the intricacies and behind-the-scenes methods used by SwiftUI to manage Core Data.

For reference I use the book 'Practical Core Data' by Donny Wals. It follows the Apple way of handling Core Data, which is to say the 'standard' way, not Paul's way. As a reference it is really good at explaning the concepts of Core Data. I expect that over time, as Core Data in SwiftUI changes, some of the examples will become out-of-date. The key points and methods, though, will remain relevant. If Core Data changes significantly, I would hope that Donny would then bring out a revised and updated version of this book.

If you search the internet, you will be able to find other resources that cover Core Data. Paul has written about how to be a developer, where he has also listed some other useful sources of information and learning. How to be an iOS developer in 2021

1      

Hacking with Swift is sponsored by Fernando Olivares

SPONSORED Fernando's book will guide you in fixing bugs in three real, open-source, downloadable apps from the App Store. Learn applied programming fundamentals by refactoring real code from published apps. Hacking with Swift readers get a $10 discount!

Read the book

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.