Field.Coded implementations for transformable attributes

This commit is contained in:
John Estropia
2020-01-18 16:22:06 +09:00
parent 43f61359da
commit bcc2d9def3
22 changed files with 1505 additions and 101 deletions

View File

@@ -81,7 +81,7 @@ extension FieldContainer {
- parameter initial: the initial value for the property when the object is first create
- parameter keyPath: the permanent attribute name for this property.
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.primitiveValue(for:)` instead of `PartialObject<O>.value(for:)`, which would unintentionally execute the same closure again recursively.
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.setPrimitiveValue(_:for:)` instead of `PartialObject<O>.setValue(_:for:)`, which would unintentionally execute the same closure again recursively.
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
@@ -90,17 +90,18 @@ extension FieldContainer {
wrappedValue initial: @autoclosure @escaping () -> V,
_ keyPath: KeyPathString,
versionHashModifier: @autoclosure @escaping () -> String? = nil,
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
) {
self.init(
wrappedValue: initial,
keyPath: keyPath,
isOptional: false,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
renamingIdentifier: previousVersionKeyPath,
customGetter: customGetter,
customSetter: customSetter,
affectedByKeyPaths: affectedByKeyPaths
@@ -185,11 +186,14 @@ extension FieldContainer {
return customGetter(PartialObject<O>(rawObject))
}
let keyPath = field.keyPath
guard case let rawValue as V.FieldStoredNativeType = rawObject.value(forKey: keyPath) else {
switch rawObject.value(forKey: keyPath) {
case let rawValue as V.FieldStoredNativeType:
return V.cs_fromFieldStoredNativeType(rawValue)
default:
return nil
}
return V.cs_fromFieldStoredNativeType(rawValue)
}
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) {
@@ -284,6 +288,7 @@ extension FieldContainer {
allowsExternalBinaryDataStorage: false,
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
valueTransformer: nil,
affectedByKeyPaths: affectedByKeyPaths(),
defaultValue: initial().cs_toFieldStoredNativeType()
)
@@ -334,7 +339,7 @@ extension FieldContainer.Stored where V: FieldOptionalType {
- parameter initial: the initial value for the property when the object is first create
- parameter keyPath: the permanent attribute name for this property.
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.primitiveValue(for:)` instead of `PartialObject<O>.value(for:)`, which would unintentionally execute the same closure again recursively.
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.setPrimitiveValue(_:for:)` instead of `PartialObject<O>.setValue(_:for:)`, which would unintentionally execute the same closure again recursively.
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
@@ -343,7 +348,7 @@ extension FieldContainer.Stored where V: FieldOptionalType {
wrappedValue initial: @autoclosure @escaping () -> V = nil,
_ keyPath: KeyPathString,
versionHashModifier: @autoclosure @escaping () -> String? = nil,
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
@@ -353,7 +358,7 @@ extension FieldContainer.Stored where V: FieldOptionalType {
keyPath: keyPath,
isOptional: true,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
renamingIdentifier: previousVersionKeyPath,
customGetter: customGetter,
customSetter: customSetter,
affectedByKeyPaths: affectedByKeyPaths