将核心数据从 Swift 迁移到 SwiftUI

Posted

技术标签:

【中文标题】将核心数据从 Swift 迁移到 SwiftUI【英文标题】:Migrate Core Data from Swift to SwiftUI 【发布时间】:2022-01-18 14:56:59 【问题描述】:

我正在尝试将应用程序从 Swift 迁移到 SwiftUI,但在处理 Core Data 时遇到了困难。我在同一个包标识符下运行 Swift 和 SwiftUI 应用程序,因此它们访问相同的底层数据,但是虽然我对两者使用相同的 xcdatamodeld 模型名称,但它们都指向不同的数据库。

我需要做的是在 Swift 上运行应用程序并将数据加载到 Core Data 中。然后重新运行 SwiftUI 版本的应用,就可以加载相同的数据了。

这里是 Swift 版本的代码:

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)")
        
    

在 SwiftUI 版本中,我生成 NSPersistentContainer() 并将其注入 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)
        
    

我哪里出错了?

【问题讨论】:

【参考方案1】:

我发现了为什么数据库没有出现在应用程序的 SwiftUI 版本中。原因是 Apple 在某些早期版本的 ios(不确定具体时间)中更改了存储位置,最初位于 Documents 文件夹中,现在位于 Library/Application%20Support 中。 所以解决办法就是改变NSPersistentStoreDescription的url:

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.

【讨论】:

以上是关于将核心数据从 Swift 迁移到 SwiftUI的主要内容,如果未能解决你的问题,请参考以下文章

核心数据迁移问题:无法启动storePath

核心数据迁移问题:storePath无法启动

使用 Objective C 创建并从 Swift 获取的核心数据

将核心数据模型从 OSX 迁移到 IOS

有没有办法将数据从核心数据迁移到在线数据库?

从 UIManagedDocument 到普通堆栈的核心数据迁移