如何使用引用了使用 "external storage" 保存的图像的 SQLite 文件预加载核心数据?

How to pre-load Core Data with a SQLite file that have references to images that were saved using "external storage"?

我的目标是在应用程序首次启动时预加载核心数据。到目前为止,我 运行 模拟并用数据填充了 Core Data。 (我勾选了“允许外部存储”)。

我进入 application_support 并复制了:MyApp.sqlite-wal、MyApp.sqlite-shm、.MyApp_SUPPORT/_EXTERNAL_DATA/ 和 MyApp.sqlite.

然后我在我的应用程序包中添加了 MyApp.sqlite 文件,并在我的应用程序委托中添加了这段代码:

lazy var persistentContainer: NSPersistentContainer = {
        let modelName = "MyApp"

        var container: NSPersistentContainer!

        container = NSPersistentContainer(name: modelName)
        
        
        // Preloading
        let appName: String = "MyApp"
        var persistentStoreDescriptions: NSPersistentStoreDescription

        let storeUrl = self.getDocumentsDirectory().appendingPathComponent("MyApp.sqlite")

        if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
            let seededDataUrl = Bundle.main.url(forResource: appName, withExtension: "sqlite")
            try! FileManager.default.copyItem(at: seededDataUrl!, to: storeUrl)
        }

        let description = NSPersistentStoreDescription()
        description.shouldInferMappingModelAutomatically = true
        description.shouldMigrateStoreAutomatically = true
        description.url = storeUrl

        container.persistentStoreDescriptions = [description]
        //End Preloading

        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {

                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

它有效,但它似乎找不到保存在外部存储器中的图像。它们作为参考出现在 .MyApp_SUPPORT/_EXTERNAL_DATA 中。 我应该在哪里添加参考文献?

加载一切是我的目标。

如果你有一个名为 MyApp.sqlite 的文件,在某个目录(这里是应用程序包)中有外部二进制存储,Core Data 会将这些文件放在一个名为 .MyApp_SUPPORT/_EXTERNAL_DATA/ 的子目录中。您需要递归复制该目录及其子目录中的所有内容。

不过,使用该路径不是一个好主意,因为它没有记录并且可能会在没有警告的情况下更改。此外,这将错过 MyApp.sqlite-walMyApp.sqlite-shm,如果它们存在的话。

更好的办法是将种子库放在自己的自定义目录中,然后从 那个 目录复制所有内容。您将拥有一个名为 MyAppSeedData 的目录,而不是只有 MyApp.sqlite,其中将包含 MyApp.sqlite。它还将包含 Core Data 所需的所有其他内容,如外部二进制文件。使用相同的 FileManager 函数复制 MyAppSeedData 目录(因为它会递归复制每个文件),你应该没问题。

复制文件夹的代码如下所示:

if let sourceDirURL = Bundle.main.url(forResource: "Source Folder", withExtension: nil) {
    let destinationDirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("TestFolder")
    if !FileManager.default.fileExists(atPath: destinationDirURL.path) {
        do {
            try FileManager.default.copyItem(at: sourceDirURL, to: destinationDirURL)
        } catch {
            print("Error copying directory: \(error)")
        }
    }
}

然后您可以将 SQLite 文件名添加到 destinationDirURL 的末尾并将其用于核心数据。

第 1 步:创建“MyAppSeedData”目录并粘贴 MyApp.sqlite、MyApp_SUPPORT、MyApp.sqilte-smh、MyApp.sqilte-wal 文件。

第 2 步:将 MyAppSeedData 拖到 AppDelegate 下的包中,然后勾选添加目标框。

第 3 步:这些函数必须在 AppDelegate 文件中:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
    //If first launch condition == true {
    seedData()
    //}
    return true
}

    
func seedData() {
    let fm = FileManager.default
    
    //Destination URL: Application Folder
    let libURL = fm.urls(for: .libraryDirectory, in: .userDomainMask).first!
    let destFolder = libURL.appendingPathComponent("Application Support").path
    //Or
    //let l1 = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last!
    //
    
    //Starting URL: MyAppSeedData dir
    let folderPath = Bundle.main.resourceURL!.appendingPathComponent("MyAppSeedData").path
    
    let fileManager = FileManager.default
        let urls = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask)
        if let applicationSupportURL = urls.last {
            do{
                try fileManager.createDirectory(at: applicationSupportURL, withIntermediateDirectories: true, attributes: nil)
            }
            catch{
                print(error)
            }
        }
    copyFiles(pathFromBundle: folderPath, pathDestDocs: destFolder)
}


func copyFiles(pathFromBundle : String, pathDestDocs: String) {
    let fm = FileManager.default
    do {
        let filelist = try fm.contentsOfDirectory(atPath: pathFromBundle)
        let fileDestList = try fm.contentsOfDirectory(atPath: pathDestDocs)

        for filename in fileDestList {
            try FileManager.default.removeItem(atPath: "\(pathDestDocs)/\(filename)")
        }
        
        for filename in filelist {
            try? fm.copyItem(atPath: "\(pathFromBundle)/\(filename)", toPath: "\(pathDestDocs)/\(filename)")
        }
    } catch {
        print("Error info: \(error)")
    }
}




// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
    let modelName = "MyApp"

    var container: NSPersistentContainer!

    container = NSPersistentContainer(name: modelName)
            
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()