In memory CoreData container is empty when initialized

独自空忆成欢 提交于 2019-12-13 01:28:21

问题


tl;dr:

  1. clone the project: https://github.com/Jasperav/CoreDataInMemoryFail
  2. Run the test and see it fail. Why does my in memory container not have any data and how can I make sure it will have data?

Long:

I have a sqlite file with filled data and I have an in-memory database in CoreData. Some code:

// ...
func createInMemoryPerformanceTestDatabase() -> NSPersistentContainer {
    let url = createPathToSomeSQLiteFile()
    let container = NSPersistentContainer(name: dataModelName, managedObjectModel: objectModel)
    let description = NSPersistentStoreDescription(url: url)

    description.type = NSInMemoryStoreType

    container.persistentStoreDescriptions = [description]

    container.loadPersistentStores { description, error in
        XCTAssertNil(error)
    }

    return container
}
// ...

Although the sqlite file has data inside it, I don't see it back inside my contexts I create with container.

When I create an in-memory database with CoreData pointing to a sqlite file with data, I don't see any results when querying the database. I want to see the data inside the sqlite file. The data should just load all in memory. This is for testing purposes.


回答1:


The problem with what you have tried was that you set the type of your storeDescription as NSInMemoryStoreType before loading them into the container. Since, the type of storeDescription is stated as NSInMemoryStoreType the api won't read and populate data from the file URL you have provided. In order for the api to read the data from the file url, the type of storeDescription must be the one defined by initialising with the initialiser init(url: URL) which is SQLite in your case.

However if you want to have a persistentStore of type NSInMemoryStoreType with data read from the file url, you can migrate the persistentStores of your persistentContainer with NSInMemoryStoreType type using function migratePersistentStore:toURL:options:withType:error:. You can try out the code snippet below.

import CoreData
import XCTest
@testable import CoreDataInMemoryFail


class CoreDataInMemoryFailTests: XCTestCase {

    private func createContainer(modify: (NSPersistentContainer) -> ()) -> NSPersistentContainer {
        let bundle = Bundle(for: type(of: self))
        let path = bundle.path(forResource: "InMemoryDatabase", ofType: "sqlite")!
        let url = URL(fileURLWithPath: path)
        let persistentContainer = createPersistentContainer(dataModelName: "InMemoryDatabase")
        let storeDescription = NSPersistentStoreDescription(url: url)

        persistentContainer.persistentStoreDescriptions = [storeDescription]
        persistentContainer.loadPersistentStores { description, error in
            XCTAssertEqual(storeDescription.type, description.type)
            XCTAssertNil(error)
        }

        modify(persistentContainer)
        return persistentContainer
    }

    func testFail() {
        let persistentContainer = createContainer(modify: { _ in })
        let inMemoryContainer = createContainer { persistentContainer in
            let coordinator = persistentContainer.persistentStoreCoordinator
            coordinator.persistentStores.forEach { (persistentStore) in
                do {
                    try coordinator.migratePersistentStore(persistentStore, to: NSPersistentContainer.defaultDirectoryURL(), options: nil, withType: NSInMemoryStoreType)
                } catch {
                    print("Error while migrating persistentStore")
                }
            }
        }

        let persistentContainerCoordinator = persistentContainer.persistentStoreCoordinator
        persistentContainerCoordinator.persistentStores.forEach { (persistentStore) in
            XCTAssertEqual(persistentStore.type, "SQLite")
        }

        let inMemoryContainerCoordinator = inMemoryContainer.persistentStoreCoordinator
        inMemoryContainerCoordinator.persistentStores.forEach { (persistentStore) in
            XCTAssertEqual(persistentStore.type, NSInMemoryStoreType)
        }

        let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
        let persistentContainerCount = (try! persistentContainer.viewContext.fetch(fetchRequest)).count
        let inMemoryContainerCount = (try! inMemoryContainer.viewContext.fetch(fetchRequest)).count

        XCTAssertEqual(8, persistentContainerCount)
        XCTAssertEqual(persistentContainerCount, inMemoryContainerCount)
    }

}

In the above snippet, I have also added asserts to verify whether persistentStore type is NSInMemoryStoreType in your inMemoryContainer and SQLite in your persistentContainer. Hope it helps.




回答2:


The InMemoryType is not loading the date from your url as the other answer suggests. If you need to load the data from the file then please use the Migrate approach mentioned however if you only need to fill it with random data for testing purposes then here is another solution.

import CoreData
import XCTest
@testable import CoreDataInMemoryFail


class CoreDataInMemoryFailTests: XCTestCase {

    var persistentContainer: NSPersistentContainer!
    var inMemoryContainer: NSPersistentContainer!

    override func setUp() {
        super.setUp()
        persistentContainer = createContainer(modify: { _ in })
        inMemoryContainer = createContainer { storeDescription in
            storeDescription.type = NSInMemoryStoreType
        }
        initStubs()
    }

    override class func tearDown() {
        super.tearDown()
    }

    private func createContainer(modify: (NSPersistentStoreDescription) -> ()) -> NSPersistentContainer {
        let bundle = Bundle(for: type(of: self))
        let path = bundle.path(forResource: "InMemoryDatabase", ofType: "sqlite")!
        let url = URL(fileURLWithPath: path)
        let fileManager = FileManager.default
        let uuid = UUID().uuidString

        let saveDirectory = fileManager
                .urls(for: .cachesDirectory, in: .userDomainMask)[0]
                .appendingPathComponent(uuid)

        let saveLocation = saveDirectory.appendingPathComponent(url.lastPathComponent)

        try! fileManager.createDirectory(at: saveDirectory, withIntermediateDirectories: false)
        try! fileManager.copyItem(at: url, to: saveLocation)

        let persistentContainer = createPersistentContainer(dataModelName: "InMemoryDatabase")
        let storeDescription = NSPersistentStoreDescription(url: saveLocation)


        modify(storeDescription)
        print("TYPE OF STORE IS: \(storeDescription)")
        persistentContainer.persistentStoreDescriptions = [storeDescription]
        persistentContainer.loadPersistentStores { description, error in
            XCTAssertEqual(storeDescription.type, description.type)
            XCTAssertNil(error)
        }

        return persistentContainer
    }

    func initStubs() {

        func inserPerson( age: Int32) -> Person? {
            let obj = NSEntityDescription.insertNewObject(forEntityName: "Person", into: inMemoryContainer.viewContext)

            obj.setValue(age, forKey: "age")

            return obj as? Person
        }

        _ = inserPerson(age: 1)
        _ = inserPerson(age: 2)
        _ = inserPerson(age: 3)
        _ = inserPerson(age: 4)
        _ = inserPerson(age: 5)

        do {
            try inMemoryContainer.viewContext.save()
        }  catch {
            print("create fakes error \(error)")
        }

    }

    func removeData() {
        let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
        let objs = try! inMemoryContainer.viewContext.fetch(fetchRequest)
        for case let obj as NSManagedObject in objs {
            inMemoryContainer.viewContext.delete(obj)
        }
        try! inMemoryContainer.viewContext.save()
    }

    func testFail() {
        let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
        let persistentContainerCount = (try! persistentContainer.viewContext.fetch(fetchRequest)).count
        let inMemoryContainerCount = (try! inMemoryContainer.viewContext.fetch(fetchRequest)).count

        XCTAssertEqual(8, persistentContainerCount)
        XCTAssertEqual(5, inMemoryContainerCount)
    }

}

More info can be found here



来源:https://stackoverflow.com/questions/58584486/in-memory-coredata-container-is-empty-when-initialized

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!