From a1407e4121891126205f849fad90e18acf320ad2 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Thu, 28 Feb 2019 13:07:47 +0900 Subject: [PATCH] Revert workaround for iOS 10 NSFetchedResultsController (#100) --- .../FetchedResultsControllerDelegate.swift | 123 +++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/Sources/FetchedResultsControllerDelegate.swift b/Sources/FetchedResultsControllerDelegate.swift index de4b073..6d29988 100644 --- a/Sources/FetchedResultsControllerDelegate.swift +++ b/Sources/FetchedResultsControllerDelegate.swift @@ -86,6 +86,14 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult return } + + if #available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) {} + else { + + self.deletedSections = [] + self.insertedSections = [] + } + self.handler?.controllerWillChangeContent(controller) } @@ -100,6 +108,7 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult return } + self.handler?.controllerDidChangeContent(controller) } @@ -110,11 +119,102 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult return } + + if #available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) { + + self.handler?.controller( + controller, + didChangeObject: anObject, + atIndexPath: indexPath, + forChangeType: type, + newIndexPath: newIndexPath + ) + return + } + + guard var actualType = NSFetchedResultsChangeType(rawValue: type.rawValue) else { + + // This fix is for a bug where iOS passes 0 for NSFetchedResultsChangeType, but this is not a valid enum case. + // Swift will then always execute the first case of the switch causing strange behaviour. + // https://forums.developer.apple.com/thread/12184#31850 + return + } + + // This whole dance is a workaround for a nasty bug introduced in XCode 7 targeted at iOS 8 devices + // http://stackoverflow.com/questions/31383760/ios-9-attempt-to-delete-and-reload-the-same-index-path/31384014#31384014 + // https://forums.developer.apple.com/message/9998#9998 + // https://forums.developer.apple.com/message/31849#31849 + + if case .update = actualType, + indexPath != nil, + newIndexPath != nil { + + actualType = .move + } + + switch actualType { + + case .update: + guard let section = indexPath?[0] else { + + return + } + if self.deletedSections.contains(section) + || self.insertedSections.contains(section) { + + return + } + + case .move: + guard let indexPath = indexPath, let newIndexPath = newIndexPath else { + + return + } + guard indexPath == newIndexPath else { + + break + } + if self.insertedSections.contains(indexPath[0]) { + + // Observers that handle the .Move change are advised to delete then reinsert the object instead of just moving. This is especially true when indexPath and newIndexPath are equal. For example, calling tableView.moveRowAtIndexPath(_:toIndexPath) when both indexPaths are the same will crash the tableView. + self.handler?.controller( + controller, + didChangeObject: anObject, + atIndexPath: indexPath, + forChangeType: .move, + newIndexPath: newIndexPath + ) + return + } + if self.deletedSections.contains(indexPath[0]) { + + self.handler?.controller( + controller, + didChangeObject: anObject, + atIndexPath: nil, + forChangeType: .insert, + newIndexPath: indexPath + ) + return + } + self.handler?.controller( + controller, + didChangeObject: anObject, + atIndexPath: indexPath, + forChangeType: .update, + newIndexPath: nil + ) + return + + default: + break + } + self.handler?.controller( controller, didChangeObject: anObject, atIndexPath: indexPath, - forChangeType: type, + forChangeType: actualType, newIndexPath: newIndexPath ) } @@ -126,6 +226,18 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult return } + + if #available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) {} + else { + + switch type { + + case .delete: self.deletedSections.insert(sectionIndex) + case .insert: self.insertedSections.insert(sectionIndex) + default: break + } + } + self.handler?.controller( controller, didChangeSection: sectionInfo, @@ -142,4 +254,13 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult sectionIndexTitleForSectionName: sectionName ) } + + + // MARK: Private + + @nonobjc + private var deletedSections = Set() + + @nonobjc + private var insertedSections = Set() }