Mark Cornelisse

  • Home
  • Apps
    • We All Pay
      • We all pay for iPhone
      • We all pay for iPad
    • Freeze – Your cool drink buddy
    • Help me decide between for iOS
  • Mac Apps
    • EMC – Easy Money Converter
    • Help Me Decide Between
  • Blog
  • About

Category Archives: iOS

NSFetchedResultsController Assertion failure

Posted on July 29, 2016 by Mark

I spent some time in creating a fix for We All Pay. The issue was a invalid data on screen when editing a payment on an iPad running iOS 8. The entire issue was caused by a bug in iOS 8 combined with Swift 2.2 and I wasn’t aware of it.

If you fetch data using NSFetchedResultsController on iOS 8 and you present that data on the screen everything is fine. However when you edit that data and write it to the Core Data database the NSFetchedResultsController will thing your changing values but also that you are inserting records. Resulting in a warning and not updating the screen properly. In the log file you can find the following error:

1
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-3347.44.2/UITableView.m:1406

So I was using this code to update the screen and its Objective-C counter part works just fine. Also for iOS 9 it works fine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    // MARK: NS Fetched Results Controller Delegate
    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        debugPrint("controllerWillChangeContent")
        tableView.beginUpdates()
    }
    
    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        debugPrint("controllerDidChangeContent")
        tableView.endUpdates()
    }
    
    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
            switch (type) {
            case .Insert:
                tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
            case .Delete:
                tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
            case .Update:
                tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
            case .Move:
                tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
                tableView.insertRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
            }
/    }

On iOS 8 it causes trouble. In order to solve these problems I suggest the following workaround:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    // MARK: NS Fetched Results Controller Delegate
    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        debugPrint("controllerWillChangeContent")
        tableView.beginUpdates()
    }
    
    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        debugPrint("controllerDidChangeContent")
        tableView.endUpdates()
    }
    
    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
        if #available(iOS 9, *) {
            switch (type) {
            case .Insert:
                tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
            case .Delete:
                tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
            case .Update:
                tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
            case .Move:
                tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
                tableView.insertRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
            }
        } else {
            switch (type) {
            case .Insert:
                if indexPath == nil {
                    tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
                }
            case .Delete:
                tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
            case .Update:
                tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
            case .Move:
                if indexPath == newIndexPath {
                    tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
                } else {
                    tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
                    tableView.insertRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
                }
            }
        }
    }

Cheers, if you have question or remarks please let me know.

Posted in Core Data, iOS, swift | Tags: iOS 8, NSFetchedResultsController, Swift |

Search

Pages

  • Apps
    • Freeze – Your cool drink buddy
    • Help me decide between for iOS
    • We All Pay
      • We all pay for iPhone
      • We all pay for iPad
  • Home
  • Mac Apps
    • EMC – Easy Money Converter
    • Help Me Decide Between
  • Blog
  • About
    • Cookies

Archives

  • July 2016
  • May 2015
  • March 2015
  • February 2015
  • January 2015
  • April 2014

Categories

  • AppKit (1)
  • Blog (3)
    • Core Data (1)
    • iOS (1)
  • CloudKit (1)
  • cocoa (3)
  • Launch Screen (1)
  • LLVM (1)
  • Objective-C (1)
  • osx (2)
  • swift (3)
  • xcode (2)

WordPress

  • Log in
  • WordPress
Copyright 2016 © Mark Cornelisse
This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish.Accept Read More
Privacy & Cookies Policy