Fixed ListMonitor bug for CoreStoreObjects where ListObservers don't get update notifications

This commit is contained in:
John Estropia
2017-06-14 17:37:46 +09:00
parent 8c437e19b8
commit 801cf8d9f0
2 changed files with 198 additions and 30 deletions

View File

@@ -704,13 +704,13 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
guard let `self` = self,
let userInfo = note.userInfo,
let object = userInfo[String(describing: NSManagedObject.self)] as? ObjectType else {
let rawObject = userInfo[String(describing: NSManagedObject.self)] as? NSManagedObject else {
return
}
callback(
self,
object,
ObjectType.cs_fromRaw(object: rawObject),
userInfo[String(describing: IndexPath.self)] as? IndexPath,
userInfo["\(String(describing: IndexPath.self)).New"] as? IndexPath
)

View File

@@ -160,10 +160,9 @@ public enum ValueContainer<O: CoreStoreObject> {
object,
{ () -> V in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { V.cs_fromImportableNativeType($0 as! V.ImportableNativeType)! }
)
return V.cs_fromImportableNativeType(
object.rawObject!.value(forKey: self.keyPath)! as! V.ImportableNativeType
)!
}
)
}
@@ -181,7 +180,7 @@ public enum ValueContainer<O: CoreStoreObject> {
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.isTransient || object.rawObject!.isEditableInContext() == true,
object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
let customSetter = (self.customSetter ?? { $1($2) })
@@ -190,9 +189,8 @@ public enum ValueContainer<O: CoreStoreObject> {
{ (newValue: V) -> Void in
object.rawObject!.setValue(
newValue,
forKvcKey: self.keyPath,
willSetValue: { $0.cs_toImportableNativeType() }
newValue.cs_toImportableNativeType(),
forKey: self.keyPath
)
},
newValue
@@ -201,6 +199,52 @@ public enum ValueContainer<O: CoreStoreObject> {
}
}
/**
The primitive value. Compared to `value`, `primitiveValue` bypasses all notification mechanisms. This is typically only used for setting values for transient properties.
*/
public var primitiveValue: V {
get {
CoreStore.assert(
self.parentObject != nil,
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
)
return V.cs_fromImportableNativeType(
object.rawObject!.primitiveValue(forKey: self.keyPath)! as! V.ImportableNativeType
)!
}
}
set {
CoreStore.assert(
self.parentObject != nil,
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
)
CoreStore.assert(
self.isTransient || object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s primitive value from outside a transaction."
)
object.rawObject!.setPrimitiveValue(
newValue.cs_toImportableNativeType(),
forKey: self.keyPath
)
}
}
}
// MARK: AttributeProtocol
@@ -363,10 +407,8 @@ public enum ValueContainer<O: CoreStoreObject> {
object,
{ () -> V? in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { ($0 as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) }
)
return (object.rawObject!.value(forKey: self.keyPath) as! V.ImportableNativeType?)
.flatMap(V.cs_fromImportableNativeType)
}
)
}
@@ -384,7 +426,7 @@ public enum ValueContainer<O: CoreStoreObject> {
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.isTransient || object.rawObject!.isEditableInContext() == true,
object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
let customSetter = (self.customSetter ?? { $1($2) })
@@ -393,9 +435,8 @@ public enum ValueContainer<O: CoreStoreObject> {
{ (newValue: V?) -> Void in
object.rawObject!.setValue(
newValue,
forKvcKey: self.keyPath,
willSetValue: { $0?.cs_toImportableNativeType() }
newValue?.cs_toImportableNativeType(),
forKey: self.keyPath
)
},
newValue
@@ -404,6 +445,51 @@ public enum ValueContainer<O: CoreStoreObject> {
}
}
/**
The primitive value. Compared to `value`, `primitiveValue` bypasses all notification mechanisms. This is typically only used for setting values for transient properties.
*/
public var primitiveValue: V? {
get {
CoreStore.assert(
self.parentObject != nil,
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
)
return (object.rawObject!.primitiveValue(forKey: self.keyPath) as! V.ImportableNativeType?)
.flatMap(V.cs_fromImportableNativeType)
}
}
set {
CoreStore.assert(
self.parentObject != nil,
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
)
CoreStore.assert(
self.isTransient || object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s primitive value from outside a transaction."
)
object.rawObject!.setPrimitiveValue(
newValue?.cs_toImportableNativeType(),
forKey: self.keyPath
)
}
}
}
// MARK: AttributeProtocol
@@ -621,10 +707,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
object,
{ () -> V in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { $0 as! V }
)
return object.rawObject!.value(forKey: self.keyPath)! as! V
}
)
}
@@ -642,7 +725,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.isTransient || object.rawObject!.isEditableInContext() == true,
object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
let customSetter = (self.customSetter ?? { $1($2) })
@@ -652,7 +735,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
object.rawObject!.setValue(
newValue,
forKvcKey: self.keyPath
forKey: self.keyPath
)
},
newValue
@@ -661,6 +744,50 @@ public enum TransformableContainer<O: CoreStoreObject> {
}
}
/**
The primitive value. Compared to `value`, `primitiveValue` bypasses all notification mechanisms. This is typically only used for setting values for transient properties.
*/
public var primitiveValue: V {
get {
CoreStore.assert(
self.parentObject != nil,
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
)
return object.rawObject!.primitiveValue(forKey: self.keyPath)! as! V
}
}
set {
CoreStore.assert(
self.parentObject != nil,
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
)
CoreStore.assert(
self.isTransient || object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s primitive value from outside a transaction."
)
object.rawObject!.setPrimitiveValue(
newValue,
forKey: self.keyPath
)
}
}
}
// MARK: AttributeProtocol
@@ -807,10 +934,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
object,
{ () -> V? in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { $0 as! V? }
)
object.rawObject!.value(forKey: self.keyPath) as! V?
}
)
}
@@ -828,7 +952,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.isTransient || object.rawObject!.isEditableInContext() == true,
object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
let customSetter = (self.customSetter ?? { $1($2) })
@@ -838,7 +962,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
object.rawObject!.setValue(
newValue,
forKvcKey: self.keyPath
forKey: self.keyPath
)
},
newValue
@@ -847,6 +971,50 @@ public enum TransformableContainer<O: CoreStoreObject> {
}
}
/**
The primitive value. Compared to `value`, `primitiveValue` bypasses all notification mechanisms. This is typically only used for setting values for transient properties.
*/
public var primitiveValue: V? {
get {
CoreStore.assert(
self.parentObject != nil,
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
)
return object.rawObject!.primitiveValue(forKey: self.keyPath) as! V?
}
}
set {
CoreStore.assert(
self.parentObject != nil,
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
)
CoreStore.assert(
self.isTransient || object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s primitive value from outside a transaction."
)
object.rawObject!.setPrimitiveValue(
newValue,
forKey: self.keyPath
)
}
}
}
// MARK: AttributeProtocol