简体   繁体   中英

Migrate Core Data from Swift to SwiftUI

I am attempting to migrate an app from Swift to SwiftUI but am struggling with Core Data. I run both the Swift and SwiftUI apps under the same bundle identifier so they are accessing the same underlying data but although I use the same xcdatamodeld model name for both, they both point to different data bases.

What I need to do is run the app on Swift and load data into Core Data. Then re-run the SwiftUI version of the app and be able to load the identical data.

Here the code for the Swift version:

class DataStore {
    static let sharedDataStore = DataStore()
    var managedContext: NSManagedObjectContext!
    
    lazy var coreDataStack = CoreDataStack()
    
    fileprivate init() {
        self.managedContext = coreDataStack.context
    }
    
    func createParcours() -> Parcours {
        let parcours = Parcours(context: managedContext)
        parcours.timeStamp = NSDate()
        return parcours
    }
    
    func deleteParcours(_ toDelete: Parcours) {
        managedContext.delete(toDelete)
        self.saveParcours()
    }
    
    func saveContext(parcours: Parcours?) {
        if let parcours = parcours {
            encodeParcours(parcours)
        }
        coreDataStack.saveContext()
    }
}



class CoreDataStack {
    
    let modelName = "MyParcours" // Exactly same name as name.xcdatamodeld
    
    fileprivate lazy var applicationDocumentsDirectory: URL = {
        let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return urls[urls.count-1]
    }()
    
    lazy var context: NSManagedObjectContext = {
        var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = self.psc
        return managedObjectContext
    }()
    
    fileprivate lazy var psc: NSPersistentStoreCoordinator = {
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        
        let url = self.applicationDocumentsDirectory.appendingPathComponent(self.modelName)
        
        do {
            let options = [NSMigratePersistentStoresAutomaticallyOption: true]
            try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
        } catch {
            // Report any error we got.
            var dict = [String: AnyObject]()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
            dict[NSLocalizedFailureReasonErrorKey] = "There was an error creating or loading the application's saved data." as AnyObject?
            
            dict[NSUnderlyingErrorKey] = error as NSError
            let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
            // Replace this with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
            abort()
        }
        
        return coordinator
    }()
    
    fileprivate lazy var managedObjectModel: NSManagedObjectModel = {
        let modelURL = Bundle.main.url(forResource: self.modelName, withExtension: "momd")!
        return NSManagedObjectModel(contentsOf: modelURL)!
    }()
    
    func saveContext () {
        
        guard context.hasChanges else {return}
        
        do {
            try context.save()
        } catch let error as NSError {
            print("Unresolved error: \(error), \(error.userInfo)")
        }
    }
}

And in the SwiftUI version, I generate the NSPersistentContainer() and inject it into the ContentView:

class DataController: ObservableObject {
    let container = NSPersistentContainer(name: "MyParcours")
    
    init() {
        container.loadPersistentStores { NSEntityDescription, error in
            if let error = error {
                print("Core Data failed to load: \(error.localizedDescription)")
            }
        }
    }
}

@main
struct MySwiftUIApp: App {
    @StateObject private var dataController = DataController()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, dataController.container.viewContext)
        }
    }
}

Any pointers where I am going wrong?

I found out why the database did not show up in the SwiftUI version of the app. The reason is that Apple changed the storage location in some earlier version of iOS (not certain exactly when), originally located in the Documents folder and now in the Library/Application%20Support. So the solution is to change the url of the NSPersistentStoreDescription:

init() {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentsDirectory = paths[0].appendingPathComponent("MyParcours")
        
        self.container = NSPersistentContainer(name: "MyParcours")
        
        // Change URL to allow for compatibility with older version in Swift
        let description = NSPersistentStoreDescription(url: documentsDirectory)
        container.persistentStoreDescriptions = [description]
        
        container.loadPersistentStores { NSEntityDescription, error in

        etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM