CoreStore icon indicating copy to clipboard operation
CoreStore copied to clipboard

fetchAll returning empty array

Open darrenasaro opened this issue 4 years ago • 10 comments

I have an app which stores an entity class EntityA: NSManagedObject {...} I have the following simplified CoreData code to fetch this entity

    let container = NSPersistentContainer(name: "Model")
    let context = container.viewContext
    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: container.managedObjectModel)
    
    let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let persistentStoreURL = documentsPath.appendingPathComponent("Model.sqlite")
    
    try! coordinator.addPersistentStore(
      ofType: NSSQLiteStoreType,
      configurationName: nil,
      at: persistentStoreURL,
      options: [:]
    )

    container.loadPersistentStores { _, _ in }
    
    let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "EntityA")
    try! context.fetch(fetchRequest) as! [EntityA] // array populated with multiple objects

I created the equivalent CoreStore code

    let dataStack = DataStack(xcodeModelName: "Model")

    let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let sqliteFileUrl = documentsPath.appendingPathComponent("Model.sqlite")
    
    let storage = SQLiteStore(fileURL: sqliteFileUrl)
    try! dataStack.addStorageAndWait(storage)

    try! dataStack.fetchAll(From<ModelA>(nil)) // array empty

The CoreData code returns the properly populated array while CoreStore returns an empty array. I tried initializing SQLiteStore with a number of different options, and tried the async versions of addStorage all to no avail. I set breakpoints in the relevant CoreStore functions, but could not find anything glaring that would indicate the issue. Not sure if this is a bug or if I am misusing CoreStore but any help here would be much appreciated.

darrenasaro avatar Mar 17 '22 04:03 darrenasaro

@darrenasaro Since you're not getting an error at try! dataStack.fetchAll(), then it's likely your SQLite file does not contain records in the first place.

JohnEstropia avatar Mar 17 '22 04:03 JohnEstropia

It does as evidenced by the CoreData implementation returning a populated array. I've swapped these implementations verbatim with all else equal and the CoreData one always returns a populated array while the CoreStore implementation always returns empty.

darrenasaro avatar Mar 17 '22 04:03 darrenasaro

@darrenasaro Can you print the original file URL from here

let persistentStore = try! coordinator.addPersistentStore(
      ofType: NSSQLiteStoreType,
      configurationName: nil,
      at: persistentStoreURL,
      options: [:]
    )
print(persistentStore.url)

and check if it's still the same URL passed to SQLiteStore here?

let sqliteFileUrl = documentsPath.appendingPathComponent("Model.sqlite")

JohnEstropia avatar Mar 17 '22 09:03 JohnEstropia

@JohnEstropia They are the same. I've recreated the issue in it's simplest form here https://github.com/darrenasaro/fetch-demo

darrenasaro avatar Mar 17 '22 18:03 darrenasaro

Thanks for isolating the issue! I'll investigate

JohnEstropia avatar Mar 18 '22 00:03 JohnEstropia

@darrenasaro Oh boy... It looks like NSPersistentContainer forces the file directory to where it likes. Try this out:

print("Expected URL:\n\(sqliteFileUrl)")
container.loadPersistentStores { persistentStore, _ in
    print("NSPersistentContainer:\n\(persistentStore.url!)")
}
let store = try! dataStack.addStorageAndWait(storage)
print("CoreStore:\n\(store.fileURL)")

Look at the output:

Expected URL:
file:///Users/<User>/Library/Developer/CoreSimulator/Devices/185DD2F2-A17F-4FBB-9D90-2BDA5B802F57/data/Containers/Data/Application/2732C567-34D7-42EF-942C-2DB5F34CD27F/Documents/Model.sqlite

NSPersistentContainer:
file:///Users/<User>/Library/Developer/CoreSimulator/Devices/185DD2F2-A17F-4FBB-9D90-2BDA5B802F57/data/Containers/Data/Application/2732C567-34D7-42EF-942C-2DB5F34CD27F/Library/Application%20Support/Model.sqlite

CoreStore:
file:///Users/<User>/Library/Developer/CoreSimulator/Devices/185DD2F2-A17F-4FBB-9D90-2BDA5B802F57/data/Containers/Data/Application/2732C567-34D7-42EF-942C-2DB5F34CD27F/Documents/Model.sqlite

Note that NSPersistentContainer moved the directory to Library/Application Support/, while CoreStore keeps it to where it's actually configured (/Documents/).

JohnEstropia avatar Mar 18 '22 00:03 JohnEstropia

It looks like the behavior is correct if you use the NSPersistentStoreDescription method instead: Screen Shot 2022-03-18 at 10 13 28

JohnEstropia avatar Mar 18 '22 01:03 JohnEstropia

Since it looks like CoreStore is behaving correctly here, I'm marking this issue as a ios/compiler bug. I suggest that you find the original path actually created by the NSPersistentContainer for your store, and then pass it to CoreStore in your migrated code. I'd also recommend submitting a bug report to Apple through feedback assistant, since this issue seems destructive enough on its own.

JohnEstropia avatar Mar 18 '22 01:03 JohnEstropia

@JohnEstropia Thank you for investigating this! Excited I can use this library now. This CoreData behavior definitely seems to be problematic. I actually downloaded the container to investigate and it looks like CoreData puts an sqlite file in /Documents/(or whatever directory you specify) and Library/Application Support/, the former file having just structure data and the latter containing structure data as well as actual records. Super weird. Will follow with a bug report.

darrenasaro avatar Mar 18 '22 20:03 darrenasaro

Oh my, that's even worse. I'll try to explain that situation in the CoreStore README for future migrators. Thanks for investigating the behavior further

JohnEstropia avatar Mar 18 '22 22:03 JohnEstropia