mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-17 22:46:51 +01:00
Implement dynamic initializers for Field properties (fixes #382)
This commit is contained in:
@@ -33,6 +33,7 @@ import Foundation
|
||||
|
||||
internal typealias CustomGetter = @convention(block) (_ rawObject: Any) -> Any?
|
||||
internal typealias CustomSetter = @convention(block) (_ rawObject: Any, _ newValue: Any?) -> Void
|
||||
internal typealias CustomInitializer = @convention(block) (_ rawObject: Any) -> Void
|
||||
internal typealias CustomGetterSetter = (getter: CustomGetter?, setter: CustomSetter?)
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
|
||||
@@ -209,15 +209,17 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
let rawModel = NSManagedObjectModel()
|
||||
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||
var allCustomGettersSetters: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||
var allCustomInitializers: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomInitializer]] = [:]
|
||||
var allFieldCoders: [DynamicEntity: [KeyPathString: Internals.AnyFieldCoder]] = [:]
|
||||
for entity in self.allEntities {
|
||||
|
||||
let (entityDescription, customGetterSetterByKeyPaths, fieldCoders) = self.entityDescription(
|
||||
let (entityDescription, customGetterSetterByKeyPaths, customInitializerByKeyPaths, fieldCoders) = self.entityDescription(
|
||||
for: entity,
|
||||
initializer: CoreStoreSchema.firstPassCreateEntityDescription(from:in:)
|
||||
)
|
||||
entityDescriptionsByEntity[entity] = (entityDescription.copy() as! NSEntityDescription)
|
||||
allCustomGettersSetters[entity] = customGetterSetterByKeyPaths
|
||||
allCustomInitializers[entity] = customInitializerByKeyPaths
|
||||
allFieldCoders[entity] = fieldCoders
|
||||
}
|
||||
CoreStoreSchema.secondPassConnectRelationshipAttributes(for: entityDescriptionsByEntity)
|
||||
@@ -225,6 +227,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
CoreStoreSchema.fourthPassSynthesizeManagedObjectClasses(
|
||||
for: entityDescriptionsByEntity,
|
||||
allCustomGettersSetters: allCustomGettersSetters,
|
||||
allCustomInitializers: allCustomInitializers,
|
||||
allFieldCoders: allFieldCoders
|
||||
)
|
||||
|
||||
@@ -257,6 +260,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
|
||||
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||
private var customGettersSettersByEntity: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||
private var customInitializersByEntity: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomInitializer]] = [:]
|
||||
private var fieldCodersByEntity: [DynamicEntity: [KeyPathString: Internals.AnyFieldCoder]] = [:]
|
||||
private weak var cachedRawModel: NSManagedObjectModel?
|
||||
|
||||
@@ -265,11 +269,13 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
initializer: (DynamicEntity, ModelVersion) -> (
|
||||
entity: NSEntityDescription,
|
||||
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
||||
customInitializersByEntity: [KeyPathString: CoreStoreManagedObject.CustomInitializer],
|
||||
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
||||
)
|
||||
) -> (
|
||||
entity: NSEntityDescription,
|
||||
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
||||
customInitializerByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomInitializer],
|
||||
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
||||
) {
|
||||
|
||||
@@ -278,23 +284,31 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
return (
|
||||
cachedEntityDescription,
|
||||
self.customGettersSettersByEntity[entity] ?? [:],
|
||||
self.customInitializersByEntity[entity] ?? [:],
|
||||
self.fieldCodersByEntity[entity] ?? [:]
|
||||
)
|
||||
}
|
||||
let modelVersion = self.modelVersion
|
||||
let (entityDescription, customGetterSetterByKeyPaths, fieldCoders) = withoutActuallyEscaping(
|
||||
let (entityDescription, customGetterSetterByKeyPaths, customInitializerByKeyPaths, fieldCoders) = withoutActuallyEscaping(
|
||||
initializer,
|
||||
do: { $0(entity, modelVersion) }
|
||||
)
|
||||
self.entityDescriptionsByEntity[entity] = entityDescription
|
||||
self.customGettersSettersByEntity[entity] = customGetterSetterByKeyPaths
|
||||
self.customInitializersByEntity[entity] = customInitializerByKeyPaths
|
||||
self.fieldCodersByEntity[entity] = fieldCoders
|
||||
return (entityDescription, customGetterSetterByKeyPaths, fieldCoders)
|
||||
return (
|
||||
entityDescription,
|
||||
customGetterSetterByKeyPaths,
|
||||
customInitializerByKeyPaths,
|
||||
fieldCoders
|
||||
)
|
||||
}
|
||||
|
||||
private static func firstPassCreateEntityDescription(from entity: DynamicEntity, in modelVersion: ModelVersion) -> (
|
||||
entity: NSEntityDescription,
|
||||
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
||||
customInitializerByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomInitializer],
|
||||
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
||||
) {
|
||||
|
||||
@@ -306,6 +320,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
entityDescription.managedObjectClassName = CoreStoreManagedObject.cs_subclassName(for: entity, in: modelVersion)
|
||||
|
||||
var keyPathsByAffectedKeyPaths: [KeyPathString: Set<KeyPathString>] = [:]
|
||||
var customInitialValuesByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomInitializer] = [:]
|
||||
var customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter] = [:]
|
||||
var fieldCoders: [KeyPathString: Internals.AnyFieldCoder] = [:]
|
||||
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
||||
@@ -338,6 +353,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
|
||||
keyPathsByAffectedKeyPaths[attribute.keyPath] = entityDescriptionValues.affectedByKeyPaths
|
||||
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
|
||||
customInitialValuesByKeyPaths[attribute.keyPath] = attribute.initializer
|
||||
fieldCoders[attribute.keyPath] = valueTransformer
|
||||
|
||||
case let relationship as FieldRelationshipProtocol:
|
||||
@@ -401,7 +417,12 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
}
|
||||
entityDescription.properties = createProperties(for: entity.type as! CoreStoreObject.Type)
|
||||
entityDescription.keyPathsByAffectedKeyPaths = keyPathsByAffectedKeyPaths
|
||||
return (entityDescription, customGetterSetterByKeyPaths, fieldCoders)
|
||||
return (
|
||||
entityDescription,
|
||||
customGetterSetterByKeyPaths,
|
||||
customInitialValuesByKeyPaths,
|
||||
fieldCoders
|
||||
)
|
||||
}
|
||||
|
||||
private static func secondPassConnectRelationshipAttributes(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription]) {
|
||||
@@ -597,10 +618,15 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
private static func fourthPassSynthesizeManagedObjectClasses(
|
||||
for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription],
|
||||
allCustomGettersSetters: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]],
|
||||
allCustomInitializers: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomInitializer]],
|
||||
allFieldCoders: [DynamicEntity: [KeyPathString: Internals.AnyFieldCoder]]
|
||||
) {
|
||||
|
||||
func createManagedObjectSubclass(for entityDescription: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]?) {
|
||||
func createManagedObjectSubclass(
|
||||
for entityDescription: NSEntityDescription,
|
||||
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]?,
|
||||
customInitializers: [KeyPathString: CoreStoreManagedObject.CustomInitializer]?
|
||||
) {
|
||||
|
||||
let superEntity = entityDescription.superentity
|
||||
let className = entityDescription.managedObjectClassName!
|
||||
@@ -612,7 +638,8 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
|
||||
createManagedObjectSubclass(
|
||||
for: superEntity,
|
||||
customGetterSetterByKeyPaths: superEntity.coreStoreEntity.flatMap({ allCustomGettersSetters[$0] })
|
||||
customGetterSetterByKeyPaths: superEntity.coreStoreEntity.flatMap({ allCustomGettersSetters[$0] }),
|
||||
customInitializers: superEntity.coreStoreEntity.flatMap({ allCustomInitializers[$0] })
|
||||
)
|
||||
}
|
||||
let superClass = Internals.with { () -> CoreStoreManagedObject.Type in
|
||||
@@ -675,40 +702,80 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let newSelector = NSSelectorFromString("cs_keyPathsForValuesAffectingValueForKey:")
|
||||
let keyPathsByAffectedKeyPaths = entityDescription.keyPathsByAffectedKeyPaths
|
||||
let keyPathsForValuesAffectingValue: @convention(block) (Any, String) -> Set<String> = { (instance, keyPath) in
|
||||
|
||||
if let keyPaths = keyPathsByAffectedKeyPaths[keyPath] {
|
||||
swizzle_keyPathsForValuesAffectingValueForKey: do {
|
||||
|
||||
let newSelector = NSSelectorFromString("cs_keyPathsForValuesAffectingValueForKey:")
|
||||
let keyPathsByAffectedKeyPaths = entityDescription.keyPathsByAffectedKeyPaths
|
||||
let keyPathsForValuesAffectingValue: @convention(block) (Any, String) -> Set<String> = { (instance, keyPath) in
|
||||
|
||||
return keyPaths
|
||||
if let keyPaths = keyPathsByAffectedKeyPaths[keyPath] {
|
||||
|
||||
return keyPaths
|
||||
}
|
||||
return []
|
||||
}
|
||||
return []
|
||||
}
|
||||
let origSelector = #selector(CoreStoreManagedObject.keyPathsForValuesAffectingValue(forKey:))
|
||||
|
||||
let metaClass: AnyClass = object_getClass(managedObjectClass)!
|
||||
let origMethod = class_getClassMethod(managedObjectClass, origSelector)!
|
||||
|
||||
let origImp = method_getImplementation(origMethod)
|
||||
let newImp = imp_implementationWithBlock(keyPathsForValuesAffectingValue)
|
||||
|
||||
if class_addMethod(metaClass, origSelector, newImp, method_getTypeEncoding(origMethod)) {
|
||||
let origSelector = #selector(CoreStoreManagedObject.keyPathsForValuesAffectingValue(forKey:))
|
||||
|
||||
class_replaceMethod(metaClass, newSelector, origImp, method_getTypeEncoding(origMethod))
|
||||
}
|
||||
else {
|
||||
let metaClass: AnyClass = object_getClass(managedObjectClass)!
|
||||
let origMethod = class_getClassMethod(managedObjectClass, origSelector)!
|
||||
|
||||
let newMethod = class_getClassMethod(managedObjectClass, newSelector)!
|
||||
method_exchangeImplementations(origMethod, newMethod)
|
||||
let origImp = method_getImplementation(origMethod)
|
||||
let newImp = imp_implementationWithBlock(keyPathsForValuesAffectingValue)
|
||||
|
||||
if class_addMethod(metaClass, origSelector, newImp, method_getTypeEncoding(origMethod)) {
|
||||
|
||||
class_replaceMethod(metaClass, newSelector, origImp, method_getTypeEncoding(origMethod))
|
||||
}
|
||||
else {
|
||||
|
||||
let newMethod = class_getClassMethod(managedObjectClass, newSelector)!
|
||||
method_exchangeImplementations(origMethod, newMethod)
|
||||
}
|
||||
}
|
||||
swizzle_awakeFromInsert: do {
|
||||
|
||||
let newSelector = NSSelectorFromString("cs_awakeFromInsert")
|
||||
let awakeFromInsertValue: @convention(block) (Any) -> Void
|
||||
if let customInitializers = customInitializers,
|
||||
!customInitializers.isEmpty {
|
||||
|
||||
let initializers = Array(customInitializers.values)
|
||||
awakeFromInsertValue = { (instance) in
|
||||
|
||||
initializers.forEach {
|
||||
|
||||
$0(instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
awakeFromInsertValue = { _ in }
|
||||
}
|
||||
let origSelector = #selector(CoreStoreManagedObject.awakeFromInsert)
|
||||
|
||||
let origMethod = class_getInstanceMethod(managedObjectClass, origSelector)!
|
||||
|
||||
let origImp = method_getImplementation(origMethod)
|
||||
let newImp = imp_implementationWithBlock(awakeFromInsertValue)
|
||||
|
||||
if class_addMethod(managedObjectClass, origSelector, newImp, method_getTypeEncoding(origMethod)) {
|
||||
|
||||
class_replaceMethod(managedObjectClass, newSelector, origImp, method_getTypeEncoding(origMethod))
|
||||
}
|
||||
else {
|
||||
|
||||
let newMethod = class_getInstanceMethod(managedObjectClass, newSelector)!
|
||||
method_exchangeImplementations(origMethod, newMethod)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (dynamicEntity, entityDescription) in entityDescriptionsByEntity {
|
||||
|
||||
createManagedObjectSubclass(
|
||||
for: entityDescription,
|
||||
customGetterSetterByKeyPaths: allCustomGettersSetters[dynamicEntity]
|
||||
customGetterSetterByKeyPaths: allCustomGettersSetters[dynamicEntity],
|
||||
customInitializers: allCustomInitializers[dynamicEntity]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,32 @@ extension FieldContainer {
|
||||
valueTransformer: { Internals.AnyFieldCoder(fieldCoderType) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
public init<Coder: FieldCoderType>(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
coder fieldCoderType: Coder.Type,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = [],
|
||||
dynamicInitialValue: @escaping () -> V
|
||||
) where Coder.FieldStoredValue == V {
|
||||
|
||||
self.init(
|
||||
defaultValue: nil,
|
||||
keyPath: keyPath,
|
||||
isOptional: false,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
valueTransformer: { Internals.AnyFieldCoder(fieldCoderType) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: dynamicInitialValue,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
@@ -142,6 +168,32 @@ extension FieldContainer {
|
||||
valueTransformer: { Internals.AnyFieldCoder(tag: UUID(), encode: coder.encode, decode: coder.decode) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
coder: (encode: (V) -> Data?, decode: (Data?) -> V),
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = [],
|
||||
dynamicInitialValue: @escaping () -> V
|
||||
) {
|
||||
|
||||
self.init(
|
||||
defaultValue: nil,
|
||||
keyPath: keyPath,
|
||||
isOptional: false,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
valueTransformer: { Internals.AnyFieldCoder(tag: UUID(), encode: coder.encode, decode: coder.decode) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: dynamicInitialValue,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
@@ -333,12 +385,29 @@ extension FieldContainer {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal var initializer: CoreStoreManagedObject.CustomInitializer? {
|
||||
|
||||
guard let dynamicInitialValue = self.dynamicInitialValue else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any) -> Void in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.setPrimitiveValue(
|
||||
dynamicInitialValue(),
|
||||
forKey: keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate init(
|
||||
defaultValue: @escaping () -> Any?,
|
||||
defaultValue: (() -> Any?)?,
|
||||
keyPath: KeyPathString,
|
||||
isOptional: Bool,
|
||||
versionHashModifier: @escaping () -> String?,
|
||||
@@ -346,6 +415,7 @@ extension FieldContainer {
|
||||
valueTransformer: @escaping () -> Internals.AnyFieldCoder?,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? ,
|
||||
dynamicInitialValue: (() -> V)?,
|
||||
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
@@ -361,14 +431,17 @@ extension FieldContainer {
|
||||
renamingIdentifier: renamingIdentifier(),
|
||||
valueTransformer: fieldCoder,
|
||||
affectedByKeyPaths: affectedByKeyPaths(),
|
||||
defaultValue: Internals.AnyFieldCoder.TransformableDefaultValueCodingBox(
|
||||
defaultValue: defaultValue(),
|
||||
fieldCoder: fieldCoder
|
||||
)
|
||||
defaultValue: defaultValue.map {
|
||||
Internals.AnyFieldCoder.TransformableDefaultValueCodingBox(
|
||||
defaultValue: $0(),
|
||||
fieldCoder: fieldCoder
|
||||
) as Any
|
||||
}
|
||||
)
|
||||
}
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
self.dynamicInitialValue = dynamicInitialValue
|
||||
}
|
||||
|
||||
|
||||
@@ -376,6 +449,7 @@ extension FieldContainer {
|
||||
|
||||
private let customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?
|
||||
private let customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)?
|
||||
private let dynamicInitialValue: (() -> V)?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,6 +496,32 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
||||
valueTransformer: { Internals.AnyFieldCoder(coder) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
public init<Coder: FieldCoderType>(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
coder: Coder.Type,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = [],
|
||||
dynamicInitialValue: @escaping () -> V
|
||||
) where Coder.FieldStoredValue == V.Wrapped {
|
||||
|
||||
self.init(
|
||||
defaultValue: nil,
|
||||
keyPath: keyPath,
|
||||
isOptional: true,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
valueTransformer: { Internals.AnyFieldCoder(coder) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: dynamicInitialValue,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
@@ -470,6 +570,32 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
||||
valueTransformer: { Internals.AnyFieldCoder(tag: UUID(), encode: coder.encode, decode: coder.decode) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
coder: (encode: (V) -> Data?, decode: (Data?) -> V),
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = [],
|
||||
dynamicInitialValue: @escaping () -> V
|
||||
) {
|
||||
|
||||
self.init(
|
||||
defaultValue: nil,
|
||||
keyPath: keyPath,
|
||||
isOptional: true,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
valueTransformer: { Internals.AnyFieldCoder(tag: UUID(), encode: coder.encode, decode: coder.decode) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: dynamicInitialValue,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
@@ -516,6 +642,31 @@ extension FieldContainer.Coded where V: DefaultNSSecureCodable {
|
||||
valueTransformer: { Internals.AnyFieldCoder(FieldCoders.DefaultNSSecureCoding<V>.self) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = [],
|
||||
dynamicInitialValue: @escaping () -> V
|
||||
) {
|
||||
|
||||
self.init(
|
||||
defaultValue: nil,
|
||||
keyPath: keyPath,
|
||||
isOptional: false,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
valueTransformer: { Internals.AnyFieldCoder(FieldCoders.DefaultNSSecureCoding<V>.self) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: dynamicInitialValue,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
@@ -562,6 +713,31 @@ extension FieldContainer.Coded where V: FieldOptionalType, V.Wrapped: DefaultNSS
|
||||
valueTransformer: { Internals.AnyFieldCoder(FieldCoders.DefaultNSSecureCoding<V.Wrapped>.self) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = [],
|
||||
dynamicInitialValue: @escaping () -> V
|
||||
) {
|
||||
|
||||
self.init(
|
||||
defaultValue: nil,
|
||||
keyPath: keyPath,
|
||||
isOptional: true,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
valueTransformer: { Internals.AnyFieldCoder(FieldCoders.DefaultNSSecureCoding<V.Wrapped>.self) },
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: dynamicInitialValue,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
@@ -85,6 +85,30 @@ extension FieldContainer {
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = [],
|
||||
dynamicInitialValue: @escaping () -> V
|
||||
) {
|
||||
|
||||
self.init(
|
||||
wrappedValue: nil,
|
||||
keyPath: keyPath,
|
||||
isOptional: false,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: dynamicInitialValue,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
@@ -262,18 +286,36 @@ extension FieldContainer {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal var initializer: CoreStoreManagedObject.CustomInitializer? {
|
||||
|
||||
guard let dynamicInitialValue = self.dynamicInitialValue else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any) -> Void in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.setPrimitiveValue(
|
||||
dynamicInitialValue().cs_toFieldStoredNativeType(),
|
||||
forKey: keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate init(
|
||||
wrappedValue initial: @escaping () -> V,
|
||||
wrappedValue initial: (() -> V)?,
|
||||
keyPath: KeyPathString,
|
||||
isOptional: Bool,
|
||||
versionHashModifier: @escaping () -> String?,
|
||||
renamingIdentifier: @escaping () -> String?,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? ,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)?,
|
||||
dynamicInitialValue: (() -> V)?,
|
||||
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
@@ -287,11 +329,12 @@ extension FieldContainer {
|
||||
renamingIdentifier: renamingIdentifier(),
|
||||
valueTransformer: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths(),
|
||||
defaultValue: initial().cs_toFieldStoredNativeType()
|
||||
defaultValue: initial?().cs_toFieldStoredNativeType()
|
||||
)
|
||||
}
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
self.dynamicInitialValue = dynamicInitialValue
|
||||
}
|
||||
|
||||
|
||||
@@ -299,6 +342,7 @@ extension FieldContainer {
|
||||
|
||||
private let customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?
|
||||
private let customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)?
|
||||
private let dynamicInitialValue: (() -> V)?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +375,8 @@ extension FieldContainer.Stored where V: FieldOptionalType {
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = 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(
|
||||
wrappedValue: initial,
|
||||
@@ -341,6 +386,30 @@ extension FieldContainer.Stored where V: FieldOptionalType {
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: nil,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = nil,
|
||||
customSetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = [],
|
||||
dynamicInitialValue: @escaping () -> V
|
||||
) {
|
||||
|
||||
self.init(
|
||||
wrappedValue: nil,
|
||||
keyPath: keyPath,
|
||||
isOptional: true,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
dynamicInitialValue: dynamicInitialValue,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
@@ -275,6 +275,8 @@ extension FieldContainer {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let initializer: CoreStoreManagedObject.CustomInitializer? = nil
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
@@ -48,4 +48,5 @@ internal protocol FieldAttributeProtocol: FieldProtocol {
|
||||
var entityDescriptionValues: () -> EntityDescriptionValues { get }
|
||||
var getter: CoreStoreManagedObject.CustomGetter? { get }
|
||||
var setter: CoreStoreManagedObject.CustomSetter? { get }
|
||||
var initializer: CoreStoreManagedObject.CustomInitializer? { get }
|
||||
}
|
||||
|
||||
@@ -345,6 +345,30 @@ extension From where O: CoreStoreObject {
|
||||
return self.select(R.self, [SelectTerm<O>.attribute(keyPath)])
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections
|
||||
|
||||
- parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, FieldContainer<O>.Stored<T>>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections
|
||||
|
||||
- parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, FieldContainer<O>.Virtual<T>>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections
|
||||
|
||||
|
||||
@@ -115,27 +115,6 @@ extension NSEntityDescription {
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal var customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter] {
|
||||
|
||||
get {
|
||||
|
||||
if let userInfo = self.userInfo,
|
||||
let value = userInfo[UserInfoKey.CoreStoreManagedObjectCustomGetterSetterByKeyPaths] {
|
||||
|
||||
return value as! [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]
|
||||
}
|
||||
return [:]
|
||||
}
|
||||
set {
|
||||
|
||||
cs_setUserInfo { (userInfo) in
|
||||
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectCustomGetterSetterByKeyPaths] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@@ -151,7 +130,6 @@ extension NSEntityDescription {
|
||||
fileprivate static let CoreStoreManagedObjectUniqueConstraints = "CoreStoreManagedObjectUniqueConstraints"
|
||||
|
||||
fileprivate static let CoreStoreManagedObjectKeyPathsByAffectedKeyPaths = "CoreStoreManagedObjectKeyPathsByAffectedKeyPaths"
|
||||
fileprivate static let CoreStoreManagedObjectCustomGetterSetterByKeyPaths = "CoreStoreManagedObjectCustomGetterSetterByKeyPaths"
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user