NSFetchedResultsController major issue

I was working on a app with Xcode 6/iOS 8 for awhile now. The other day iOS 9 beta 4 and a new Xcode 7 beta was released, and I thought it would be stable enough to update my app to use Swift 2 (100% Swift project). The app supports iOS 8 and 9 now. I ran the app to my iOS 8 device, and noticed a strange effect on NSFetchedResultsController on iOS 8 only (doesn't happen on iOS 9 with Swift 2 for some reason).


So here is my implementation of NSFetchedResultsController,

func setupReturningShowsFetchedResultsController() {
        let fetchRequest = NSFetchRequest(entityName: "TVShow")
        let titleSort = NSSortDescriptor(key: "title", ascending: true)
        fetchRequest.sortDescriptors = [titleSort];

        let statusPredicate1 = NSPredicate(format: "status == 'returning series'")
        let statusPredicate2 = NSPredicate(format: "status == 'in production'")
        let statusPredicates = NSCompoundPredicate(orPredicateWithSubpredicates: [statusPredicate1, statusPredicate2])
        let predicate = NSPredicate(format: "upcomingEpisode == nil")
        fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [statusPredicates, predicate])
        fetchRequest.fetchBatchSize = 14;

        returningShowsResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil)

        do {
            try returningShowsResultsController!.performFetch()
        }
        catch let error as NSError {
            print(error)
        }
        catch {
   
        }
    }


The delegate is set to my view controller, but the FRC is set up in a data source class I made.


Anyways, in my app I create only ONE TVShow object, the status is "returning series" and it has no upcomingEpisode, so it is inserted into this FRC. I update 1 property and 2 relationships,

public func updateUnwatchedCount() {
        self.unwatchedCount = self.numberOfEpisodesLeftToWatch()

        /
        if let nextEpisode = self.getNextEpisodeToWatch() {
            self.nextEpisodeToWatch = nextEpisode
        }
        else {
            /
            self.nextEpisodeToWatch = nil
        }

        /
        if let upcomingEpisode = self.getNextEpisodeToAir() {
            self.upcomingEpisode = upcomingEpisode
        }
        else {
            /
            self.upcomingEpisode = nil
        }
    }


I then save the context (on the main thread).

What would you expect the NSFetchedResultsChangeType to be when this NSFetchedResultsControllerDelegate method is called?

func controller(controller: NSFetchedResultsController, didChangeObject anObject: NSManagedObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)


I expect only the update type, there is only one object and it was being update so that is the logcal assumption. For some reason, on iOS 8 only with Swift 2 and built with Xcode 7 beta 4, there are 2 calls to this delegate method for that 1 update. The first time, the NSFetchedResultsChangeType is .Update, but the second time it is .Insert. The app then throws this error,


2015-07-24 10:04:33.992 TVShows[35366:3921961] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-3347.44.2/UITableView.m:1623
Invalid update: invalid number of rows in section 1.  The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).


When I built the app before with Xcode 6, Swift 1.2 and iOS 8 only the delegate method was only called once as an update, but is now called twice on a iOS 8.4 device, project built with Swift 2 on Xcode 7. I have file a radar (21983293), I hope this bug can be fixed soon, I still need to be able to insert table view cells but the only fix I could find is comment out the code to insert table view cells.

Also, when the delegate method is called for the second time and I print out the NSFetchedResultsChangeType it prints

<invalid> (0x0)


and controllerWillChangeContent: is only called once

Similar situation:


- app in Swift 1.2 on iOS 8: OK

- app in Swift 2 on iOS 8: NG

- app in Swift 2 on iOS 9: OK

Does it crash under iOS 8.4 when compiled with Xcode 6.4, or only when you use Xcode 7 and target iOS 8.4?


Please file a bug report with sample code, if possible.

I filed it, number 21983293. It worked fine on iOS 8.4 compiled with Xcode 6.4, only happend with Xcode 7 on iOS 8.4.

Can confirm this issue on Xcode 7 Beta 3 and Xcode 7 Beta 5.


Thanks for filing this Maximilian, I'm having the exact same issue. I have a physical iPhone 5 running iOS 8.3, FRC will get Insert change type after Update when it should only be update.


Running in the Simulator with iOS 9 FRC works normally.

I can confirm this issus is still present in Xcode 7 beta 6 when compiling on iOS 8.4 (Swift 2.0).


If I compile my code in Xcode 7 beta 6 on iOS 9 beta 5, .Update works as expected and the delegate function is called once for .Update. If I however compile my code on iOS 8.4, I get the above warning... no update is visible until I relaunch the app.


The controller:didChangeObject:... function is called twice for .Update and .Insert even though I am only updating the object.


Hope the fix is on the way.

Yes, still a problem in the GM when running on 8.x simulator or device. I guess I have to leave my code in place that ignores the row Inserts whenever the new and old index paths are the same.


Here's some other conversations about this:

http://stackoverflow.com/questions/31383760/ios-9-attempt-to-delete-and-reload-the-same-index-path/31384014#31384014

https://forums.developer.apple.com/message/29866#29866

yup not fixed in the XC7 GM Seed - more than slightly annoying and worrying


XC6 iOS 8.4 good

XC7 iOS 9 good

XC7 iOS 8.4 big headache!

Hello,


OMG I found this... I have been hours crazy trying to figure out what was going on... Is there anyway/workaround the issue? like knowing when the type change is invalid or so? I tried printing the raw value but of course is 0, thats why is going into the case .Insert clause, hence making an invalid table update,

I can confirm wha tyou said,


I treid XC7 iOS 9 and probleem persisted!

I rolled back to Siwft 1.2 ( little bit of headachem thanks GIT!! 🙂), and now XC7 ios 9 problem is gone

Hi all. I have the same issue in xcode 7.0 (7A220) release!:

xcode6+iOS8 - no problem;
xcode7+iOS8 - not work!!;
xcode7+iOS9 - no problem;


The simple workaround - use [tableView reloadData] for any updates from FetchViewController (for iOS 8 case)
Yes, I know thats sounds bad, but it works...

NSFetchedResultsController major issue
 
 
Q