mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-22 09:29:18 +01:00
removed long-standing workaround for NSFetchedResultsController bugs since they seem to be fixed
This commit is contained in:
@@ -47,6 +47,12 @@ public protocol CoreStoreObjectKeyValueObservation: class {
|
||||
|
||||
extension ValueContainer.Required {
|
||||
|
||||
/**
|
||||
Observes changes in the receiver value. When the returned `CoreStoreObjectKeyValueObservation` is deinited or invalidated, it will stop observing.
|
||||
|
||||
- parameter options: The flags indicating which values to include in the change dictionary.
|
||||
- parameter: changeHandler: The closure called when the value is updated.
|
||||
*/
|
||||
public func observe(options: NSKeyValueObservingOptions = [], changeHandler: @escaping (O, CoreStoreObjectValueDiff<V>) -> Void) -> CoreStoreObjectKeyValueObservation {
|
||||
|
||||
return self.observe(with: options, changeHandler: changeHandler)
|
||||
@@ -58,6 +64,12 @@ extension ValueContainer.Required {
|
||||
|
||||
extension ValueContainer.Optional {
|
||||
|
||||
/**
|
||||
Observes changes in the receiver value. When the returned `CoreStoreObjectKeyValueObservation` is deinited or invalidated, it will stop observing.
|
||||
|
||||
- parameter options: The flags indicating which values to include in the change dictionary.
|
||||
- parameter: changeHandler: The closure called when the value is updated.
|
||||
*/
|
||||
public func observe(options: NSKeyValueObservingOptions = [], changeHandler: @escaping (O, CoreStoreObjectValueDiff<V>) -> Void) -> CoreStoreObjectKeyValueObservation {
|
||||
|
||||
return self.observe(with: options, changeHandler: changeHandler)
|
||||
@@ -69,6 +81,12 @@ extension ValueContainer.Optional {
|
||||
|
||||
extension TransformableContainer.Required {
|
||||
|
||||
/**
|
||||
Observes changes in the receiver value. When the returned `CoreStoreObjectKeyValueObservation` is deinited or invalidated, it will stop observing.
|
||||
|
||||
- parameter options: The flags indicating which values to include in the change dictionary.
|
||||
- parameter: changeHandler: The closure called when the value is updated.
|
||||
*/
|
||||
public func observe(options: NSKeyValueObservingOptions = [], changeHandler: @escaping (O, CoreStoreObjectTransformableDiff<V>) -> Void) -> CoreStoreObjectKeyValueObservation {
|
||||
|
||||
return self.observe(with: options, changeHandler: changeHandler)
|
||||
@@ -80,6 +98,12 @@ extension TransformableContainer.Required {
|
||||
|
||||
extension TransformableContainer.Optional {
|
||||
|
||||
/**
|
||||
Observes changes in the receiver value. When the returned `CoreStoreObjectKeyValueObservation` is deinited or invalidated, it will stop observing.
|
||||
|
||||
- parameter options: The flags indicating which values to include in the change dictionary.
|
||||
- parameter: changeHandler: The closure called when the value is updated.
|
||||
*/
|
||||
public func observe(options: NSKeyValueObservingOptions = [], changeHandler: @escaping (O, CoreStoreObjectTransformableDiff<V>) -> Void) -> CoreStoreObjectKeyValueObservation {
|
||||
|
||||
return self.observe(with: options, changeHandler: changeHandler)
|
||||
@@ -91,6 +115,12 @@ extension TransformableContainer.Optional {
|
||||
|
||||
extension RelationshipContainer.ToOne {
|
||||
|
||||
/**
|
||||
Observes changes in the receiver value. When the returned `CoreStoreObjectKeyValueObservation` is deinited or invalidated, it will stop observing.
|
||||
|
||||
- parameter options: The flags indicating which values to include in the change dictionary.
|
||||
- parameter: changeHandler: The closure called when the value is updated.
|
||||
*/
|
||||
public func observe(options: NSKeyValueObservingOptions = [], changeHandler: @escaping (O, CoreStoreObjectObjectDiff<D>) -> Void) -> CoreStoreObjectKeyValueObservation {
|
||||
|
||||
let result = _CoreStoreObjectKeyValueObservation(
|
||||
@@ -120,6 +150,12 @@ extension RelationshipContainer.ToOne {
|
||||
|
||||
extension RelationshipContainer.ToManyUnordered {
|
||||
|
||||
/**
|
||||
Observes changes in the receiver value. When the returned `CoreStoreObjectKeyValueObservation` is deinited or invalidated, it will stop observing.
|
||||
|
||||
- parameter options: The flags indicating which values to include in the change dictionary.
|
||||
- parameter: changeHandler: The closure called when the value is updated.
|
||||
*/
|
||||
public func observe(options: NSKeyValueObservingOptions = [], changeHandler: @escaping (O, CoreStoreObjectUnorderedDiff<D>) -> Void) -> CoreStoreObjectKeyValueObservation {
|
||||
|
||||
let result = _CoreStoreObjectKeyValueObservation(
|
||||
@@ -149,6 +185,12 @@ extension RelationshipContainer.ToManyUnordered {
|
||||
|
||||
extension RelationshipContainer.ToManyOrdered {
|
||||
|
||||
/**
|
||||
Observes changes in the receiver value. When the returned `CoreStoreObjectKeyValueObservation` is deinited or invalidated, it will stop observing.
|
||||
|
||||
- parameter options: The flags indicating which values to include in the change dictionary.
|
||||
- parameter: changeHandler: The closure called when the value is updated.
|
||||
*/
|
||||
public func observe(options: NSKeyValueObservingOptions = [], changeHandler: @escaping (O, CoreStoreObjectOrderedDiff<D>) -> Void) -> CoreStoreObjectKeyValueObservation {
|
||||
|
||||
let result = _CoreStoreObjectKeyValueObservation(
|
||||
@@ -177,6 +219,9 @@ extension RelationshipContainer.ToManyOrdered {
|
||||
|
||||
// MARK: - CoreStoreObjectValueDiff
|
||||
|
||||
/**
|
||||
The object containing the changeset for an observed `ValueContainer.Required` and `ValueContainer.Optional` property.
|
||||
*/
|
||||
public final class CoreStoreObjectValueDiff<V: ImportableAttributeType> {
|
||||
|
||||
/**
|
||||
@@ -185,12 +230,12 @@ public final class CoreStoreObjectValueDiff<V: ImportableAttributeType> {
|
||||
public let kind: NSKeyValueChange
|
||||
|
||||
/**
|
||||
`newValue` and `oldValue` will only be non-nil if `.new`/`.old` is passed to `observe()`. In general, get the most up to date value by accessing it directly on the observed object instead.
|
||||
`newValue` and `oldValue` will only be non-nil if `.new`/`.old` is passed to `observe()`. `newValue` will be `nil` if `isPrior` is `true`.
|
||||
*/
|
||||
public private(set) lazy var newValue: V? = self.newNativeValue.flatMap(V.cs_fromQueryableNativeType)
|
||||
|
||||
/**
|
||||
`newValue` and `oldValue` will only be non-nil if `.new`/`.old` is passed to `observe()`. In general, get the most up to date value by accessing it directly on the observed object instead.
|
||||
`newValue` and `oldValue` will only be non-nil if `.new`/`.old` is passed to `observe()`.
|
||||
*/
|
||||
public private(set) lazy var oldValue: V? = self.oldNativeValue.flatMap(V.cs_fromQueryableNativeType)
|
||||
|
||||
@@ -220,6 +265,9 @@ public final class CoreStoreObjectValueDiff<V: ImportableAttributeType> {
|
||||
|
||||
// MARK: - CoreStoreObjectValueDiff
|
||||
|
||||
/**
|
||||
The object containing the changeset for an observed `TransformableContainer.Required` or `TransformableContainer.Optional` property.
|
||||
*/
|
||||
public final class CoreStoreObjectTransformableDiff<V: NSCoding & NSCopying> {
|
||||
|
||||
/**
|
||||
@@ -257,6 +305,9 @@ public final class CoreStoreObjectTransformableDiff<V: NSCoding & NSCopying> {
|
||||
|
||||
// MARK: - CoreStoreObjectObjectDiff
|
||||
|
||||
/**
|
||||
The object containing the changeset for an observed `RelationshipContainer.ToOne` property.
|
||||
*/
|
||||
public final class CoreStoreObjectObjectDiff<D: CoreStoreObject> {
|
||||
|
||||
/**
|
||||
@@ -300,6 +351,9 @@ public final class CoreStoreObjectObjectDiff<D: CoreStoreObject> {
|
||||
|
||||
// MARK: - CoreStoreObjectUnorderedDiff
|
||||
|
||||
/**
|
||||
The object containing the changeset for an observed `RelationshipContainer.ToManyUnordered` property.
|
||||
*/
|
||||
public final class CoreStoreObjectUnorderedDiff<D: CoreStoreObject> {
|
||||
|
||||
/**
|
||||
@@ -343,6 +397,9 @@ public final class CoreStoreObjectUnorderedDiff<D: CoreStoreObject> {
|
||||
|
||||
// MARK: - CoreStoreObjectOrderedDiff
|
||||
|
||||
/**
|
||||
The object containing the changeset for an observed `RelationshipContainer.Ordered` property.
|
||||
*/
|
||||
public final class CoreStoreObjectOrderedDiff<D: CoreStoreObject> {
|
||||
|
||||
/**
|
||||
@@ -451,6 +508,7 @@ fileprivate final class _CoreStoreObjectKeyValueObservation: NSObject, CoreStore
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
@nonobjc
|
||||
fileprivate init(object: CoreStoreManagedObject, keyPath: KeyPathString, callback: @escaping (_ object: CoreStoreManagedObject, _ kind: NSKeyValueChange, _ newValue: Any?, _ oldValue: Any?, _ indexes: IndexSet?, _ isPrior: Bool) -> Void) {
|
||||
|
||||
let _ = _CoreStoreObjectKeyValueObservation.swizzler
|
||||
@@ -459,6 +517,7 @@ fileprivate final class _CoreStoreObjectKeyValueObservation: NSObject, CoreStore
|
||||
self.callback = callback
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
fileprivate func start(_ options: NSKeyValueObservingOptions) {
|
||||
|
||||
self.object?.addObserver(self, forKeyPath: self.keyPath, options: options, context: nil)
|
||||
@@ -472,6 +531,7 @@ fileprivate final class _CoreStoreObjectKeyValueObservation: NSObject, CoreStore
|
||||
|
||||
// MARK: DynamicObjectKeyValueObservation
|
||||
|
||||
@nonobjc
|
||||
public func invalidate() {
|
||||
|
||||
self.object?.removeObserver(self, forKeyPath: self.keyPath, context: nil)
|
||||
@@ -482,7 +542,8 @@ fileprivate final class _CoreStoreObjectKeyValueObservation: NSObject, CoreStore
|
||||
// MARK: Private
|
||||
|
||||
// workaround for <rdar://problem/31640524> Erroneous (?) error when using bridging in the Foundation overlay
|
||||
@nonobjc static var swizzler: Any? = cs_lazy {
|
||||
@nonobjc
|
||||
static var swizzler: Any? = cs_lazy {
|
||||
|
||||
let bridgeClass: AnyClass = _CoreStoreObjectKeyValueObservation.self
|
||||
let rootObserveImpl = class_getInstanceMethod(
|
||||
@@ -497,11 +558,17 @@ fileprivate final class _CoreStoreObjectKeyValueObservation: NSObject, CoreStore
|
||||
return nil
|
||||
}
|
||||
|
||||
@nonobjc private weak var object: CoreStoreManagedObject?
|
||||
@nonobjc private let callback: (_ object: CoreStoreManagedObject, _ kind: NSKeyValueChange, _ newValue: Any?, _ oldValue: Any?, _ indexes: IndexSet?, _ isPrior: Bool) -> Void
|
||||
@nonobjc private let keyPath: KeyPathString
|
||||
@nonobjc
|
||||
private weak var object: CoreStoreManagedObject?
|
||||
|
||||
@objc private dynamic func _cs_swizzle_me_observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSString: Any]?, context: UnsafeMutableRawPointer?) {
|
||||
@nonobjc
|
||||
private let callback: (_ object: CoreStoreManagedObject, _ kind: NSKeyValueChange, _ newValue: Any?, _ oldValue: Any?, _ indexes: IndexSet?, _ isPrior: Bool) -> Void
|
||||
|
||||
@nonobjc
|
||||
private let keyPath: KeyPathString
|
||||
|
||||
@objc
|
||||
private dynamic func _cs_swizzle_me_observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSString: Any]?, context: UnsafeMutableRawPointer?) {
|
||||
|
||||
guard
|
||||
let object = object as? CoreStoreManagedObject,
|
||||
|
||||
@@ -86,10 +86,6 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
self.deletedSections = []
|
||||
self.insertedSections = []
|
||||
|
||||
self.handler?.controllerWillChangeContent(controller)
|
||||
}
|
||||
|
||||
@@ -104,7 +100,6 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
self.handler?.controllerDidChangeContent(controller)
|
||||
}
|
||||
|
||||
@@ -115,98 +110,11 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult
|
||||
|
||||
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 #available(iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
|
||||
|
||||
// I don't know if iOS 10 even attempted to fix this mess...
|
||||
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
|
||||
}
|
||||
guard #available(iOS 9.0, tvOS 9.0, watchOS 3.0, *) else {
|
||||
|
||||
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: actualType,
|
||||
forChangeType: type,
|
||||
newIndexPath: newIndexPath
|
||||
)
|
||||
}
|
||||
@@ -218,14 +126,6 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch type {
|
||||
|
||||
case .delete: self.deletedSections.insert(sectionIndex)
|
||||
case .insert: self.insertedSections.insert(sectionIndex)
|
||||
default: break
|
||||
}
|
||||
|
||||
self.handler?.controller(
|
||||
controller,
|
||||
didChangeSection: sectionInfo,
|
||||
@@ -242,13 +142,4 @@ internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResult
|
||||
sectionIndexTitleForSectionName: sectionName
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@nonobjc
|
||||
private var deletedSections = Set<Int>()
|
||||
|
||||
@nonobjc
|
||||
private var insertedSections = Set<Int>()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user