luizv
luizv

Reputation: 638

NSFetchedResultsController not tracking CoreData changes from the app extension

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

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!)
    }

}

Upvotes: 7

Views: 1077

Answers (2)

mattroberts
mattroberts

Reputation: 630

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.

Upvotes: 0

CSmith
CSmith

Reputation: 13458

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.

Upvotes: 0

Related Questions