swiftswiftuiswift-data

Unable to use preseeded persistent store with SwiftData


In trying to follow Paul Hudson's guide on pre-populating an app with an existing SwiftData database, I've found an error I'm stuck on, it only appears on a physical device, it works fine in the Simulator. Thanks in advance

I'm seeding the database in one app:

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Button("Seed") {
                create()
            }
        }
        .padding()
    }
    func create() {
        let container = NSPersistentContainer(name: "Model")
        let storeURL = URL.documentsDirectory.appending(path: "Model.store")
        if let description = container.persistentStoreDescriptions.first {
            try? FileManager.default.removeItem(at: storeURL)
            description.url = storeURL
            description.setValue("DELETE" as NSObject, forPragmaNamed: "journal_mode")
        }
        
        container.loadPersistentStores { description, error in
            do {
                for _ in 1...100 {
                    let user = Entity(context: container.viewContext)
                    user.identifier = UUID().uuidString
                    container.viewContext.insert(user)
                }
                try container.viewContext.save()
                try? FileManager.default.removeItem(at: URL(filePath: "/Users/engies/Downloads/Model.store"))
                let destination = URL(filePath: "/Users/engies/Downloads/Model.store")
                try FileManager.default.copyItem(at: storeURL, to: destination)
            } catch {
                print("Failed to create data: \(error.localizedDescription)")
            }
        }
    }
}

Entity+CoreDataClass.swift

import Foundation
import CoreData

public class Entity: NSManagedObject {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Entity> {
        return NSFetchRequest<Entity>(entityName: "Entity")
    }
    @NSManaged public var identifier: String
}

In the SwiftData App, I get the error when initializing the ModelContainer:

import SwiftUI
import SwiftData

@main
struct TestSeededApp: App {
    let container: ModelContainer
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
    init() {
        do {
            guard let storeURL = Bundle.main.url(forResource: "Model", withExtension: "store") else {
                fatalError("Failed to find users.store")
            }
            let config = ModelConfiguration(url: storeURL)
            container = try ModelContainer(for: Entity.self, configurations: config) // <- Erroring here
        } catch {
            fatalError("Failed to create model container: \(error)") 
        }
    }
}

The error: NSUnderlyingException=error during SQL execution : attempt to write a readonly database, reason=error during SQL execution : attempt to write a readonly database

Full(ish) error: UserInfo={sourceURL=file:///private/var/containers/Bundle/Application/0C1F96BC-D734-4E21-82A0-75BC9C1D6ADD/TestSeeded.app/Model.store, reason=Cannot migrate store in-place: error during SQL execution : attempt to write a readonly database, destinationURL=file:///private/var/containers/Bundle/Application/0C1F96BC-D734-4E21-82A0-75BC9C1D6ADD/TestSeeded.app/Model.store, NSUnderlyingError=0x283df0bd0 {Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={NSSQLiteErrorDomain=8, NSFilePath=/private/var/containers/Bundle/Application/0C1F96BC-D734-4E21-82A0-75BC9C1D6ADD/TestSeeded.app/Model.store, NSUnderlyingException=error during SQL execution : attempt to write a readonly database, reason=error during SQL execution : attempt to write a readonly database}}} with userInfo { NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={NSSQLiteErrorDomain=8, NSFilePath=/private/var/containers/Bundle/Application/0C1F96BC-D734-4E21-82A0-75BC9C1D6ADD/TestSeeded.app/Model.store, NSUnderlyingException=error during SQL execution : attempt to write a readonly database, reason=error during SQL execution : attempt to write a readonly database}"; destinationURL = "file:///private/var/containers/Bundle/Application/0C1F96BC-D734-4E21-82A0-75BC9C1D6ADD/TestSeeded.app/Model.store"; reason = "Cannot migrate store in-place: error during SQL execution : attempt to write a readonly database"; sourceURL = "file:///private/var/containers/Bundle/Application/0C1F96BC-D734-4E21-82A0-75BC9C1D6ADD/TestSeeded.app/Model.store"; } TestSeeded/TestSeededApp.swift:28: Fatal error: Failed to create model container: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer)


Solution

  • Found the answer here: How to pre-populate an app with a read-only store located in the app's bundle?

    This should probably be marked as duplicate