mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-31 06:33:06 +02:00
Implement dynamic initializers for Field properties (fixes #382)
This commit is contained in:
@@ -146,8 +146,7 @@ final class CollectionViewDemoViewController: UICollectionViewController {
|
|||||||
ColorsDemo.stack.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { (transaction) in
|
asynchronous: { (transaction) in
|
||||||
|
|
||||||
let palette = transaction.create(Into<Palette>())
|
_ = transaction.create(Into<Palette>())
|
||||||
palette.setInitialValues(in: transaction)
|
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
@@ -159,8 +158,8 @@ final class CollectionViewDemoViewController: UICollectionViewController {
|
|||||||
|
|
||||||
for palette in try transaction.fetchAll(From<Palette>()) {
|
for palette in try transaction.fetchAll(From<Palette>()) {
|
||||||
|
|
||||||
palette.hue .= Palette.randomHue()
|
palette.hue = Palette.randomHue()
|
||||||
palette.colorName .= nil
|
palette.colorName = nil
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ struct ColorsDemo {
|
|||||||
switch self {
|
switch self {
|
||||||
|
|
||||||
case .all: return .init()
|
case .all: return .init()
|
||||||
case .light: return (\Palette.brightness >= 0.9)
|
case .light: return (\Palette.$brightness >= 0.9)
|
||||||
case .dark: return (\Palette.brightness <= 0.4)
|
case .dark: return (\Palette.$brightness <= 0.4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,9 +47,9 @@ struct ColorsDemo {
|
|||||||
|
|
||||||
try! self.palettes.refetch(
|
try! self.palettes.refetch(
|
||||||
From<Palette>()
|
From<Palette>()
|
||||||
.sectionBy(\.colorName)
|
.sectionBy(\.$colorName)
|
||||||
.where(self.filter.whereClause())
|
.where(self.filter.whereClause())
|
||||||
.orderBy(.ascending(\.hue))
|
.orderBy(.ascending(\.$hue))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,8 +81,8 @@ struct ColorsDemo {
|
|||||||
|
|
||||||
return ColorsDemo.stack.publishList(
|
return ColorsDemo.stack.publishList(
|
||||||
From<Palette>()
|
From<Palette>()
|
||||||
.sectionBy(\.colorName)
|
.sectionBy(\.$colorName)
|
||||||
.orderBy(.ascending(\.hue))
|
.orderBy(.ascending(\.$hue))
|
||||||
)
|
)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,8 +158,7 @@ final class ListObserverDemoViewController: UITableViewController {
|
|||||||
ColorsDemo.stack.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { (transaction) in
|
asynchronous: { (transaction) in
|
||||||
|
|
||||||
let palette = transaction.create(Into<Palette>())
|
_ = transaction.create(Into<Palette>())
|
||||||
palette.setInitialValues(in: transaction)
|
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
@@ -171,8 +170,8 @@ final class ListObserverDemoViewController: UITableViewController {
|
|||||||
|
|
||||||
for palette in try transaction.fetchAll(From<Palette>()) {
|
for palette in try transaction.fetchAll(From<Palette>()) {
|
||||||
|
|
||||||
palette.hue .= Palette.randomHue()
|
palette.hue = Palette.randomHue()
|
||||||
palette.colorName .= nil
|
palette.colorName = nil
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
|
||||||
if let palette = try! ColorsDemo.stack.fetchOne(From<Palette>().orderBy(.ascending(\.hue))) {
|
if let palette = try! ColorsDemo.stack.fetchOne(From<Palette>().orderBy(.ascending(\.$hue))) {
|
||||||
|
|
||||||
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
||||||
}
|
}
|
||||||
@@ -52,12 +52,11 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
_ = try? ColorsDemo.stack.perform(
|
_ = try? ColorsDemo.stack.perform(
|
||||||
synchronous: { (transaction) in
|
synchronous: { (transaction) in
|
||||||
|
|
||||||
let palette = transaction.create(Into<Palette>())
|
_ = transaction.create(Into<Palette>())
|
||||||
palette.setInitialValues(in: transaction)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
let palette = try! ColorsDemo.stack.fetchOne(From<Palette>().orderBy(.ascending(\.hue)))!
|
let palette = try! ColorsDemo.stack.fetchOne(From<Palette>().orderBy(.ascending(\.$hue)))!
|
||||||
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +118,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
if let palette = transaction.edit(self?.monitor?.object) {
|
||||||
|
|
||||||
palette.hue .= Int(hue)
|
palette.hue = Int(hue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
@@ -134,7 +133,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
if let palette = transaction.edit(self?.monitor?.object) {
|
||||||
|
|
||||||
palette.saturation .= saturation
|
palette.saturation = saturation
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
@@ -149,7 +148,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
if let palette = transaction.edit(self?.monitor?.object) {
|
||||||
|
|
||||||
palette.brightness .= brightness
|
palette.brightness = brightness
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
@@ -169,7 +168,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
func reloadPaletteInfo(_ palette: Palette, changedKeys: Set<String>?) {
|
func reloadPaletteInfo(_ palette: Palette, changedKeys: Set<String>?) {
|
||||||
|
|
||||||
self.colorNameLabel?.text = palette.colorName.value
|
self.colorNameLabel?.text = palette.colorName
|
||||||
|
|
||||||
let color = palette.color
|
let color = palette.color
|
||||||
self.colorNameLabel?.textColor = color
|
self.colorNameLabel?.textColor = color
|
||||||
@@ -177,17 +176,17 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
self.hsbLabel?.text = palette.colorText
|
self.hsbLabel?.text = palette.colorText
|
||||||
|
|
||||||
if changedKeys == nil || changedKeys?.contains(String(keyPath: \Palette.hue)) == true {
|
if changedKeys == nil || changedKeys?.contains(String(keyPath: \Palette.$hue)) == true {
|
||||||
|
|
||||||
self.hueSlider?.value = Float(palette.hue.value)
|
self.hueSlider?.value = Float(palette.hue)
|
||||||
}
|
}
|
||||||
if changedKeys == nil || changedKeys?.contains(String(keyPath: \Palette.saturation)) == true {
|
if changedKeys == nil || changedKeys?.contains(String(keyPath: \Palette.$saturation)) == true {
|
||||||
|
|
||||||
self.saturationSlider?.value = palette.saturation.value
|
self.saturationSlider?.value = palette.saturation
|
||||||
}
|
}
|
||||||
if changedKeys == nil || changedKeys?.contains(String(keyPath: \Palette.brightness)) == true {
|
if changedKeys == nil || changedKeys?.contains(String(keyPath: \Palette.$brightness)) == true {
|
||||||
|
|
||||||
self.brightnessSlider?.value = palette.brightness.value
|
self.brightnessSlider?.value = palette.brightness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,44 +16,53 @@ import CoreStore
|
|||||||
|
|
||||||
final class Palette: CoreStoreObject {
|
final class Palette: CoreStoreObject {
|
||||||
|
|
||||||
let hue = Value.Required<Int>("hue", initial: 0)
|
@Field.Stored(
|
||||||
let saturation = Value.Required<Float>("saturation", initial: 0)
|
"hue",
|
||||||
let brightness = Value.Required<Float>("brightness", initial: 0)
|
dynamicInitialValue: { Palette.randomHue() }
|
||||||
|
|
||||||
let colorName = Value.Optional<String>(
|
|
||||||
"colorName",
|
|
||||||
isTransient: true,
|
|
||||||
customGetter: Palette.getColorName
|
|
||||||
)
|
)
|
||||||
|
var hue: Int
|
||||||
|
|
||||||
static func randomHue() -> Int {
|
@Field.Stored("saturation")
|
||||||
|
var saturation: Float = 1
|
||||||
return Int(arc4random_uniform(360))
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func getColorName(_ partialObject: PartialObject<Palette>) -> String? {
|
@Field.Stored(
|
||||||
|
"brightness",
|
||||||
if let colorName = partialObject.primitiveValue(for: { $0.colorName }) {
|
dynamicInitialValue: { Palette.randomBrightness() }
|
||||||
|
)
|
||||||
|
var brightness: Float
|
||||||
|
|
||||||
|
@Field.Virtual(
|
||||||
|
"colorName",
|
||||||
|
customGetter: { object, field in
|
||||||
|
if let colorName = field.primitiveValue {
|
||||||
|
return colorName
|
||||||
|
}
|
||||||
|
let colorName: String
|
||||||
|
switch object.$hue.value % 360 {
|
||||||
|
case 0 ..< 20: colorName = "Lower Reds"
|
||||||
|
case 20 ..< 57: colorName = "Oranges and Browns"
|
||||||
|
case 57 ..< 90: colorName = "Yellow-Greens"
|
||||||
|
case 90 ..< 159: colorName = "Greens"
|
||||||
|
case 159 ..< 197: colorName = "Blue-Greens"
|
||||||
|
case 197 ..< 241: colorName = "Blues"
|
||||||
|
case 241 ..< 297: colorName = "Violets"
|
||||||
|
case 297 ..< 331: colorName = "Magentas"
|
||||||
|
default: colorName = "Upper Reds"
|
||||||
|
}
|
||||||
|
field.primitiveValue = colorName
|
||||||
return colorName
|
return colorName
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
var colorName: String!
|
||||||
|
|
||||||
|
static func randomHue() -> Int {
|
||||||
|
|
||||||
let colorName: String
|
return Int.random(in: 0 ..< 360)
|
||||||
switch partialObject.value(for: { $0.hue }) % 360 {
|
}
|
||||||
|
|
||||||
case 0 ..< 20: colorName = "Lower Reds"
|
static func randomBrightness() -> Float {
|
||||||
case 20 ..< 57: colorName = "Oranges and Browns"
|
|
||||||
case 57 ..< 90: colorName = "Yellow-Greens"
|
|
||||||
case 90 ..< 159: colorName = "Greens"
|
|
||||||
case 159 ..< 197: colorName = "Blue-Greens"
|
|
||||||
case 197 ..< 241: colorName = "Blues"
|
|
||||||
case 241 ..< 297: colorName = "Violets"
|
|
||||||
case 297 ..< 331: colorName = "Magentas"
|
|
||||||
default: colorName = "Upper Reds"
|
|
||||||
}
|
|
||||||
|
|
||||||
partialObject.setPrimitiveValue(colorName, for: { $0.colorName })
|
return (Float.random(in: 0 ..< 70) + 30) / 100.0
|
||||||
return colorName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,25 +71,15 @@ extension Palette {
|
|||||||
var color: UIColor {
|
var color: UIColor {
|
||||||
|
|
||||||
return UIColor(
|
return UIColor(
|
||||||
hue: CGFloat(self.hue.value) / 360.0,
|
hue: CGFloat(self.hue) / 360.0,
|
||||||
saturation: CGFloat(self.saturation.value),
|
saturation: CGFloat(self.saturation),
|
||||||
brightness: CGFloat(self.brightness.value),
|
brightness: CGFloat(self.brightness),
|
||||||
alpha: 1.0
|
alpha: 1.0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var colorText: String {
|
var colorText: String {
|
||||||
|
|
||||||
return "H: \(self.hue.value)˚, S: \(round(self.saturation.value * 100.0))%, B: \(round(self.brightness.value * 100.0))%"
|
return "H: \(self.hue)˚, S: \(round(self.saturation * 100.0))%, B: \(round(self.brightness * 100.0))%"
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Palette {
|
|
||||||
|
|
||||||
func setInitialValues(in transaction: BaseDataTransaction) {
|
|
||||||
|
|
||||||
self.hue .= Palette.randomHue()
|
|
||||||
self.saturation .= Float(1.0)
|
|
||||||
self.brightness .= Float(arc4random_uniform(70) + 30) / 100.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ final class SwiftUIContainerViewController: UIViewController {
|
|||||||
rootView: SwiftUIView(
|
rootView: SwiftUIView(
|
||||||
palettes: ColorsDemo.stack.publishList(
|
palettes: ColorsDemo.stack.publishList(
|
||||||
From<Palette>()
|
From<Palette>()
|
||||||
.sectionBy(\.colorName)
|
.sectionBy(\.$colorName)
|
||||||
.orderBy(.ascending(\.hue))
|
.orderBy(.ascending(\.$hue))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.environment(\.dataStack, ColorsDemo.stack)
|
.environment(\.dataStack, ColorsDemo.stack)
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ struct SwiftUIView: View {
|
|||||||
|
|
||||||
for palette in try transaction.fetchAll(From<Palette>()) {
|
for palette in try transaction.fetchAll(From<Palette>()) {
|
||||||
|
|
||||||
palette.hue .= Palette.randomHue()
|
palette.hue = Palette.randomHue()
|
||||||
palette.colorName .= nil
|
palette.colorName = nil
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
@@ -79,8 +79,7 @@ struct SwiftUIView: View {
|
|||||||
self.dataStack.perform(
|
self.dataStack.perform(
|
||||||
asynchronous: { transaction in
|
asynchronous: { transaction in
|
||||||
|
|
||||||
let palette = transaction.create(Into<Palette>())
|
_ = transaction.create(Into<Palette>())
|
||||||
palette.setInitialValues(in: transaction)
|
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
@@ -173,8 +172,8 @@ struct SwiftUIView_Previews: PreviewProvider {
|
|||||||
SwiftUIView(
|
SwiftUIView(
|
||||||
palettes: ColorsDemo.stack.publishList(
|
palettes: ColorsDemo.stack.publishList(
|
||||||
From<Palette>()
|
From<Palette>()
|
||||||
.sectionBy(\.colorName)
|
.sectionBy(\.$colorName)
|
||||||
.orderBy(.ascending(\.hue))
|
.orderBy(.ascending(\.$hue))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.environment(\.dataStack, ColorsDemo.stack)
|
.environment(\.dataStack, ColorsDemo.stack)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import Foundation
|
|||||||
|
|
||||||
internal typealias CustomGetter = @convention(block) (_ rawObject: Any) -> Any?
|
internal typealias CustomGetter = @convention(block) (_ rawObject: Any) -> Any?
|
||||||
internal typealias CustomSetter = @convention(block) (_ rawObject: Any, _ newValue: Any?) -> Void
|
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?)
|
internal typealias CustomGetterSetter = (getter: CustomGetter?, setter: CustomSetter?)
|
||||||
|
|
||||||
@nonobjc @inline(__always)
|
@nonobjc @inline(__always)
|
||||||
|
|||||||
@@ -209,15 +209,17 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
let rawModel = NSManagedObjectModel()
|
let rawModel = NSManagedObjectModel()
|
||||||
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||||
var allCustomGettersSetters: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
var allCustomGettersSetters: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||||
|
var allCustomInitializers: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomInitializer]] = [:]
|
||||||
var allFieldCoders: [DynamicEntity: [KeyPathString: Internals.AnyFieldCoder]] = [:]
|
var allFieldCoders: [DynamicEntity: [KeyPathString: Internals.AnyFieldCoder]] = [:]
|
||||||
for entity in self.allEntities {
|
for entity in self.allEntities {
|
||||||
|
|
||||||
let (entityDescription, customGetterSetterByKeyPaths, fieldCoders) = self.entityDescription(
|
let (entityDescription, customGetterSetterByKeyPaths, customInitializerByKeyPaths, fieldCoders) = self.entityDescription(
|
||||||
for: entity,
|
for: entity,
|
||||||
initializer: CoreStoreSchema.firstPassCreateEntityDescription(from:in:)
|
initializer: CoreStoreSchema.firstPassCreateEntityDescription(from:in:)
|
||||||
)
|
)
|
||||||
entityDescriptionsByEntity[entity] = (entityDescription.copy() as! NSEntityDescription)
|
entityDescriptionsByEntity[entity] = (entityDescription.copy() as! NSEntityDescription)
|
||||||
allCustomGettersSetters[entity] = customGetterSetterByKeyPaths
|
allCustomGettersSetters[entity] = customGetterSetterByKeyPaths
|
||||||
|
allCustomInitializers[entity] = customInitializerByKeyPaths
|
||||||
allFieldCoders[entity] = fieldCoders
|
allFieldCoders[entity] = fieldCoders
|
||||||
}
|
}
|
||||||
CoreStoreSchema.secondPassConnectRelationshipAttributes(for: entityDescriptionsByEntity)
|
CoreStoreSchema.secondPassConnectRelationshipAttributes(for: entityDescriptionsByEntity)
|
||||||
@@ -225,6 +227,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
CoreStoreSchema.fourthPassSynthesizeManagedObjectClasses(
|
CoreStoreSchema.fourthPassSynthesizeManagedObjectClasses(
|
||||||
for: entityDescriptionsByEntity,
|
for: entityDescriptionsByEntity,
|
||||||
allCustomGettersSetters: allCustomGettersSetters,
|
allCustomGettersSetters: allCustomGettersSetters,
|
||||||
|
allCustomInitializers: allCustomInitializers,
|
||||||
allFieldCoders: allFieldCoders
|
allFieldCoders: allFieldCoders
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -257,6 +260,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
|
|
||||||
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||||
private var customGettersSettersByEntity: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
private var customGettersSettersByEntity: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||||
|
private var customInitializersByEntity: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomInitializer]] = [:]
|
||||||
private var fieldCodersByEntity: [DynamicEntity: [KeyPathString: Internals.AnyFieldCoder]] = [:]
|
private var fieldCodersByEntity: [DynamicEntity: [KeyPathString: Internals.AnyFieldCoder]] = [:]
|
||||||
private weak var cachedRawModel: NSManagedObjectModel?
|
private weak var cachedRawModel: NSManagedObjectModel?
|
||||||
|
|
||||||
@@ -265,11 +269,13 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
initializer: (DynamicEntity, ModelVersion) -> (
|
initializer: (DynamicEntity, ModelVersion) -> (
|
||||||
entity: NSEntityDescription,
|
entity: NSEntityDescription,
|
||||||
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
||||||
|
customInitializersByEntity: [KeyPathString: CoreStoreManagedObject.CustomInitializer],
|
||||||
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
||||||
)
|
)
|
||||||
) -> (
|
) -> (
|
||||||
entity: NSEntityDescription,
|
entity: NSEntityDescription,
|
||||||
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
||||||
|
customInitializerByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomInitializer],
|
||||||
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -278,23 +284,31 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
return (
|
return (
|
||||||
cachedEntityDescription,
|
cachedEntityDescription,
|
||||||
self.customGettersSettersByEntity[entity] ?? [:],
|
self.customGettersSettersByEntity[entity] ?? [:],
|
||||||
|
self.customInitializersByEntity[entity] ?? [:],
|
||||||
self.fieldCodersByEntity[entity] ?? [:]
|
self.fieldCodersByEntity[entity] ?? [:]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let modelVersion = self.modelVersion
|
let modelVersion = self.modelVersion
|
||||||
let (entityDescription, customGetterSetterByKeyPaths, fieldCoders) = withoutActuallyEscaping(
|
let (entityDescription, customGetterSetterByKeyPaths, customInitializerByKeyPaths, fieldCoders) = withoutActuallyEscaping(
|
||||||
initializer,
|
initializer,
|
||||||
do: { $0(entity, modelVersion) }
|
do: { $0(entity, modelVersion) }
|
||||||
)
|
)
|
||||||
self.entityDescriptionsByEntity[entity] = entityDescription
|
self.entityDescriptionsByEntity[entity] = entityDescription
|
||||||
self.customGettersSettersByEntity[entity] = customGetterSetterByKeyPaths
|
self.customGettersSettersByEntity[entity] = customGetterSetterByKeyPaths
|
||||||
|
self.customInitializersByEntity[entity] = customInitializerByKeyPaths
|
||||||
self.fieldCodersByEntity[entity] = fieldCoders
|
self.fieldCodersByEntity[entity] = fieldCoders
|
||||||
return (entityDescription, customGetterSetterByKeyPaths, fieldCoders)
|
return (
|
||||||
|
entityDescription,
|
||||||
|
customGetterSetterByKeyPaths,
|
||||||
|
customInitializerByKeyPaths,
|
||||||
|
fieldCoders
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func firstPassCreateEntityDescription(from entity: DynamicEntity, in modelVersion: ModelVersion) -> (
|
private static func firstPassCreateEntityDescription(from entity: DynamicEntity, in modelVersion: ModelVersion) -> (
|
||||||
entity: NSEntityDescription,
|
entity: NSEntityDescription,
|
||||||
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter],
|
||||||
|
customInitializerByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomInitializer],
|
||||||
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
fieldCoders: [KeyPathString: Internals.AnyFieldCoder]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -306,6 +320,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
entityDescription.managedObjectClassName = CoreStoreManagedObject.cs_subclassName(for: entity, in: modelVersion)
|
entityDescription.managedObjectClassName = CoreStoreManagedObject.cs_subclassName(for: entity, in: modelVersion)
|
||||||
|
|
||||||
var keyPathsByAffectedKeyPaths: [KeyPathString: Set<KeyPathString>] = [:]
|
var keyPathsByAffectedKeyPaths: [KeyPathString: Set<KeyPathString>] = [:]
|
||||||
|
var customInitialValuesByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomInitializer] = [:]
|
||||||
var customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter] = [:]
|
var customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter] = [:]
|
||||||
var fieldCoders: [KeyPathString: Internals.AnyFieldCoder] = [:]
|
var fieldCoders: [KeyPathString: Internals.AnyFieldCoder] = [:]
|
||||||
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
||||||
@@ -338,6 +353,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
|
|
||||||
keyPathsByAffectedKeyPaths[attribute.keyPath] = entityDescriptionValues.affectedByKeyPaths
|
keyPathsByAffectedKeyPaths[attribute.keyPath] = entityDescriptionValues.affectedByKeyPaths
|
||||||
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
|
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
|
||||||
|
customInitialValuesByKeyPaths[attribute.keyPath] = attribute.initializer
|
||||||
fieldCoders[attribute.keyPath] = valueTransformer
|
fieldCoders[attribute.keyPath] = valueTransformer
|
||||||
|
|
||||||
case let relationship as FieldRelationshipProtocol:
|
case let relationship as FieldRelationshipProtocol:
|
||||||
@@ -401,7 +417,12 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
}
|
}
|
||||||
entityDescription.properties = createProperties(for: entity.type as! CoreStoreObject.Type)
|
entityDescription.properties = createProperties(for: entity.type as! CoreStoreObject.Type)
|
||||||
entityDescription.keyPathsByAffectedKeyPaths = keyPathsByAffectedKeyPaths
|
entityDescription.keyPathsByAffectedKeyPaths = keyPathsByAffectedKeyPaths
|
||||||
return (entityDescription, customGetterSetterByKeyPaths, fieldCoders)
|
return (
|
||||||
|
entityDescription,
|
||||||
|
customGetterSetterByKeyPaths,
|
||||||
|
customInitialValuesByKeyPaths,
|
||||||
|
fieldCoders
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func secondPassConnectRelationshipAttributes(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription]) {
|
private static func secondPassConnectRelationshipAttributes(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription]) {
|
||||||
@@ -597,10 +618,15 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
private static func fourthPassSynthesizeManagedObjectClasses(
|
private static func fourthPassSynthesizeManagedObjectClasses(
|
||||||
for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription],
|
for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription],
|
||||||
allCustomGettersSetters: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]],
|
allCustomGettersSetters: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]],
|
||||||
|
allCustomInitializers: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomInitializer]],
|
||||||
allFieldCoders: [DynamicEntity: [KeyPathString: Internals.AnyFieldCoder]]
|
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 superEntity = entityDescription.superentity
|
||||||
let className = entityDescription.managedObjectClassName!
|
let className = entityDescription.managedObjectClassName!
|
||||||
@@ -612,7 +638,8 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
|
|
||||||
createManagedObjectSubclass(
|
createManagedObjectSubclass(
|
||||||
for: superEntity,
|
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
|
let superClass = Internals.with { () -> CoreStoreManagedObject.Type in
|
||||||
@@ -675,40 +702,80 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
swizzle_keyPathsForValuesAffectingValueForKey: do {
|
||||||
let newSelector = NSSelectorFromString("cs_keyPathsForValuesAffectingValueForKey:")
|
|
||||||
let keyPathsByAffectedKeyPaths = entityDescription.keyPathsByAffectedKeyPaths
|
let newSelector = NSSelectorFromString("cs_keyPathsForValuesAffectingValueForKey:")
|
||||||
let keyPathsForValuesAffectingValue: @convention(block) (Any, String) -> Set<String> = { (instance, keyPath) in
|
let keyPathsByAffectedKeyPaths = entityDescription.keyPathsByAffectedKeyPaths
|
||||||
|
let keyPathsForValuesAffectingValue: @convention(block) (Any, String) -> Set<String> = { (instance, keyPath) in
|
||||||
if let keyPaths = keyPathsByAffectedKeyPaths[keyPath] {
|
|
||||||
|
|
||||||
return keyPaths
|
if let keyPaths = keyPathsByAffectedKeyPaths[keyPath] {
|
||||||
|
|
||||||
|
return keyPaths
|
||||||
|
}
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
return []
|
let origSelector = #selector(CoreStoreManagedObject.keyPathsForValuesAffectingValue(forKey:))
|
||||||
}
|
|
||||||
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)) {
|
|
||||||
|
|
||||||
class_replaceMethod(metaClass, newSelector, origImp, method_getTypeEncoding(origMethod))
|
let metaClass: AnyClass = object_getClass(managedObjectClass)!
|
||||||
}
|
let origMethod = class_getClassMethod(managedObjectClass, origSelector)!
|
||||||
else {
|
|
||||||
|
|
||||||
let newMethod = class_getClassMethod(managedObjectClass, newSelector)!
|
let origImp = method_getImplementation(origMethod)
|
||||||
method_exchangeImplementations(origMethod, newMethod)
|
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 {
|
for (dynamicEntity, entityDescription) in entityDescriptionsByEntity {
|
||||||
|
|
||||||
createManagedObjectSubclass(
|
createManagedObjectSubclass(
|
||||||
for: entityDescription,
|
for: entityDescription,
|
||||||
customGetterSetterByKeyPaths: allCustomGettersSetters[dynamicEntity]
|
customGetterSetterByKeyPaths: allCustomGettersSetters[dynamicEntity],
|
||||||
|
customInitializers: allCustomInitializers[dynamicEntity]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,32 @@ extension FieldContainer {
|
|||||||
valueTransformer: { Internals.AnyFieldCoder(fieldCoderType) },
|
valueTransformer: { Internals.AnyFieldCoder(fieldCoderType) },
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter,
|
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
|
affectedByKeyPaths: affectedByKeyPaths
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -142,6 +168,32 @@ extension FieldContainer {
|
|||||||
valueTransformer: { Internals.AnyFieldCoder(tag: UUID(), encode: coder.encode, decode: coder.decode) },
|
valueTransformer: { Internals.AnyFieldCoder(tag: UUID(), encode: coder.encode, decode: coder.decode) },
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter,
|
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
|
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
|
// MARK: FilePrivate
|
||||||
|
|
||||||
fileprivate init(
|
fileprivate init(
|
||||||
defaultValue: @escaping () -> Any?,
|
defaultValue: (() -> Any?)?,
|
||||||
keyPath: KeyPathString,
|
keyPath: KeyPathString,
|
||||||
isOptional: Bool,
|
isOptional: Bool,
|
||||||
versionHashModifier: @escaping () -> String?,
|
versionHashModifier: @escaping () -> String?,
|
||||||
@@ -346,6 +415,7 @@ extension FieldContainer {
|
|||||||
valueTransformer: @escaping () -> Internals.AnyFieldCoder?,
|
valueTransformer: @escaping () -> Internals.AnyFieldCoder?,
|
||||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?,
|
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>) {
|
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
@@ -361,14 +431,17 @@ extension FieldContainer {
|
|||||||
renamingIdentifier: renamingIdentifier(),
|
renamingIdentifier: renamingIdentifier(),
|
||||||
valueTransformer: fieldCoder,
|
valueTransformer: fieldCoder,
|
||||||
affectedByKeyPaths: affectedByKeyPaths(),
|
affectedByKeyPaths: affectedByKeyPaths(),
|
||||||
defaultValue: Internals.AnyFieldCoder.TransformableDefaultValueCodingBox(
|
defaultValue: defaultValue.map {
|
||||||
defaultValue: defaultValue(),
|
Internals.AnyFieldCoder.TransformableDefaultValueCodingBox(
|
||||||
fieldCoder: fieldCoder
|
defaultValue: $0(),
|
||||||
)
|
fieldCoder: fieldCoder
|
||||||
|
) as Any
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
self.customGetter = customGetter
|
self.customGetter = customGetter
|
||||||
self.customSetter = customSetter
|
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 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 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) },
|
valueTransformer: { Internals.AnyFieldCoder(coder) },
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter,
|
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
|
affectedByKeyPaths: affectedByKeyPaths
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -470,6 +570,32 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
|||||||
valueTransformer: { Internals.AnyFieldCoder(tag: UUID(), encode: coder.encode, decode: coder.decode) },
|
valueTransformer: { Internals.AnyFieldCoder(tag: UUID(), encode: coder.encode, decode: coder.decode) },
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter,
|
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
|
affectedByKeyPaths: affectedByKeyPaths
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -516,6 +642,31 @@ extension FieldContainer.Coded where V: DefaultNSSecureCodable {
|
|||||||
valueTransformer: { Internals.AnyFieldCoder(FieldCoders.DefaultNSSecureCoding<V>.self) },
|
valueTransformer: { Internals.AnyFieldCoder(FieldCoders.DefaultNSSecureCoding<V>.self) },
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter,
|
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
|
affectedByKeyPaths: affectedByKeyPaths
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -562,6 +713,31 @@ extension FieldContainer.Coded where V: FieldOptionalType, V.Wrapped: DefaultNSS
|
|||||||
valueTransformer: { Internals.AnyFieldCoder(FieldCoders.DefaultNSSecureCoding<V.Wrapped>.self) },
|
valueTransformer: { Internals.AnyFieldCoder(FieldCoders.DefaultNSSecureCoding<V.Wrapped>.self) },
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter,
|
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
|
affectedByKeyPaths: affectedByKeyPaths
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,30 @@ extension FieldContainer {
|
|||||||
renamingIdentifier: previousVersionKeyPath,
|
renamingIdentifier: previousVersionKeyPath,
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter,
|
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
|
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
|
// MARK: FilePrivate
|
||||||
|
|
||||||
fileprivate init(
|
fileprivate init(
|
||||||
wrappedValue initial: @escaping () -> V,
|
wrappedValue initial: (() -> V)?,
|
||||||
keyPath: KeyPathString,
|
keyPath: KeyPathString,
|
||||||
isOptional: Bool,
|
isOptional: Bool,
|
||||||
versionHashModifier: @escaping () -> String?,
|
versionHashModifier: @escaping () -> String?,
|
||||||
renamingIdentifier: @escaping () -> String?,
|
renamingIdentifier: @escaping () -> String?,
|
||||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)?,
|
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>) {
|
affectedByKeyPaths: @escaping () -> Set<KeyPathString>) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
@@ -287,11 +329,12 @@ extension FieldContainer {
|
|||||||
renamingIdentifier: renamingIdentifier(),
|
renamingIdentifier: renamingIdentifier(),
|
||||||
valueTransformer: nil,
|
valueTransformer: nil,
|
||||||
affectedByKeyPaths: affectedByKeyPaths(),
|
affectedByKeyPaths: affectedByKeyPaths(),
|
||||||
defaultValue: initial().cs_toFieldStoredNativeType()
|
defaultValue: initial?().cs_toFieldStoredNativeType()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
self.customGetter = customGetter
|
self.customGetter = customGetter
|
||||||
self.customSetter = customSetter
|
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 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 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,
|
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||||
customGetter: ((_ object: ObjectProxy<O>, _ field: ObjectProxy<O>.FieldProxy<V>) -> V)? = 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,
|
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(
|
||||||
wrappedValue: initial,
|
wrappedValue: initial,
|
||||||
@@ -341,6 +386,30 @@ extension FieldContainer.Stored where V: FieldOptionalType {
|
|||||||
renamingIdentifier: previousVersionKeyPath,
|
renamingIdentifier: previousVersionKeyPath,
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter,
|
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
|
affectedByKeyPaths: affectedByKeyPaths
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,6 +275,8 @@ extension FieldContainer {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let initializer: CoreStoreManagedObject.CustomInitializer? = nil
|
||||||
|
|
||||||
|
|
||||||
// MARK: FilePrivate
|
// MARK: FilePrivate
|
||||||
|
|||||||
@@ -48,4 +48,5 @@ internal protocol FieldAttributeProtocol: FieldProtocol {
|
|||||||
var entityDescriptionValues: () -> EntityDescriptionValues { get }
|
var entityDescriptionValues: () -> EntityDescriptionValues { get }
|
||||||
var getter: CoreStoreManagedObject.CustomGetter? { get }
|
var getter: CoreStoreManagedObject.CustomGetter? { get }
|
||||||
var setter: CoreStoreManagedObject.CustomSetter? { 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)])
|
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
|
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
|
// MARK: Private
|
||||||
|
|
||||||
@@ -151,7 +130,6 @@ extension NSEntityDescription {
|
|||||||
fileprivate static let CoreStoreManagedObjectUniqueConstraints = "CoreStoreManagedObjectUniqueConstraints"
|
fileprivate static let CoreStoreManagedObjectUniqueConstraints = "CoreStoreManagedObjectUniqueConstraints"
|
||||||
|
|
||||||
fileprivate static let CoreStoreManagedObjectKeyPathsByAffectedKeyPaths = "CoreStoreManagedObjectKeyPathsByAffectedKeyPaths"
|
fileprivate static let CoreStoreManagedObjectKeyPathsByAffectedKeyPaths = "CoreStoreManagedObjectKeyPathsByAffectedKeyPaths"
|
||||||
fileprivate static let CoreStoreManagedObjectCustomGetterSetterByKeyPaths = "CoreStoreManagedObjectCustomGetterSetterByKeyPaths"
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user