mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-27 20:01:27 +01:00
improved API for custom getters and setters in Field properties
This commit is contained in:
@@ -189,6 +189,10 @@
|
|||||||
B50E17622351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
B50E17622351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
||||||
B50E17632351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
B50E17632351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
||||||
B50E17642351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
B50E17642351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
||||||
|
B50E42F723FBB91800ED476E /* ObjectProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E42F623FBB91800ED476E /* ObjectProxy.swift */; };
|
||||||
|
B50E42F823FBB91800ED476E /* ObjectProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E42F623FBB91800ED476E /* ObjectProxy.swift */; };
|
||||||
|
B50E42F923FBB91800ED476E /* ObjectProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E42F623FBB91800ED476E /* ObjectProxy.swift */; };
|
||||||
|
B50E42FA23FBB91800ED476E /* ObjectProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E42F623FBB91800ED476E /* ObjectProxy.swift */; };
|
||||||
B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
||||||
B50EE14323473C96009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
B50EE14323473C96009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
||||||
B50EE14423473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
B50EE14423473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
||||||
@@ -1025,6 +1029,7 @@
|
|||||||
B50E175623517DE4004F033C /* Differentiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Differentiable.swift; sourceTree = "<group>"; };
|
B50E175623517DE4004F033C /* Differentiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Differentiable.swift; sourceTree = "<group>"; };
|
||||||
B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.DiffResult.swift; sourceTree = "<group>"; };
|
B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.DiffResult.swift; sourceTree = "<group>"; };
|
||||||
B50E17602351FA66004F033C /* Internals.Closure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.Closure.swift; sourceTree = "<group>"; };
|
B50E17602351FA66004F033C /* Internals.Closure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.Closure.swift; sourceTree = "<group>"; };
|
||||||
|
B50E42F623FBB91800ED476E /* ObjectProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectProxy.swift; sourceTree = "<group>"; };
|
||||||
B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+DataSources.swift"; sourceTree = "<group>"; };
|
B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+DataSources.swift"; sourceTree = "<group>"; };
|
||||||
B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+Convenience.swift"; sourceTree = "<group>"; };
|
B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+Convenience.swift"; sourceTree = "<group>"; };
|
||||||
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEntityDescription+DynamicModel.swift"; sourceTree = "<group>"; };
|
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEntityDescription+DynamicModel.swift"; sourceTree = "<group>"; };
|
||||||
@@ -1646,6 +1651,7 @@
|
|||||||
children = (
|
children = (
|
||||||
B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */,
|
B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */,
|
||||||
B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */,
|
B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */,
|
||||||
|
B50E42F623FBB91800ED476E /* ObjectProxy.swift */,
|
||||||
B56E4EC823CD9B2E00E1708C /* Field Properties */,
|
B56E4EC823CD9B2E00E1708C /* Field Properties */,
|
||||||
B5831B6E1F3355C300A9F647 /* Legacy Properties */,
|
B5831B6E1F3355C300A9F647 /* Legacy Properties */,
|
||||||
B52F74391E9B8724005F3DAC /* Dynamic Schema */,
|
B52F74391E9B8724005F3DAC /* Dynamic Schema */,
|
||||||
@@ -2381,6 +2387,7 @@
|
|||||||
B5E84F411AFF8CCD0064E85B /* TypeErasedClauses.swift in Sources */,
|
B5E84F411AFF8CCD0064E85B /* TypeErasedClauses.swift in Sources */,
|
||||||
B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */,
|
B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */,
|
||||||
B52F74451E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */,
|
B52F74451E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */,
|
||||||
|
B50E42F723FBB91800ED476E /* ObjectProxy.swift in Sources */,
|
||||||
B5FAD6AC1B51285300714891 /* Internals.MigrationManager.swift in Sources */,
|
B5FAD6AC1B51285300714891 /* Internals.MigrationManager.swift in Sources */,
|
||||||
B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */,
|
B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */,
|
||||||
B50C3EE023D062C300B29880 /* FieldCoderType.swift in Sources */,
|
B50C3EE023D062C300B29880 /* FieldCoderType.swift in Sources */,
|
||||||
@@ -2632,6 +2639,7 @@
|
|||||||
B59851491C90289D00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
B59851491C90289D00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||||
B5E1B5A41CAA4365007FD580 /* CSCoreStore+Observing.swift in Sources */,
|
B5E1B5A41CAA4365007FD580 /* CSCoreStore+Observing.swift in Sources */,
|
||||||
B596BBB71DD5BC67001DCDD9 /* FetchableSource.swift in Sources */,
|
B596BBB71DD5BC67001DCDD9 /* FetchableSource.swift in Sources */,
|
||||||
|
B50E42F823FBB91800ED476E /* ObjectProxy.swift in Sources */,
|
||||||
B5FEC18F1C9166E600532541 /* NSPersistentStore+Setup.swift in Sources */,
|
B5FEC18F1C9166E600532541 /* NSPersistentStore+Setup.swift in Sources */,
|
||||||
82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */,
|
82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */,
|
||||||
B50C3EE123D062C300B29880 /* FieldCoderType.swift in Sources */,
|
B50C3EE123D062C300B29880 /* FieldCoderType.swift in Sources */,
|
||||||
@@ -2883,6 +2891,7 @@
|
|||||||
B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */,
|
B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */,
|
||||||
B5E222271CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */,
|
B5E222271CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */,
|
||||||
B52F74481E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */,
|
B52F74481E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */,
|
||||||
|
B50E42FA23FBB91800ED476E /* ObjectProxy.swift in Sources */,
|
||||||
B5519A621CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */,
|
B5519A621CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */,
|
||||||
B52DD19C1BE1F92C00949AFE /* Into.swift in Sources */,
|
B52DD19C1BE1F92C00949AFE /* Into.swift in Sources */,
|
||||||
B50C3EE323D062C300B29880 /* FieldCoderType.swift in Sources */,
|
B50C3EE323D062C300B29880 /* FieldCoderType.swift in Sources */,
|
||||||
@@ -3134,6 +3143,7 @@
|
|||||||
B52F74471E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */,
|
B52F74471E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */,
|
||||||
B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */,
|
B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */,
|
||||||
B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */,
|
B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */,
|
||||||
|
B50E42F923FBB91800ED476E /* ObjectProxy.swift in Sources */,
|
||||||
B5E1B5A51CAA4365007FD580 /* CSCoreStore+Observing.swift in Sources */,
|
B5E1B5A51CAA4365007FD580 /* CSCoreStore+Observing.swift in Sources */,
|
||||||
B596BBB81DD5BC67001DCDD9 /* FetchableSource.swift in Sources */,
|
B596BBB81DD5BC67001DCDD9 /* FetchableSource.swift in Sources */,
|
||||||
B50C3EE223D062C300B29880 /* FieldCoderType.swift in Sources */,
|
B50C3EE223D062C300B29880 /* FieldCoderType.swift in Sources */,
|
||||||
|
|||||||
@@ -94,22 +94,43 @@ enum Job: String {
|
|||||||
|
|
||||||
class Person: CoreStoreObject {
|
class Person: CoreStoreObject {
|
||||||
|
|
||||||
@Field.Stored("title", customSetter: Person.setTitle(_:_:))
|
@Field.Stored(
|
||||||
|
"title",
|
||||||
|
customSetter: { (object, field, newValue) in
|
||||||
|
field.primitiveValue = newValue
|
||||||
|
object.$displayName.primitiveValue = nil
|
||||||
|
}
|
||||||
|
)
|
||||||
var title: String = "Mr."
|
var title: String = "Mr."
|
||||||
|
|
||||||
@Field.Stored("name", customSetter: Person.setName(_:_:))
|
@Field.Stored(
|
||||||
|
"name",
|
||||||
|
customSetter: { (object, field, newValue) in
|
||||||
|
field.primitiveValue = newValue
|
||||||
|
object.$displayName.primitiveValue = nil
|
||||||
|
}
|
||||||
|
)
|
||||||
var name: String = ""
|
var name: String = ""
|
||||||
|
|
||||||
@Field.Virtual(
|
@Field.Virtual(
|
||||||
"displayName",
|
"displayName",
|
||||||
customGetter: Person.getDisplayName(_:),
|
customGetter: Person.getDisplayName(_:_:),
|
||||||
affectedByKeyPaths: Person.keyPathsAffectingDisplayName()
|
affectedByKeyPaths: Person.keyPathsAffectingDisplayName()
|
||||||
)
|
)
|
||||||
var displayName: String?
|
var displayName: String?
|
||||||
|
|
||||||
@Field.Virtual(
|
@Field.Virtual(
|
||||||
"customType",
|
"customType",
|
||||||
customGetter: Person.getCustomField(_:)
|
customGetter: { (object, field) in
|
||||||
|
|
||||||
|
if let value = field.primitiveValue {
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
let value = CustomType()
|
||||||
|
field.primitiveValue = value
|
||||||
|
return value
|
||||||
|
}
|
||||||
)
|
)
|
||||||
var customField: CustomType
|
var customField: CustomType
|
||||||
|
|
||||||
@@ -131,40 +152,17 @@ class Person: CoreStoreObject {
|
|||||||
@Field.Relationship("_spouseInverse", inverse: \.$spouse)
|
@Field.Relationship("_spouseInverse", inverse: \.$spouse)
|
||||||
private var spouseInverse: Person?
|
private var spouseInverse: Person?
|
||||||
|
|
||||||
private static func setTitle(_ partialObject: PartialObject<Person>, _ newValue: String) {
|
static func getDisplayName(_ object: ObjectProxy<Person>, _ field: ObjectProxy<Person>.FieldProxy<String?>) -> String? {
|
||||||
|
|
||||||
partialObject.setPrimitiveValue(newValue, for: \.$title)
|
|
||||||
partialObject.setPrimitiveValue(nil, for: \.$displayName)
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func setName(_ partialObject: PartialObject<Person>, _ newValue: String) {
|
|
||||||
|
|
||||||
partialObject.setPrimitiveValue(newValue, for: \.$name)
|
|
||||||
partialObject.setPrimitiveValue(nil, for: \.$displayName)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func getCustomField(_ partialObject: PartialObject<Person>) -> CustomType {
|
if let value = field.primitiveValue {
|
||||||
|
|
||||||
if let customField = partialObject.primitiveValue(for: \.$customField) {
|
return value
|
||||||
|
|
||||||
return customField
|
|
||||||
}
|
}
|
||||||
let customField = CustomType()
|
let title = object.$title.value
|
||||||
partialObject.setPrimitiveValue(customField, for: \.$customField)
|
let name = object.$name.value
|
||||||
return customField
|
let value = "\(title) \(name)"
|
||||||
}
|
field.primitiveValue = value
|
||||||
|
return value
|
||||||
static func getDisplayName(_ partialObject: PartialObject<Person>) -> String? {
|
|
||||||
|
|
||||||
if let displayName = partialObject.primitiveValue(for: \.$displayName) {
|
|
||||||
|
|
||||||
return displayName
|
|
||||||
}
|
|
||||||
let title = partialObject.value(for: \.$title)
|
|
||||||
let name = partialObject.value(for: \.$name)
|
|
||||||
let displayName = "\(title) \(name)"
|
|
||||||
partialObject.setPrimitiveValue(displayName, for: \.$displayName)
|
|
||||||
return displayName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func keyPathsAffectingDisplayName() -> Set<String> {
|
static func keyPathsAffectingDisplayName() -> Set<String> {
|
||||||
|
|||||||
@@ -65,15 +65,15 @@ extension FieldContainer {
|
|||||||
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
||||||
var displayName: String = ""
|
var displayName: String = ""
|
||||||
|
|
||||||
private static func getName(_ partialObject: PartialObject<Person>) -> String {
|
private static func getName(_ object: ObjectProxy<Person>) -> String {
|
||||||
let cachedDisplayName = partialObject.primitiveValue(for: \.$displayName)
|
let cachedDisplayName = object.primitiveValue(for: \.$displayName)
|
||||||
if !cachedDisplayName.isEmpty {
|
if !cachedDisplayName.isEmpty {
|
||||||
return cachedDisplayName
|
return cachedDisplayName
|
||||||
}
|
}
|
||||||
let title = partialObject.value(for: \.$title)
|
let title = object.value(for: \.$title)
|
||||||
let name = partialObject.value(for: \.$name)
|
let name = object.value(for: \.$name)
|
||||||
let displayName = "\(title) \(name)"
|
let displayName = "\(title) \(name)"
|
||||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
object.setPrimitiveValue(displayName, for: { $0.displayName })
|
||||||
return displayName
|
return displayName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,8 +82,8 @@ extension FieldContainer {
|
|||||||
- parameter keyPath: the permanent attribute name for this property.
|
- 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 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 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 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 customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.primitiveValue(for:)` instead of `ObjectProxy<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 customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.$property.primitiveValue` instead of `ObjectProxy<O>.$property.value`, 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:)`.
|
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||||
*/
|
*/
|
||||||
public init<Coder: FieldCoderType>(
|
public init<Coder: FieldCoderType>(
|
||||||
@@ -92,8 +92,8 @@ extension FieldContainer {
|
|||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
coder fieldCoderType: Coder.Type,
|
coder fieldCoderType: Coder.Type,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||||
) where Coder.FieldStoredValue == V {
|
) where Coder.FieldStoredValue == V {
|
||||||
|
|
||||||
@@ -116,8 +116,8 @@ extension FieldContainer {
|
|||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
coder: (encode: (V) -> Data?, decode: (Data?) -> V),
|
coder: (encode: (V) -> Data?, decode: (Data?) -> V),
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -209,7 +209,10 @@ extension FieldContainer {
|
|||||||
let field = field as! Self
|
let field = field as! Self
|
||||||
if let customGetter = field.customGetter {
|
if let customGetter = field.customGetter {
|
||||||
|
|
||||||
return customGetter(PartialObject<O>(rawObject))
|
return customGetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: field)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let keyPath = field.keyPath
|
let keyPath = field.keyPath
|
||||||
switch rawObject.value(forKey: keyPath) {
|
switch rawObject.value(forKey: keyPath) {
|
||||||
@@ -237,7 +240,11 @@ extension FieldContainer {
|
|||||||
let keyPath = field.keyPath
|
let keyPath = field.keyPath
|
||||||
if let customSetter = field.customSetter {
|
if let customSetter = field.customSetter {
|
||||||
|
|
||||||
return customSetter(PartialObject<O>(rawObject), newValue)
|
return customSetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: field),
|
||||||
|
newValue
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return rawObject.setValue(newValue, forKey: keyPath)
|
return rawObject.setValue(newValue, forKey: keyPath)
|
||||||
}
|
}
|
||||||
@@ -279,7 +286,10 @@ extension FieldContainer {
|
|||||||
|
|
||||||
rawObject.didAccessValue(forKey: keyPath)
|
rawObject.didAccessValue(forKey: keyPath)
|
||||||
}
|
}
|
||||||
let value = customGetter(PartialObject<O>(rawObject))
|
let value = customGetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: self)
|
||||||
|
)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,7 +310,8 @@ extension FieldContainer {
|
|||||||
rawObject.didChangeValue(forKey: keyPath)
|
rawObject.didChangeValue(forKey: keyPath)
|
||||||
}
|
}
|
||||||
customSetter(
|
customSetter(
|
||||||
PartialObject<O>(rawObject),
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: self),
|
||||||
newValue as! V
|
newValue as! V
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -316,8 +327,8 @@ extension FieldContainer {
|
|||||||
versionHashModifier: @escaping () -> String?,
|
versionHashModifier: @escaping () -> String?,
|
||||||
renamingIdentifier: @escaping () -> String?,
|
renamingIdentifier: @escaping () -> String?,
|
||||||
valueTransformer: @escaping () -> Internals.AnyFieldCoder?,
|
valueTransformer: @escaping () -> Internals.AnyFieldCoder?,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)?,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? ,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? ,
|
||||||
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
@@ -346,8 +357,8 @@ extension FieldContainer {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: ((_ partialObject: PartialObject<O>) -> V)?
|
private let customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?
|
||||||
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)?
|
private let customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,15 +380,15 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
|||||||
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
||||||
var displayName: String = ""
|
var displayName: String = ""
|
||||||
|
|
||||||
private static func getName(_ partialObject: PartialObject<Person>) -> String {
|
private static func getName(_ object: ObjectProxy<Person>) -> String {
|
||||||
let cachedDisplayName = partialObject.primitiveValue(for: \.$displayName)
|
let cachedDisplayName = object.primitiveValue(for: \.$displayName)
|
||||||
if !cachedDisplayName.isEmpty {
|
if !cachedDisplayName.isEmpty {
|
||||||
return cachedDisplayName
|
return cachedDisplayName
|
||||||
}
|
}
|
||||||
let title = partialObject.value(for: \.$title)
|
let title = object.value(for: \.$title)
|
||||||
let name = partialObject.value(for: \.$name)
|
let name = object.value(for: \.$name)
|
||||||
let displayName = "\(title) \(name)"
|
let displayName = "\(title) \(name)"
|
||||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
object.setPrimitiveValue(displayName, for: { $0.displayName })
|
||||||
return displayName
|
return displayName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,8 +397,8 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
|||||||
- parameter keyPath: the permanent attribute name for this property.
|
- 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 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 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 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 customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.primitiveValue(for:)` instead of `ObjectProxy<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 customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.$property.primitiveValue` instead of `ObjectProxy<O>.$property.value`, 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:)`.
|
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||||
*/
|
*/
|
||||||
public init<Coder: FieldCoderType>(
|
public init<Coder: FieldCoderType>(
|
||||||
@@ -396,8 +407,8 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
|||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
coder: Coder.Type,
|
coder: Coder.Type,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||||
) where Coder.FieldStoredValue == V.Wrapped {
|
) where Coder.FieldStoredValue == V.Wrapped {
|
||||||
|
|
||||||
@@ -420,8 +431,8 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
|||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
coder: (encode: (V) -> Data?, decode: (Data?) -> V),
|
coder: (encode: (V) -> Data?, decode: (Data?) -> V),
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -457,15 +468,15 @@ extension FieldContainer.Coded where V: DefaultNSSecureCodable {
|
|||||||
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
||||||
var displayName: String = ""
|
var displayName: String = ""
|
||||||
|
|
||||||
private static func getName(_ partialObject: PartialObject<Person>) -> String {
|
private static func getName(_ object: ObjectProxy<Person>) -> String {
|
||||||
let cachedDisplayName = partialObject.primitiveValue(for: \.$displayName)
|
let cachedDisplayName = object.primitiveValue(for: \.$displayName)
|
||||||
if !cachedDisplayName.isEmpty {
|
if !cachedDisplayName.isEmpty {
|
||||||
return cachedDisplayName
|
return cachedDisplayName
|
||||||
}
|
}
|
||||||
let title = partialObject.value(for: \.$title)
|
let title = object.value(for: \.$title)
|
||||||
let name = partialObject.value(for: \.$name)
|
let name = object.value(for: \.$name)
|
||||||
let displayName = "\(title) \(name)"
|
let displayName = "\(title) \(name)"
|
||||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
object.setPrimitiveValue(displayName, for: { $0.displayName })
|
||||||
return displayName
|
return displayName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -474,8 +485,8 @@ extension FieldContainer.Coded where V: DefaultNSSecureCodable {
|
|||||||
- parameter keyPath: the permanent attribute name for this property.
|
- 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 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 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 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 customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.primitiveValue(for:)` instead of `ObjectProxy<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 customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.$property.primitiveValue` instead of `ObjectProxy<O>.$property.value`, 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:)`.
|
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||||
*/
|
*/
|
||||||
public init(
|
public init(
|
||||||
@@ -483,8 +494,8 @@ extension FieldContainer.Coded where V: DefaultNSSecureCodable {
|
|||||||
_ keyPath: KeyPathString,
|
_ keyPath: KeyPathString,
|
||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -512,8 +523,8 @@ extension FieldContainer.Coded where V: FieldOptionalType, V.Wrapped: DefaultNSS
|
|||||||
_ keyPath: KeyPathString,
|
_ keyPath: KeyPathString,
|
||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|||||||
@@ -62,28 +62,26 @@ extension FieldContainer {
|
|||||||
@Field.Stored("name")
|
@Field.Stored("name")
|
||||||
var name: String = ""
|
var name: String = ""
|
||||||
|
|
||||||
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
@Field.Virtual(
|
||||||
var displayName: String = ""
|
"displayName",
|
||||||
|
customGetter: { (object, field) in
|
||||||
private static func getName(_ partialObject: PartialObject<Person>) -> String {
|
if let cached = field.primitiveValue, !cached.isEmpty {
|
||||||
let cachedDisplayName = partialObject.primitiveValue(for: \.$displayName)
|
return cached
|
||||||
if !cachedDisplayName.isEmpty {
|
}
|
||||||
return cachedDisplayName
|
let value = "\(object.$title.value) \(object.$name.value)"
|
||||||
|
field.primitiveValue = value
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
let title = partialObject.value(for: \.$title)
|
)
|
||||||
let name = partialObject.value(for: \.$name)
|
var displayName: String = ""
|
||||||
let displayName = "\(title) \(name)"
|
|
||||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
|
||||||
return displayName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- parameter initial: the initial value for the property when the object is first create
|
- parameter initial: the initial value for the property when the object is first create
|
||||||
- parameter keyPath: the permanent attribute name for this property.
|
- 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 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 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 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 customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.primitiveValue(for:)` instead of `ObjectProxy<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 customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.$property.primitiveValue` instead of `ObjectProxy<O>.$property.value`, 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:)`.
|
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||||
*/
|
*/
|
||||||
public init(
|
public init(
|
||||||
@@ -91,8 +89,8 @@ extension FieldContainer {
|
|||||||
_ keyPath: KeyPathString,
|
_ keyPath: KeyPathString,
|
||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -183,7 +181,10 @@ extension FieldContainer {
|
|||||||
let field = field as! Self
|
let field = field as! Self
|
||||||
if let customGetter = field.customGetter {
|
if let customGetter = field.customGetter {
|
||||||
|
|
||||||
return customGetter(PartialObject<O>(rawObject))
|
return customGetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: field)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let keyPath = field.keyPath
|
let keyPath = field.keyPath
|
||||||
switch rawObject.value(forKey: keyPath) {
|
switch rawObject.value(forKey: keyPath) {
|
||||||
@@ -211,7 +212,11 @@ extension FieldContainer {
|
|||||||
let keyPath = field.keyPath
|
let keyPath = field.keyPath
|
||||||
if let customSetter = field.customSetter {
|
if let customSetter = field.customSetter {
|
||||||
|
|
||||||
return customSetter(PartialObject<O>(rawObject), newValue)
|
return customSetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: field),
|
||||||
|
newValue
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return rawObject.setValue(
|
return rawObject.setValue(
|
||||||
newValue.cs_toFieldStoredNativeType(),
|
newValue.cs_toFieldStoredNativeType(),
|
||||||
@@ -239,7 +244,10 @@ extension FieldContainer {
|
|||||||
|
|
||||||
rawObject.didAccessValue(forKey: keyPath)
|
rawObject.didAccessValue(forKey: keyPath)
|
||||||
}
|
}
|
||||||
let value = customGetter(PartialObject<O>(rawObject))
|
let value = customGetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: self)
|
||||||
|
)
|
||||||
return value.cs_toFieldStoredNativeType()
|
return value.cs_toFieldStoredNativeType()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,7 +268,8 @@ extension FieldContainer {
|
|||||||
rawObject.didChangeValue(forKey: keyPath)
|
rawObject.didChangeValue(forKey: keyPath)
|
||||||
}
|
}
|
||||||
customSetter(
|
customSetter(
|
||||||
PartialObject<O>(rawObject),
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: self),
|
||||||
V.cs_fromFieldStoredNativeType(newValue as! V.FieldStoredNativeType)
|
V.cs_fromFieldStoredNativeType(newValue as! V.FieldStoredNativeType)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -275,8 +284,8 @@ extension FieldContainer {
|
|||||||
isOptional: Bool,
|
isOptional: Bool,
|
||||||
versionHashModifier: @escaping () -> String?,
|
versionHashModifier: @escaping () -> String?,
|
||||||
renamingIdentifier: @escaping () -> String?,
|
renamingIdentifier: @escaping () -> String?,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)?,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? ,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? ,
|
||||||
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
@@ -300,8 +309,8 @@ extension FieldContainer {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: ((_ partialObject: PartialObject<O>) -> V)?
|
private let customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?
|
||||||
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)?
|
private let customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,15 +332,15 @@ extension FieldContainer.Stored where V: FieldOptionalType {
|
|||||||
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
||||||
var displayName: String = ""
|
var displayName: String = ""
|
||||||
|
|
||||||
private static func getName(_ partialObject: PartialObject<Person>) -> String {
|
private static func getName(_ object: ObjectProxy<Person>) -> String {
|
||||||
let cachedDisplayName = partialObject.primitiveValue(for: \.$displayName)
|
let cachedDisplayName = object.primitiveValue(for: \.$displayName)
|
||||||
if !cachedDisplayName.isEmpty {
|
if !cachedDisplayName.isEmpty {
|
||||||
return cachedDisplayName
|
return cachedDisplayName
|
||||||
}
|
}
|
||||||
let title = partialObject.value(for: \.$title)
|
let title = object.value(for: \.$title)
|
||||||
let name = partialObject.value(for: \.$name)
|
let name = object.value(for: \.$name)
|
||||||
let displayName = "\(title) \(name)"
|
let displayName = "\(title) \(name)"
|
||||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
object.setPrimitiveValue(displayName, for: { $0.displayName })
|
||||||
return displayName
|
return displayName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,8 +349,8 @@ extension FieldContainer.Stored where V: FieldOptionalType {
|
|||||||
- parameter keyPath: the permanent attribute name for this property.
|
- 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 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 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 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 customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.primitiveValue(for:)` instead of `ObjectProxy<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 customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.$property.primitiveValue` instead of `ObjectProxy<O>.$property.value`, 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:)`.
|
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||||
*/
|
*/
|
||||||
public init(
|
public init(
|
||||||
@@ -349,8 +358,8 @@ extension FieldContainer.Stored where V: FieldOptionalType {
|
|||||||
_ keyPath: KeyPathString,
|
_ keyPath: KeyPathString,
|
||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
|
|||||||
@@ -65,28 +65,28 @@ extension FieldContainer {
|
|||||||
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
@Field.Virtual("displayName", customGetter: Person.getName(_:))
|
||||||
var displayName: String = ""
|
var displayName: String = ""
|
||||||
|
|
||||||
private static func getName(_ partialObject: PartialObject<Person>) -> String {
|
private static func getName(_ object: ObjectProxy<Person>) -> String {
|
||||||
let cachedDisplayName = partialObject.primitiveValue(for: \.$displayName)
|
let cachedDisplayName = object.primitiveValue(for: \.$displayName)
|
||||||
if !cachedDisplayName.isEmpty {
|
if !cachedDisplayName.isEmpty {
|
||||||
return cachedDisplayName
|
return cachedDisplayName
|
||||||
}
|
}
|
||||||
let title = partialObject.value(for: \.$title)
|
let title = object.value(for: \.$title)
|
||||||
let name = partialObject.value(for: \.$name)
|
let name = object.value(for: \.$name)
|
||||||
let displayName = "\(title) \(name)"
|
let displayName = "\(title) \(name)"
|
||||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
object.setPrimitiveValue(displayName, for: { $0.displayName })
|
||||||
return displayName
|
return displayName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- parameter keyPath: the permanent attribute name for this property.
|
- parameter keyPath: the permanent attribute name for this property.
|
||||||
- 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 customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.primitiveValue(for:)` instead of `ObjectProxy<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 customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<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 `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.$property.primitiveValue` instead of `ObjectProxy<O>.$property.value`, 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:)`.
|
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||||
*/
|
*/
|
||||||
public init(
|
public init(
|
||||||
_ keyPath: KeyPathString,
|
_ keyPath: KeyPathString,
|
||||||
customGetter: @escaping (_ partialObject: PartialObject<O>) -> V,
|
customGetter: @escaping (_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
@@ -106,8 +106,8 @@ extension FieldContainer {
|
|||||||
wrappedValue initial: @autoclosure @escaping () -> V,
|
wrappedValue initial: @autoclosure @escaping () -> V,
|
||||||
_ keyPath: KeyPathString,
|
_ keyPath: KeyPathString,
|
||||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
|
||||||
|
|
||||||
fatalError()
|
fatalError()
|
||||||
@@ -188,7 +188,10 @@ extension FieldContainer {
|
|||||||
let field = field as! Self
|
let field = field as! Self
|
||||||
if let customGetter = field.customGetter {
|
if let customGetter = field.customGetter {
|
||||||
|
|
||||||
return customGetter(PartialObject<O>(rawObject))
|
return customGetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: field)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let keyPath = field.keyPath
|
let keyPath = field.keyPath
|
||||||
switch rawObject.value(forKey: keyPath) {
|
switch rawObject.value(forKey: keyPath) {
|
||||||
@@ -216,7 +219,11 @@ extension FieldContainer {
|
|||||||
let keyPath = field.keyPath
|
let keyPath = field.keyPath
|
||||||
if let customSetter = field.customSetter {
|
if let customSetter = field.customSetter {
|
||||||
|
|
||||||
return customSetter(PartialObject<O>(rawObject), newValue)
|
return customSetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: field),
|
||||||
|
newValue
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return rawObject.setValue(newValue, forKey: keyPath)
|
return rawObject.setValue(newValue, forKey: keyPath)
|
||||||
}
|
}
|
||||||
@@ -241,7 +248,10 @@ extension FieldContainer {
|
|||||||
|
|
||||||
rawObject.didAccessValue(forKey: keyPath)
|
rawObject.didAccessValue(forKey: keyPath)
|
||||||
}
|
}
|
||||||
return customGetter(PartialObject<O>(rawObject))
|
return customGetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: self)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +270,11 @@ extension FieldContainer {
|
|||||||
|
|
||||||
rawObject.didChangeValue(forKey: keyPath)
|
rawObject.didChangeValue(forKey: keyPath)
|
||||||
}
|
}
|
||||||
return customSetter(PartialObject<O>(rawObject), newValue as! V)
|
return customSetter(
|
||||||
|
ObjectProxy<O>(rawObject),
|
||||||
|
ObjectProxy<O>.FieldProxy<V>(rawObject: rawObject, field: self),
|
||||||
|
newValue as! V
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,8 +284,8 @@ extension FieldContainer {
|
|||||||
fileprivate init(
|
fileprivate init(
|
||||||
keyPath: KeyPathString,
|
keyPath: KeyPathString,
|
||||||
isOptional: Bool,
|
isOptional: Bool,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)?,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? ,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? ,
|
||||||
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
@@ -295,8 +309,8 @@ extension FieldContainer {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: ((_ partialObject: PartialObject<O>) -> V)?
|
private let customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?
|
||||||
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)?
|
private let customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,8 +319,8 @@ extension FieldContainer.Virtual where V: FieldOptionalType {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
_ keyPath: KeyPathString,
|
_ keyPath: KeyPathString,
|
||||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
|
|||||||
235
Sources/ObjectProxy.swift
Normal file
235
Sources/ObjectProxy.swift
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
//
|
||||||
|
// ObjectProxy.swift
|
||||||
|
// CoreStore
|
||||||
|
//
|
||||||
|
// Copyright © 2020 John Rommel Estropia
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - ObjectProxy
|
||||||
|
|
||||||
|
/**
|
||||||
|
An `ObjectProxy` is only used when overriding getters and setters for `CoreStoreObject` properties. Custom getters and setters are implemented as a closure that "overrides" the default property getter/setter. The closure receives an `ObjectProxy<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 every time KVO invokes this accessor method incurs a heavy performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `ObjectProxy<O>.$property.primitiveValue` instead of `ObjectProxy<O>.$property.value`, which would execute the same accessor again recursively.
|
||||||
|
*/
|
||||||
|
@dynamicMemberLookup
|
||||||
|
public struct ObjectProxy<O: CoreStoreObject> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the value for the property identified by a given key.
|
||||||
|
*/
|
||||||
|
public subscript<OBase, V>(dynamicMember member: KeyPath<O, FieldContainer<OBase>.Stored<V>>) -> FieldProxy<V> {
|
||||||
|
|
||||||
|
return .init(rawObject: self.rawObject, keyPath: member)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the value for the property identified by a given key.
|
||||||
|
*/
|
||||||
|
public subscript<OBase, V>(dynamicMember member: KeyPath<O, FieldContainer<OBase>.Virtual<V>>) -> FieldProxy<V> {
|
||||||
|
|
||||||
|
return .init(rawObject: self.rawObject, keyPath: member)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the value for the property identified by a given key.
|
||||||
|
*/
|
||||||
|
public subscript<OBase, V>(dynamicMember member: KeyPath<O, FieldContainer<OBase>.Coded<V>>) -> FieldProxy<V> {
|
||||||
|
|
||||||
|
return .init(rawObject: self.rawObject, keyPath: member)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
internal let rawObject: CoreStoreManagedObject
|
||||||
|
|
||||||
|
internal init(_ rawObject: CoreStoreManagedObject) {
|
||||||
|
|
||||||
|
self.rawObject = rawObject
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - FieldProxy
|
||||||
|
|
||||||
|
public struct FieldProxy<V> {
|
||||||
|
|
||||||
|
// MARK: Public
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the value for the specified property from the object’s private internal storage.
|
||||||
|
|
||||||
|
Accessing this property does not invoke the access notification methods (`willAccessValue(forKey:)` and `didAccessValue(forKey:)`). This method is used primarily to implement custom accessor methods that need direct access to the object's private storage.
|
||||||
|
*/
|
||||||
|
public var primitiveValue: V? {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
|
return self.getPrimitiveValue()
|
||||||
|
}
|
||||||
|
nonmutating set {
|
||||||
|
|
||||||
|
self.setPrimitiveValue(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the value for the property identified by a given key.
|
||||||
|
|
||||||
|
Accessing this property triggers the access notification methods (`willAccessValue(forKey:)` and `didAccessValue(forKey:)`).
|
||||||
|
*/
|
||||||
|
public var value: V {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
|
return self.getValue()
|
||||||
|
}
|
||||||
|
nonmutating set {
|
||||||
|
|
||||||
|
self.setValue(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
internal init<OBase>(rawObject: CoreStoreManagedObject, keyPath: KeyPath<O, FieldContainer<OBase>.Stored<V>>) {
|
||||||
|
|
||||||
|
self.init(rawObject: rawObject, field: O.meta[keyPath: keyPath])
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init<OBase>(rawObject: CoreStoreManagedObject, keyPath: KeyPath<O, FieldContainer<OBase>.Virtual<V>>) {
|
||||||
|
|
||||||
|
self.init(rawObject: rawObject, field: O.meta[keyPath: keyPath])
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init<OBase>(rawObject: CoreStoreManagedObject, keyPath: KeyPath<O, FieldContainer<OBase>.Coded<V>>) {
|
||||||
|
|
||||||
|
self.init(rawObject: rawObject, field: O.meta[keyPath: keyPath])
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init<OBase>(rawObject: CoreStoreManagedObject, field: FieldContainer<OBase>.Stored<V>) {
|
||||||
|
|
||||||
|
let keyPathString = field.keyPath
|
||||||
|
self.getValue = {
|
||||||
|
|
||||||
|
return FieldContainer<O>.Stored<V>.read(field: field, for: rawObject) as! V
|
||||||
|
}
|
||||||
|
self.setValue = {
|
||||||
|
|
||||||
|
FieldContainer<O>.Stored<V>.modify(field: field, for: rawObject, newValue: $0)
|
||||||
|
}
|
||||||
|
self.getPrimitiveValue = {
|
||||||
|
|
||||||
|
return V.cs_fromFieldStoredNativeType(
|
||||||
|
rawObject.primitiveValue(forKey: keyPathString) as! V.FieldStoredNativeType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
self.setPrimitiveValue = {
|
||||||
|
|
||||||
|
rawObject.setPrimitiveValue(
|
||||||
|
$0.cs_toFieldStoredNativeType(),
|
||||||
|
forKey: keyPathString
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init<OBase>(rawObject: CoreStoreManagedObject, field: FieldContainer<OBase>.Virtual<V>) {
|
||||||
|
|
||||||
|
let keyPathString = field.keyPath
|
||||||
|
self.getValue = {
|
||||||
|
|
||||||
|
return FieldContainer<O>.Virtual<V>.read(field: field, for: rawObject) as! V
|
||||||
|
}
|
||||||
|
self.setValue = {
|
||||||
|
|
||||||
|
FieldContainer<O>.Virtual<V>.modify(field: field, for: rawObject, newValue: $0)
|
||||||
|
}
|
||||||
|
self.getPrimitiveValue = {
|
||||||
|
|
||||||
|
switch rawObject.primitiveValue(forKey: keyPathString) {
|
||||||
|
|
||||||
|
case let value as V:
|
||||||
|
return value
|
||||||
|
|
||||||
|
case nil,
|
||||||
|
is NSNull,
|
||||||
|
_? /* any other unrelated type */ :
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.setPrimitiveValue = {
|
||||||
|
|
||||||
|
rawObject.setPrimitiveValue(
|
||||||
|
$0,
|
||||||
|
forKey: keyPathString
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init<OBase>(rawObject: CoreStoreManagedObject, field: FieldContainer<OBase>.Coded<V>) {
|
||||||
|
|
||||||
|
let keyPathString = field.keyPath
|
||||||
|
self.getValue = {
|
||||||
|
|
||||||
|
return FieldContainer<O>.Coded<V>.read(field: field, for: rawObject) as! V
|
||||||
|
}
|
||||||
|
self.setValue = {
|
||||||
|
|
||||||
|
FieldContainer<O>.Coded<V>.modify(field: field, for: rawObject, newValue: $0)
|
||||||
|
}
|
||||||
|
self.getPrimitiveValue = {
|
||||||
|
|
||||||
|
switch rawObject.primitiveValue(forKey: keyPathString) {
|
||||||
|
|
||||||
|
case let valueBox as Internals.AnyFieldCoder.TransformableDefaultValueCodingBox:
|
||||||
|
rawObject.setPrimitiveValue(valueBox.value, forKey: keyPathString)
|
||||||
|
return valueBox.value as? V
|
||||||
|
|
||||||
|
case let value as V:
|
||||||
|
return value
|
||||||
|
|
||||||
|
case nil,
|
||||||
|
is NSNull,
|
||||||
|
_? /* any other unrelated type */ :
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.setPrimitiveValue = {
|
||||||
|
|
||||||
|
rawObject.setPrimitiveValue(
|
||||||
|
$0,
|
||||||
|
forKey: keyPathString
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let getValue: () -> V
|
||||||
|
private let setValue: (V) -> Void
|
||||||
|
private let getPrimitiveValue: () -> V?
|
||||||
|
private let setPrimitiveValue: (V?) -> Void
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user