Post

Replies

Boosts

Views

Created

Have you ever encountered NSFetchedResultsController not able to properly "section" based on sectionNameKeyPath, randomly?
This problem has buzzed me quite a while. So far, I still haven't founded a good solution. Currently, I have an entity with a Bool column named pinned. I use it as the sectionNameKeyPath for NSFetchedResultsController. So, my UICollectionView will always have 1 sections (All pinned = true, or All pinned = false), or 2 sections (Some pinned = true, and some pinned = false) When I toggle the pinned value from true to false, or false to true, I expect FRC shall fire a "move" callback. (Due to item has moved to a new section) This happen most of the time. However, sometimes, randomly, instead of firing "move" callback, FRC will fire "update" callback. When such incident happen, I will exam the content of fetchedResultsController.sections. Then, I will notice the entity item stays in wrong section. My FRC looks pretty straightforward. lazy var fetchedResultsController: NSFetchedResultsControllerNSPlainNote = { // Create a fetch request for the Quake entity sorted by time. let fetchRequest = NSFetchRequestNSPlainNote(entityName: "NSPlainNote") fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "pinned", ascending: false) ] // Create a fetched results controller and set its fetch request, context, and delegate. let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.INSTANCE.persistentContainer.viewContext, sectionNameKeyPath: "pinned", cacheName: nil ) controller.delegate = fetchedResultsControllerDelegate // Perform the fetch. do { try controller.performFetch() } catch { fatalError("Unresolved error \(error)") } return controller }() This is how I update the pinned column using background thread. func updatePinned(_ objectID: NSManagedObjectID, _ pinned: Bool) { let coreDataStack = CoreDataStack.INSTANCE let backgroundContext = coreDataStack.backgroundContext backgroundContext.perform { let nsPlainNote = try! backgroundContext.existingObject(with: objectID) as! NSPlainNote nsPlainNote.pinned = pinned RepositoryUtils.saveContextIfPossible(backgroundContext) } } I am not really sure, whether this can caused by my background thread. As, if I replace the backgroundContext with viewContext, I haven't observed the random problem so far. But, even so, I am not confident to conclude using backgroundContext is the culprit to this problem. The setup of my background thread is also pretty straightforward. I cannot see how it can went wrong. class CoreDataStack { public static let INSTANCE = CoreDataStack() private init() { } lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "xxx") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) // TODO: container.viewContext.automaticallyMergesChangesFromParent = true //container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy //container.viewContext.undoManager = nil //container.viewContext.shouldDeleteInaccessibleFaults = true return container }() lazy var backgroundContext: NSManagedObjectContext = { let backgroundContext = persistentContainer.newBackgroundContext() // TODO: //backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy //backgroundContext.undoManager = nil return backgroundContext }() } I was wondering, have anyone of you encounter similar problem when trying to utilize sectionNameKeyPath or FRC? Do you know what? Do you have any workaround/ solution for that? Thank you!
1
0
1.4k
Mar ’21
Is it possible to use background thread on NSFetchedResultsController for heavy read operation to ensure UI responsiveness?
Apple has shown us how to perform heavy write operation using background thread (by using newBackgroundContext) in their official earth quake example - https://github.com/yccheok/earthquakes-WWDC20 But, what about heavy read operation? (millions of rows for stress test purpose) We would also like our app UI to be responsiveness, when we are launching the app for first time, and the app is reading a large amount of data from CoreData. The following is the code snippet which is using NSFetchedResultController. [UI is not responsiveness if there are a lot of rows] } [UI is not responsiveness if there are a lot of rows] We try to perform controller.performFetch() using background thread. Still, but not sure why, the UI is still not responsiveness. My guess is that, after NSFetchedResultsController is occupied UI main thread, to perform some time consuming I/O read operation. } [UI is responsive now. But the solution is incorrect...] I guess, we need to place NSFetchedresultController under background thread too. Hence, we do the following modification. } The UI is responsiveness during fetching, and data is able to be fetched and shown after some time. However, if we investigate further by using the launching argument com.apple.CoreData.ConcurrencyDebug 1 We will experience the following crash after controller.performFetch() finish executed. CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]: May I know, is it ever possible to make UI responsive, when we are using NSFetchedResultController to load a large amount of data (few millions rows as per testing)? Can NSFetchedResultController ever operated under background thread?
0
0
746
Mar ’21