NSFetchedResultsController not tracking CoreData changes from the app extension
Asked Answered
D

2

7

I actively use StackOverflow almost daily for more than a year by now, but this is the first question I'm asking here. No other question grasp my exact problem. Solution to close ones doesn't work or doesn't really apply do my case.

What I'm doing

I'm developing an iOS app with a share extension. The extension app save links into CoreData, the containing(main) app, exhibits a listing of this links saved utilizing NSFetchResultsController.

The main app is the Master-Detail template provided by Xcode with really small adaptation.

I've created a embedded framework to carry CoreData stuff and set an App Group to host the CoreData and share it between apps in the usual way.

The CoreData part is working fine: I can insert objects into the ContextObjectManager and whenever my main app becomes active I can query this inserted object there promptly.

The problem

The problem starts when MasterViewController fails to update tableView. I'm inferring the NSFetchedResultsController instance do not track CoreData changes when it happens outside the main app.

I try some approaches while listening to when app becomes active:

func becomeActive(notification: NSNotification) {

    //Approach 1: Making sure to delete cache -- even though I do not have a persistent named cache file.        
    NSFetchedResultsController.deleteCacheWithName(nil)

    //Approach 2: Making sure to reload tableView data.
    self.tableView.reloadData()

    }

It didn't have any effect.

self.fetchedResultsController.sections![0].numberOfObjects it's always equal the number of exhibited cells. Then, if I share something new with the extension and then come back, this number doesn't change, but DataManager.getAllManagedObjectsFromEntity(Link.entityDescription()).objects.count equals this quantity + 1.

And then, when I close the app and open it again, it's synced and number of exhibited cells = number of objects fetched = number of all managed objects got from entity.

What I believe that could be possible solution paths

  • If I could notify the main app from a CoreData modification from the extension app, I could update the context right in the MasterViewController. I can't find a not-a-hot-fix way to do that.
  • I read about "background context" and "background context changes vs main context changes" in one stackoverflow question, but I cannot grasp exactly how it relate to my problem. I'm utilizing only one context.
  • I believe if I can reset the context or refetch results, the problem is solved. But how?

It's it. Someone have any suggestions/solutions of what I need to do to sync up the fetched results?

Relevant Code

NSFetchedResultsController instance in MasterViewController.swift

var fetchedResultsController: NSFetchedResultsController {
    if _fetchedResultsController != nil {
        return _fetchedResultsController!
    }

    let fetchRequest = NSFetchRequest()
    // Edit the entity name as appropriate.
    let entity = NSEntityDescription.entityForName("Link", inManagedObjectContext: DataAccess.shared.managedObjectContext)
    fetchRequest.entity = entity

    // Set the batch size to a suitable number.
    fetchRequest.fetchBatchSize = 20

    // Edit the sort key as appropriate.
    let sortDescriptor = NSSortDescriptor(key: "title", ascending: false)

    fetchRequest.sortDescriptors = [sortDescriptor]


   /// Template original code
   //  let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")

   /// My adaptation
    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DataAccess.shared.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
    aFetchedResultsController.delegate = self
    _fetchedResultsController = aFetchedResultsController

    do {
        try _fetchedResultsController!.performFetch()
    } catch {
         abort()
    }

    return _fetchedResultsController!
}    

func getAllManagedObjectsFromEntity(_:) at embedded framework's DataManager.swift file

public class func getAllManagedObjectsFromEntity(entity: NSEntityDescription) -> (sucesso: Bool, objects: NSArray){

   // let delegate = (UIApplication.sharedApplication()).delegate as! AppDelegate
    let context:NSManagedObjectContext? = DataAccess.shared.managedObjectContext

    let request:NSFetchRequest = NSFetchRequest()
    request.entity = entity

    var error:NSError?
    var objects:NSArray?
    do {
        objects = try context?.executeFetchRequest(request)
    } catch let error1 as NSError {
        error = error1
        objects = nil
    }

    if(error == nil){
        return(true, objects!)
    }else{
        NSLog(error!.description)
        return(false, objects!)
    }

}
Disconcerted answered 11/8, 2016 at 15:0 Comment(6)
What is DataManager.getAllManagedObjectsFromEntity? What does it do, exactly?Excuse
Hi, Tom. It's querying the context for all objects of entity's type at coreData with a fetchRequest. I'm updating the answer to show you the exact function.Disconcerted
Sorry @tom, I undertand why you asked it. DataManager.getAllManagedObjectsFromEntity(_:).objects is an array. I meant DataManager.getAllManagedObjectsFromEntity(_:).objects.count.Disconcerted
@Tom, any thoughts on what I could try?Disconcerted
were you able to fix this? I'm currently having the same issueWabash
Have you implemented the delegate methods? Providing an empty implementation of controllerDidChangeContent(_:) is sufficient.Tatouay
L
0

NSFetchedResultsController may have an NSFetchedResultsControllerDelegate attached.

You're already declaring that you implement NSFetchedResultsControllerDelegate:

aFetchedResultsController.delegate = self

so add the controllerDidChangeContent method, where you'll reload your table when this is called:

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
{
  self.tableView.reloadData
}

The NSFetchedResultsControllerDelegate also provides a dictionary on exactly what has changed, giving you very fine grained control.

Lugsail answered 12/8, 2016 at 19:44 Comment(1)
Hi @CSmith. Thanks for the suggestion, but it didn't work. I've tried it before. I'm guessing the self.tableView.reloadData reloads items listed on self.fetchedResultsController.sections![0], but this list wasn't updated when I added some object to the context from my extension app.Disconcerted
M
0

I had a similar problem, but, every time the application entered the foreground again, I made a new NSFetchedResultsController, performed my fetch, and then reloaded the UITableView and then it seemed all was up to date.

Monkshood answered 12/6, 2021 at 15:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.