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, guard let `self` = self,
let userInfo = note.userInfo, 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 return
} }
callback( callback(
self, self,
object, ObjectType.cs_fromRaw(object: rawObject),
userInfo[String(describing: IndexPath.self)] as? IndexPath, userInfo[String(describing: IndexPath.self)] as? IndexPath,
userInfo["\(String(describing: IndexPath.self)).New"] as? IndexPath userInfo["\(String(describing: IndexPath.self)).New"] as? IndexPath
) )

View File

@@ -160,10 +160,9 @@ public enum ValueContainer<O: CoreStoreObject> {
object, object,
{ () -> V in { () -> V in
return object.rawObject!.getValue( return V.cs_fromImportableNativeType(
forKvcKey: self.keyPath, object.rawObject!.value(forKey: self.keyPath)! as! V.ImportableNativeType
didGetValue: { V.cs_fromImportableNativeType($0 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." "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
) )
CoreStore.assert( 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." "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
) )
let customSetter = (self.customSetter ?? { $1($2) }) let customSetter = (self.customSetter ?? { $1($2) })
@@ -190,9 +189,8 @@ public enum ValueContainer<O: CoreStoreObject> {
{ (newValue: V) -> Void in { (newValue: V) -> Void in
object.rawObject!.setValue( object.rawObject!.setValue(
newValue, newValue.cs_toImportableNativeType(),
forKvcKey: self.keyPath, forKey: self.keyPath
willSetValue: { $0.cs_toImportableNativeType() }
) )
}, },
newValue 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 // MARK: AttributeProtocol
@@ -363,10 +407,8 @@ public enum ValueContainer<O: CoreStoreObject> {
object, object,
{ () -> V? in { () -> V? in
return object.rawObject!.getValue( return (object.rawObject!.value(forKey: self.keyPath) as! V.ImportableNativeType?)
forKvcKey: self.keyPath, .flatMap(V.cs_fromImportableNativeType)
didGetValue: { ($0 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." "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
) )
CoreStore.assert( 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." "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
) )
let customSetter = (self.customSetter ?? { $1($2) }) let customSetter = (self.customSetter ?? { $1($2) })
@@ -393,9 +435,8 @@ public enum ValueContainer<O: CoreStoreObject> {
{ (newValue: V?) -> Void in { (newValue: V?) -> Void in
object.rawObject!.setValue( object.rawObject!.setValue(
newValue, newValue?.cs_toImportableNativeType(),
forKvcKey: self.keyPath, forKey: self.keyPath
willSetValue: { $0?.cs_toImportableNativeType() }
) )
}, },
newValue 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 // MARK: AttributeProtocol
@@ -621,10 +707,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
object, object,
{ () -> V in { () -> V in
return object.rawObject!.getValue( return object.rawObject!.value(forKey: self.keyPath)! as! V
forKvcKey: self.keyPath,
didGetValue: { $0 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." "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
) )
CoreStore.assert( 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." "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
) )
let customSetter = (self.customSetter ?? { $1($2) }) let customSetter = (self.customSetter ?? { $1($2) })
@@ -652,7 +735,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
object.rawObject!.setValue( object.rawObject!.setValue(
newValue, newValue,
forKvcKey: self.keyPath forKey: self.keyPath
) )
}, },
newValue 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 // MARK: AttributeProtocol
@@ -807,10 +934,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
object, object,
{ () -> V? in { () -> V? in
return object.rawObject!.getValue( object.rawObject!.value(forKey: self.keyPath) as! V?
forKvcKey: self.keyPath,
didGetValue: { $0 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." "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
) )
CoreStore.assert( 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." "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
) )
let customSetter = (self.customSetter ?? { $1($2) }) let customSetter = (self.customSetter ?? { $1($2) })
@@ -838,7 +962,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
object.rawObject!.setValue( object.rawObject!.setValue(
newValue, newValue,
forKvcKey: self.keyPath forKey: self.keyPath
) )
}, },
newValue 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 // MARK: AttributeProtocol