swift3 CoreData fetch返回nil

时间:2017-01-13 14:22:30

标签: ios swift core-data

请参阅以下更新:

有很多类似的问题,但似乎没有一个问题可以为我充分回答,所以任何帮助都将受到赞赏。

我有一个预装的数据库,我在一个单独的应用程序中创建。如果这是第一次运行,则将数据库复制到文档目录中。 我可以从日志中看到数据库位于预期的位置:

AppDelegate urls:  [file:///Volumes/HD1%20-%20DATA/Users/david/Library/Developer/CoreSimulator/Devices/8B0E16F5-B739-4299-9B4D-E6A3C8C92E55/data/Containers/Data/Application/B8A87853-6FEB-4532-83EF-AB08985F2B43/Documents/]

通过使用SQLiteManager,我可以验证数据库是否按预期保存数据。

enter image description here

我的CoreData对象图是:

enter image description here

但是,当我执行获取请求时,结果为nil。

这是获取请求:

func testLetters() {
        print(#function)

        let moc = coreDataStack.persistentContainer.viewContext
        let letterFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "LetterEntity")

        do {
            let fetchedLetters = try moc.fetch(letterFetch) as! [LetterEntity]
            print(#function, "letter: ", fetchedLetters)
        } catch {
            fatalError("Failed to fetch employees: \(error)")
        }
}

我的App Delegate外部有一个核心数据堆栈,如下所示:

import UIKit
import CoreData
class CoreDataStack {

    static let instance = CoreDataStack()

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "GuessGreek")

        let directoryUrls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

        let applicationDocumentDirectory = directoryUrls[0]

        let storeUrl = applicationDocumentDirectory.appendingPathComponent("GuessGreek.sqlite")

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

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



    func saveContext() {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch let error as NSError {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        }
    }
}

同样,欢迎任何帮助。

更新:我已按如下方式更改了数据库的副本:

let directoryUrls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)

let applicationDocumentDirectory = directoryUrls[0]

let managedContext = coreDataStack.persistentContainer.viewContext

let storeUrl = applicationDocumentDirectory.appendingPathComponent("GuessGreek.sqlite")

print("AppDelegate", #function, "StoreURL", storeUrl)


if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
    print("Copying databases...")

    let sourceSqliteURLs = [
        Bundle.main.url(forResource: "GuessGreekDatabase", withExtension: "sqlite")!,
        Bundle.main.url(forResource: "GuessGreekDatabase", withExtension: "sqlite-wal")!,
        Bundle.main.url(forResource: "GuessGreekDatabase", withExtension: "sqlite-shm")!]

    let destSqliteURLs = [
        applicationDocumentDirectory.appendingPathComponent("GuessGreek.sqlite"),
        applicationDocumentDirectory.appendingPathComponent("GuessGreek.sqlite-wal"),
        applicationDocumentDirectory.appendingPathComponent("GuessGreek.sqlite-shm")]

    for index in 0..<sourceSqliteURLs.count {
        do {
            try FileManager.default.copyItem(at: sourceSqliteURLs[index], to: destSqliteURLs[index])
            print(#function, "Done copying")
        } catch {
            print(error)
        }


        do {
            try managedContext.save()
        } catch let error as NSError  {
            print("Error While Saving Data: \(error.userInfo)")
        }
    }
}

我从Sim中移除了应用程序并清理。当我运行时,我收到文件无法复制的错误,因为它已经存在...我在这里遗漏了一些东西。

1 个答案:

答案 0 :(得分:1)

NSPersistentContainer默认使用Application Support目录,而不是文档目录。

如果在loadPersistentStores完成块中打印出storeURL的值,您将看到它指向Application支持。发生的事情是,它正在根据您的模型创建一个空白数据库,忽略您所制作的副本。

要么复制到应用程序支持,要么传入NSPersistentStoreDescription对象,其中URL指向文档目录,如果你想控制NSPersistentContainer的作用。

值得注意的是这些界限:

let directoryUrls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

let applicationDocumentDirectory = directoryUrls[0]

let storeUrl = applicationDocumentDirectory.appendingPathComponent("GuessGreek.sqlite")

在您当前的代码中完全没有做任何事情。

另外值得注意的是 - 您的SQLiteManager屏幕截图表明该文件名为GuessGreekDatabase.sqlite,但您的NSPersistentContainer默认为商店名称GuessGreek.sqlite,因此可能还需要修复,除非您在此期间执行此操作副本。