Search code examples
swiftsqlitecore-datain-memory-database

In memory CoreData container is empty when initialized


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.


Solution

  • 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.