UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Upgrading from CoreData to SwiftData - what am I missing?

Forums > SwiftUI

I'm doing a test project to learn how to upgrade from CoreData to SwiftData. The problem that I'm having is that after changing to SwiftData, the data created from when the app was CoreData isn't showing up. Here's the steps I took.

  • Created a test app with Swift / SwiftUI / CoreData checked. That just creates a basic list / detail with timestamp.
  • Run that on an actual device and hit the plus button 5 times. Creates 5 timestamps.
  • Kill the app from Xcode and launch from device icon. The 5 timestamps are there.

Changing from CoreData to SwiftData

  • Clicked on the .xcdatamodelid, then went up to Editor, created SwiftData code. Code is below.
  • Clicked on the Peristance.swift file and deleted it.
  • Clicked on the .xcdatamodelid file and deleted it.
  • Modified the Item.swift code to get rid of the optional.
  • Changed the rest of the code to what is below.

Now I run the app. No errors or warnings. App runs on the same device. No timestamps are there. Click the plus button and can create new ones. But it should show the 5 that were created with CoreData.

What am I missing?

@Model public class Item {
    var timestamp: Date
    public init(timestamp: Date) {
        self.timestamp = timestamp
    }

}

@main
struct CDTest01App: App {

    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(for: Item.self)
        }
    }
}

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var items: [Item]

    var body: some View {
        NavigationSplitView {
            List {
                ForEach(items) { item in
                    NavigationLink {
                        Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
                    } label: {
                        Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
                    }
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
                ToolbarItem {
                    Button(action: addItem) {
                        Label("Add Item", systemImage: "plus")
                    }
                }
            }
        } detail: {
            Text("Select an item")
        }
    }

    private func addItem() {
        withAnimation {
            let newItem = Item(timestamp: Date())
            modelContext.insert(newItem)
        }
    }

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            for index in offsets {
                modelContext.delete(items[index])
            }
        }
    }
}

   

SwiftData Magic!

Behind the scenes, SwiftData creates a number of tables to represent your actual model. Apple's Swift boffins have written this boiler plate code for you. I recommend you take a look at the tables created by CoreData, then compare them to the tables created by SwiftData.

To see the table elements SwiftData produces, you can use an SQL editor, navigate to the actual datastores, and take a look inside. Warning! It's quite gnarly!

Stewart Lynch has a nice series on SwiftData on YouTube. In his series, he uses an SQL editor to peek inside datastores to demonstrate what SwiftData is doing. This may help you visualize the five records you store in CoreData, then determine if your SwiftData files read from the same files.

See -> SwiftData: CRUD Operations
Scrub to 6:16, or so to watch him explore a SwiftData file on his device.

Keep Coding!

This will be an interesting adventure. Please return here and share your findings.

   

hi Dennis,

the short answer to your question is: Core Data defaults its store to a file with extension .sqlite, but SwiftData defaults its store to a file with extension .store. so in your new app, SwiftData created a new store; it is not seeing any previous data created by Core Data.

@Obelix gives you a great reference to the Lynch series. at some point (i don't know if it's in the first video in the series or maybe later), Stewart will show how you can more carefully control how SwiftData gets set up.

of particular interest, Stewart shows how to create the SwiftData modelContainer directly at app startup (not using .modelContainer(for:)), using a ModelConfiguration where you can specify a URL for the persistent store. i believe he points that URL to the file originally created in the CRUD video with extension .store.

it would seem possible that if you set the URL to the old Core Data .sqlite file, that might do it for you. but i don't recall that specific usage of linking to an older .sqlite file being shown.

what could go wrong? you'd have to hope that the older sqlite file matches your SwiftData model exactly. since you defined the model using Xcode's creation directly from the older Core Data model, one hopes all will be well. (emphasis on hopes)

i'll also be curious to hear what you find out.

hopes that helps,

DMG

   

Jerry helps with

what could go wrong? you'd have to hope that the older sqlite file matches your SwiftData model exactly.
since you defined the model using Xcode's creation directly from the older Core Data model,
one hopes all will be well. (emphasis on hopes)

Yes!

This is the idea I was hoping @Dennis would follow.

   

Thanks to @Obelix, @delewaremathguy and Stewart Lynch, the answer to this is sort of simple. I tested the code on both the CDTest01 and one of my apps and there don't appear to be any issues. I'll do a lot more testing before i put it out to the world.

struct CDTest01App: App {

    var container: ModelContainer

    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(container)
        }
    }

    init() {

        let fileManager = FileManager.default
        let coreDataPath = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
        do {
            let config = ModelConfiguration(url: coreDataPath.appendingPathComponent("CDTest01.sqlite"))
            container = try ModelContainer(for: Item.self, configurations: config)
        } catch {
            fatalError("Failed to configure SwiftData container.")
        }
    }
}

   

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Click to save your free spot now

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.