mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-27 11:51:31 +01:00
Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "CoreStore"
|
s.name = "CoreStore"
|
||||||
s.version = "4.0.3"
|
s.version = "4.0.4"
|
||||||
s.license = "MIT"
|
s.license = "MIT"
|
||||||
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
|
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
|
||||||
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
||||||
|
|||||||
@@ -51,15 +51,49 @@ class Dog: Animal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Person: CoreStoreObject {
|
class Person: CoreStoreObject {
|
||||||
let title = Value.Required<String>("title", default: "Mr.")
|
let title = Value.Required<String>(
|
||||||
let name = Value.Required<String>(
|
"title",
|
||||||
"name",
|
default: "Mr.",
|
||||||
customGetter: { (`self`, getValue) in
|
customSetter: { (`self`, setValue, originalNewValue) in
|
||||||
|
|
||||||
return "\(self.title.value) \(getValue())"
|
setValue(originalNewValue)
|
||||||
|
self.displayName .= nil
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
let name = Value.Required<String>(
|
||||||
|
"name",
|
||||||
|
customSetter: { (`self`, setValue, originalNewValue) in
|
||||||
|
|
||||||
|
setValue(originalNewValue)
|
||||||
|
self.displayName .= nil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
let displayName = Value.Optional<String>(
|
||||||
|
"displayName",
|
||||||
|
isTransient: true,
|
||||||
|
customGetter: Person.cachedDisplayName(_:_:),
|
||||||
|
affectedByKeyPaths: Person.keyPathsAffectingDisplayName()
|
||||||
|
)
|
||||||
let pets = Relationship.ToManyUnordered<Animal>("pets", inverse: { $0.master })
|
let pets = Relationship.ToManyUnordered<Animal>("pets", inverse: { $0.master })
|
||||||
|
|
||||||
|
static func cachedDisplayName(_ instance: Person, _ getValue: () -> String?) -> String? {
|
||||||
|
|
||||||
|
if let cached = getValue() {
|
||||||
|
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
let primitiveValue = "\(instance.title.value) \(instance.name.value)"
|
||||||
|
instance.displayName .= primitiveValue
|
||||||
|
return primitiveValue
|
||||||
|
}
|
||||||
|
|
||||||
|
static func keyPathsAffectingDisplayName() -> Set<String> {
|
||||||
|
|
||||||
|
return [
|
||||||
|
self.keyPath({ $0.title }),
|
||||||
|
self.keyPath({ $0.name })
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -124,11 +158,25 @@ class DynamicModelTests: BaseTestDataTestCase {
|
|||||||
let person = transaction.create(Into<Person>())
|
let person = transaction.create(Into<Person>())
|
||||||
XCTAssertTrue(person.pets.value.isEmpty)
|
XCTAssertTrue(person.pets.value.isEmpty)
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
object_getClass(person.rawObject!).keyPathsForValuesAffectingValue(forKey: "displayName"),
|
||||||
|
["title", "name"]
|
||||||
|
)
|
||||||
|
|
||||||
|
person.name .= "Joe"
|
||||||
|
|
||||||
|
XCTAssertEqual(person.rawObject!.value(forKey: "name") as! String?, "Joe")
|
||||||
|
XCTAssertEqual(person.rawObject!.value(forKey: "displayName") as! String?, "Mr. Joe")
|
||||||
|
|
||||||
|
person.rawObject!.setValue("AAAA", forKey: "displayName")
|
||||||
|
XCTAssertEqual(person.rawObject!.value(forKey: "displayName") as! String?, "AAAA")
|
||||||
|
|
||||||
person.name .= "John"
|
person.name .= "John"
|
||||||
XCTAssertEqual(person.name.value, "Mr. John") // Custom getter
|
XCTAssertEqual(person.name.value, "John")
|
||||||
|
XCTAssertEqual(person.displayName.value, "Mr. John") // Custom getter
|
||||||
|
|
||||||
person.title .= "Sir"
|
person.title .= "Sir"
|
||||||
XCTAssertEqual(person.name.value, "Sir John")
|
XCTAssertEqual(person.displayName.value, "Sir John")
|
||||||
|
|
||||||
person.pets.value.insert(dog)
|
person.pets.value.insert(dog)
|
||||||
XCTAssertEqual(person.pets.count, 1)
|
XCTAssertEqual(person.pets.count, 1)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ Unleashing the real power of Core Data with the elegance and safety of Swift
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
* **Swift 3.1:** iOS 8+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+
|
* **Swift 3.1:** iOS 8+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+
|
||||||
|
* Beta support: [Swift 3.2](https://github.com/JohnEstropia/CoreStore/tree/prototype/Swift_3_2), [Swift 4.0](https://github.com/JohnEstropia/CoreStore/tree/prototype/Swift_4_0)
|
||||||
|
|
||||||
Upgrading from CoreStore 3.x to 4.x? Check out the [new features](#features) and make sure to read the [Migration guide](#upgrading-from-3xx-to-4xx).
|
Upgrading from CoreStore 3.x to 4.x? Check out the [new features](#features) and make sure to read the [Migration guide](#upgrading-from-3xx-to-4xx).
|
||||||
|
|
||||||
|
|||||||
@@ -13,28 +13,14 @@ import CoreData
|
|||||||
|
|
||||||
@objc internal class CoreStoreManagedObject: NSManagedObject {
|
@objc internal class CoreStoreManagedObject: NSManagedObject {
|
||||||
|
|
||||||
@nonobjc
|
internal typealias CustomGetter = @convention(block) (_ rawObject: Any) -> Any?
|
||||||
internal class func cs_setKeyPathsForValuesAffectingKeys(_ keyPathsForValuesAffectingKeys: [RawKeyPath: Set<RawKeyPath>], for managedObjectClass: CoreStoreManagedObject.Type) {
|
internal typealias CustomSetter = @convention(block) (_ rawObject: Any, _ newValue: Any?) -> Void
|
||||||
|
internal typealias CustomGetterSetter = (getter: CustomGetter?, setter: CustomSetter?)
|
||||||
|
|
||||||
Static.queue.sync(flags: .barrier) {
|
@nonobjc @inline(__always)
|
||||||
|
internal static func cs_subclassName(for entity: DynamicEntity, in modelVersion: ModelVersion) -> String {
|
||||||
|
|
||||||
Static.cache[ObjectIdentifier(managedObjectClass)] = keyPathsForValuesAffectingKeys
|
return "_\(NSStringFromClass(CoreStoreManagedObject.self))__\(modelVersion)__\(NSStringFromClass(entity.type))__\(entity.entityName)"
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: NSManagedObject
|
|
||||||
|
|
||||||
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
|
|
||||||
|
|
||||||
return Static.queue.sync(flags: .barrier) {
|
|
||||||
|
|
||||||
let cacheKey = ObjectIdentifier(self)
|
|
||||||
if let keyPathsForValuesAffectingKeys = Static.cache[cacheKey] {
|
|
||||||
|
|
||||||
return keyPathsForValuesAffectingKeys[key] ?? []
|
|
||||||
}
|
|
||||||
return super.keyPathsForValuesAffectingValue(forKey: key)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
|||||||
public required init(rawObject: NSManagedObject) {
|
public required init(rawObject: NSManagedObject) {
|
||||||
|
|
||||||
self.isMeta = false
|
self.isMeta = false
|
||||||
self.rawObject = rawObject
|
self.rawObject = (rawObject as! CoreStoreManagedObject)
|
||||||
self.initializeAttributes(Mirror(reflecting: self), { [unowned self] in self })
|
self.initializeAttributes(Mirror(reflecting: self), self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,13 +110,13 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal let rawObject: NSManagedObject?
|
internal let rawObject: CoreStoreManagedObject?
|
||||||
internal let isMeta: Bool
|
internal let isMeta: Bool
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private func initializeAttributes(_ mirror: Mirror, _ parentObject: @escaping () -> CoreStoreObject) {
|
private func initializeAttributes(_ mirror: Mirror, _ parentObject: CoreStoreObject) {
|
||||||
|
|
||||||
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, parentObject) })
|
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, parentObject) })
|
||||||
for child in mirror.children {
|
for child in mirror.children {
|
||||||
|
|||||||
@@ -208,17 +208,22 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
}
|
}
|
||||||
let rawModel = NSManagedObjectModel()
|
let rawModel = NSManagedObjectModel()
|
||||||
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||||
|
var allCustomGettersSetters: [DynamicEntity: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||||
for entity in self.allEntities {
|
for entity in self.allEntities {
|
||||||
|
|
||||||
let entityDescription = self.entityDescription(
|
let (entityDescription, customGetterSetterByKeyPaths) = self.entityDescription(
|
||||||
for: entity,
|
for: entity,
|
||||||
initializer: CoreStoreSchema.firstPassCreateEntityDescription
|
initializer: CoreStoreSchema.firstPassCreateEntityDescription(from:in:)
|
||||||
)
|
)
|
||||||
entityDescriptionsByEntity[entity] = (entityDescription.copy() as! NSEntityDescription)
|
entityDescriptionsByEntity[entity] = (entityDescription.copy() as! NSEntityDescription)
|
||||||
|
allCustomGettersSetters[entity] = customGetterSetterByKeyPaths
|
||||||
}
|
}
|
||||||
CoreStoreSchema.secondPassConnectRelationshipAttributes(for: entityDescriptionsByEntity)
|
CoreStoreSchema.secondPassConnectRelationshipAttributes(for: entityDescriptionsByEntity)
|
||||||
CoreStoreSchema.thirdPassConnectInheritanceTree(for: entityDescriptionsByEntity)
|
CoreStoreSchema.thirdPassConnectInheritanceTree(for: entityDescriptionsByEntity)
|
||||||
CoreStoreSchema.fourthPassSynthesizeManagedObjectClasses(for: entityDescriptionsByEntity)
|
CoreStoreSchema.fourthPassSynthesizeManagedObjectClasses(
|
||||||
|
for: entityDescriptionsByEntity,
|
||||||
|
allCustomGettersSetters: allCustomGettersSetters
|
||||||
|
)
|
||||||
|
|
||||||
rawModel.entities = entityDescriptionsByEntity.values.sorted(by: { $0.name! < $1.name! })
|
rawModel.entities = entityDescriptionsByEntity.values.sorted(by: { $0.name! < $1.name! })
|
||||||
for (configuration, entities) in self.entitiesByConfiguration {
|
for (configuration, entities) in self.entitiesByConfiguration {
|
||||||
@@ -248,29 +253,33 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
private let allEntities: Set<DynamicEntity>
|
private let allEntities: Set<DynamicEntity>
|
||||||
|
|
||||||
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||||
|
private var customGettersSettersByEntity: [DynamicEntity: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||||
private weak var cachedRawModel: NSManagedObjectModel?
|
private weak var cachedRawModel: NSManagedObjectModel?
|
||||||
|
|
||||||
private func entityDescription(for entity: DynamicEntity, initializer: (DynamicEntity) -> NSEntityDescription) -> NSEntityDescription {
|
private func entityDescription(for entity: DynamicEntity, initializer: (DynamicEntity, ModelVersion) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter])) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]) {
|
||||||
|
|
||||||
if let cachedEntityDescription = self.entityDescriptionsByEntity[entity] {
|
if let cachedEntityDescription = self.entityDescriptionsByEntity[entity] {
|
||||||
|
|
||||||
return cachedEntityDescription
|
return (cachedEntityDescription, self.customGettersSettersByEntity[entity] ?? [:])
|
||||||
}
|
}
|
||||||
let entityDescription = withoutActuallyEscaping(initializer, do: { $0(entity) })
|
let modelVersion = self.modelVersion
|
||||||
|
let (entityDescription, customGetterSetterByKeyPaths) = withoutActuallyEscaping(initializer, do: { $0(entity, modelVersion) })
|
||||||
self.entityDescriptionsByEntity[entity] = entityDescription
|
self.entityDescriptionsByEntity[entity] = entityDescription
|
||||||
return entityDescription
|
self.customGettersSettersByEntity[entity] = customGetterSetterByKeyPaths
|
||||||
|
return (entityDescription, customGetterSetterByKeyPaths)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func firstPassCreateEntityDescription(from entity: DynamicEntity) -> NSEntityDescription {
|
private static func firstPassCreateEntityDescription(from entity: DynamicEntity, in modelVersion: ModelVersion) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]) {
|
||||||
|
|
||||||
let entityDescription = NSEntityDescription()
|
let entityDescription = NSEntityDescription()
|
||||||
entityDescription.coreStoreEntity = entity
|
entityDescription.coreStoreEntity = entity
|
||||||
entityDescription.name = entity.entityName
|
entityDescription.name = entity.entityName
|
||||||
entityDescription.isAbstract = entity.isAbstract
|
entityDescription.isAbstract = entity.isAbstract
|
||||||
entityDescription.versionHashModifier = entity.versionHashModifier
|
entityDescription.versionHashModifier = entity.versionHashModifier
|
||||||
entityDescription.managedObjectClassName = "\(NSStringFromClass(CoreStoreManagedObject.self)).\(NSStringFromClass(entity.type)).\(entity.entityName)"
|
entityDescription.managedObjectClassName = CoreStoreManagedObject.cs_subclassName(for: entity, in: modelVersion)
|
||||||
|
|
||||||
var keyPathsByAffectedKeyPaths: [RawKeyPath: Set<RawKeyPath>] = [:]
|
var keyPathsByAffectedKeyPaths: [RawKeyPath: Set<RawKeyPath>] = [:]
|
||||||
|
var customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter] = [:]
|
||||||
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
||||||
|
|
||||||
var propertyDescriptions: [NSPropertyDescription] = []
|
var propertyDescriptions: [NSPropertyDescription] = []
|
||||||
@@ -284,12 +293,13 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
description.attributeType = Swift.type(of: attribute).attributeType
|
description.attributeType = Swift.type(of: attribute).attributeType
|
||||||
description.isOptional = attribute.isOptional
|
description.isOptional = attribute.isOptional
|
||||||
description.isIndexed = attribute.isIndexed
|
description.isIndexed = attribute.isIndexed
|
||||||
description.defaultValue = attribute.defaultValue
|
description.defaultValue = attribute.defaultValue()
|
||||||
description.isTransient = attribute.isTransient
|
description.isTransient = attribute.isTransient
|
||||||
description.versionHashModifier = attribute.versionHashModifier
|
description.versionHashModifier = attribute.versionHashModifier
|
||||||
description.renamingIdentifier = attribute.renamingIdentifier
|
description.renamingIdentifier = attribute.renamingIdentifier
|
||||||
propertyDescriptions.append(description)
|
propertyDescriptions.append(description)
|
||||||
keyPathsByAffectedKeyPaths[attribute.keyPath] = attribute.affectedByKeyPaths()
|
keyPathsByAffectedKeyPaths[attribute.keyPath] = attribute.affectedByKeyPaths()
|
||||||
|
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
|
||||||
|
|
||||||
case let relationship as RelationshipProtocol:
|
case let relationship as RelationshipProtocol:
|
||||||
let description = NSRelationshipDescription()
|
let description = NSRelationshipDescription()
|
||||||
@@ -309,9 +319,9 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
}
|
}
|
||||||
return propertyDescriptions
|
return propertyDescriptions
|
||||||
}
|
}
|
||||||
entityDescription.keyPathsByAffectedKeyPaths = keyPathsByAffectedKeyPaths
|
|
||||||
entityDescription.properties = createProperties(for: entity.type as! CoreStoreObject.Type)
|
entityDescription.properties = createProperties(for: entity.type as! CoreStoreObject.Type)
|
||||||
return entityDescription
|
entityDescription.keyPathsByAffectedKeyPaths = keyPathsByAffectedKeyPaths
|
||||||
|
return (entityDescription, customGetterSetterByKeyPaths)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func secondPassConnectRelationshipAttributes(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription]) {
|
private static func secondPassConnectRelationshipAttributes(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription]) {
|
||||||
@@ -433,9 +443,9 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func fourthPassSynthesizeManagedObjectClasses(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription]) {
|
private static func fourthPassSynthesizeManagedObjectClasses(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription], allCustomGettersSetters: [DynamicEntity: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]]) {
|
||||||
|
|
||||||
func createManagedObjectSubclass(for entityDescription: NSEntityDescription) {
|
func createManagedObjectSubclass(for entityDescription: NSEntityDescription, customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]?) {
|
||||||
|
|
||||||
let superEntity = entityDescription.superentity
|
let superEntity = entityDescription.superentity
|
||||||
let className = entityDescription.managedObjectClassName!
|
let className = entityDescription.managedObjectClassName!
|
||||||
@@ -445,7 +455,10 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
}
|
}
|
||||||
if let superEntity = superEntity {
|
if let superEntity = superEntity {
|
||||||
|
|
||||||
createManagedObjectSubclass(for: superEntity)
|
createManagedObjectSubclass(
|
||||||
|
for: superEntity,
|
||||||
|
customGetterSetterByKeyPaths: superEntity.coreStoreEntity.flatMap({ allCustomGettersSetters[$0] })
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let superClass = cs_lazy { () -> CoreStoreManagedObject.Type in
|
let superClass = cs_lazy { () -> CoreStoreManagedObject.Type in
|
||||||
|
|
||||||
@@ -456,19 +469,85 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
}
|
}
|
||||||
return CoreStoreManagedObject.self
|
return CoreStoreManagedObject.self
|
||||||
}
|
}
|
||||||
let managedObjectClass = className.withCString {
|
let managedObjectClass: AnyClass = className.withCString {
|
||||||
|
|
||||||
return objc_allocateClassPair(superClass, $0, 0) as! CoreStoreManagedObject.Type
|
return objc_allocateClassPair(superClass, $0, 0)!
|
||||||
}
|
}
|
||||||
objc_registerClassPair(managedObjectClass)
|
defer {
|
||||||
managedObjectClass.cs_setKeyPathsForValuesAffectingKeys(
|
|
||||||
entityDescription.keyPathsByAffectedKeyPaths,
|
|
||||||
for: managedObjectClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for (_, entityDescription) in entityDescriptionsByEntity {
|
|
||||||
|
|
||||||
createManagedObjectSubclass(for: entityDescription)
|
objc_registerClassPair(managedObjectClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
func capitalize(_ string: String) -> String {
|
||||||
|
|
||||||
|
return string.replacingCharacters(
|
||||||
|
in: Range(uncheckedBounds: (string.startIndex, string.index(after: string.startIndex))),
|
||||||
|
with: String(string[string.startIndex]).uppercased()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (attributeName, customGetterSetters) in (customGetterSetterByKeyPaths ?? [:])
|
||||||
|
where customGetterSetters.getter != nil || customGetterSetters.setter != nil {
|
||||||
|
|
||||||
|
if let getter = customGetterSetters.getter {
|
||||||
|
|
||||||
|
let getterName = "\(attributeName)"
|
||||||
|
guard class_addMethod(
|
||||||
|
managedObjectClass,
|
||||||
|
NSSelectorFromString(getterName),
|
||||||
|
imp_implementationWithBlock(getter),
|
||||||
|
"@@:") else {
|
||||||
|
|
||||||
|
CoreStore.abort("Could not dynamically add getter method \"\(getterName)\" to class \(cs_typeName(managedObjectClass))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let setter = customGetterSetters.setter {
|
||||||
|
|
||||||
|
let setterName = "set\(capitalize(attributeName)):"
|
||||||
|
guard class_addMethod(
|
||||||
|
managedObjectClass,
|
||||||
|
NSSelectorFromString(setterName),
|
||||||
|
imp_implementationWithBlock(setter),
|
||||||
|
"v@:@") else {
|
||||||
|
|
||||||
|
CoreStore.abort("Could not dynamically add setter method \"\(setterName)\" to class \(cs_typeName(managedObjectClass))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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] {
|
||||||
|
|
||||||
|
return keyPaths
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let origSelector = #selector(NSManagedObject.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))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
let newMethod = class_getClassMethod(managedObjectClass, newSelector)
|
||||||
|
method_exchangeImplementations(origMethod, newMethod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (dynamicEntity, entityDescription) in entityDescriptionsByEntity {
|
||||||
|
|
||||||
|
createManagedObjectSubclass(
|
||||||
|
for: entityDescription,
|
||||||
|
customGetterSetterByKeyPaths: allCustomGettersSetters[dynamicEntity]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.0.3</string>
|
<string>4.0.4</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
|
|||||||
@@ -75,14 +75,15 @@ internal extension NSEntityDescription {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@nonobjc
|
||||||
internal var keyPathsByAffectedKeyPaths: [RawKeyPath: Set<RawKeyPath>] {
|
internal var keyPathsByAffectedKeyPaths: [RawKeyPath: Set<RawKeyPath>] {
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
if let userInfo = self.userInfo,
|
if let userInfo = self.userInfo,
|
||||||
let function = userInfo[UserInfoKey.CoreStoreManagedObjectKeyPathsByAffectedKeyPaths] as! [RawKeyPath: Set<RawKeyPath>]? {
|
let value = userInfo[UserInfoKey.CoreStoreManagedObjectKeyPathsByAffectedKeyPaths] {
|
||||||
|
|
||||||
return function
|
return value as! [RawKeyPath: Set<RawKeyPath>]
|
||||||
}
|
}
|
||||||
return [:]
|
return [:]
|
||||||
}
|
}
|
||||||
@@ -95,6 +96,27 @@ internal extension NSEntityDescription {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@nonobjc
|
||||||
|
internal var customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter] {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
|
if let userInfo = self.userInfo,
|
||||||
|
let value = userInfo[UserInfoKey.CoreStoreManagedObjectCustomGetterSetterByKeyPaths] {
|
||||||
|
|
||||||
|
return value as! [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]
|
||||||
|
}
|
||||||
|
return [:]
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
|
||||||
|
cs_setUserInfo { (userInfo) in
|
||||||
|
|
||||||
|
userInfo[UserInfoKey.CoreStoreManagedObjectCustomGetterSetterByKeyPaths] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
@@ -108,6 +130,8 @@ internal extension NSEntityDescription {
|
|||||||
fileprivate static let CoreStoreManagedObjectVersionHashModifier = "CoreStoreManagedObjectVersionHashModifier"
|
fileprivate static let CoreStoreManagedObjectVersionHashModifier = "CoreStoreManagedObjectVersionHashModifier"
|
||||||
|
|
||||||
fileprivate static let CoreStoreManagedObjectKeyPathsByAffectedKeyPaths = "CoreStoreManagedObjectKeyPathsByAffectedKeyPaths"
|
fileprivate static let CoreStoreManagedObjectKeyPathsByAffectedKeyPaths = "CoreStoreManagedObjectKeyPathsByAffectedKeyPaths"
|
||||||
|
fileprivate static let CoreStoreManagedObjectCustomGetterSetterByKeyPaths = "CoreStoreManagedObjectCustomGetterSetterByKeyPaths"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func cs_setUserInfo(_ closure: (_ userInfo: inout [AnyHashable: Any]) -> Void) {
|
private func cs_setUserInfo(_ closure: (_ userInfo: inout [AnyHashable: Any]) -> Void) {
|
||||||
|
|||||||
@@ -245,41 +245,49 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
|||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
internal let affectedByKeyPaths: () -> Set<String>
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
|
internal weak var parentObject: CoreStoreObject?
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
|
||||||
|
|
||||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal var nativeValue: NSManagedObject? {
|
internal var nativeValue: NSManagedObject? {
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
return object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { $0 as! NSManagedObject? }
|
|
||||||
)
|
)
|
||||||
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
|
)
|
||||||
|
return object.rawObject!.getValue(
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
didGetValue: { $0 as! NSManagedObject? }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
CoreStore.assert(
|
|
||||||
object.rawObject!.isEditableInContext() == true,
|
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
|
||||||
)
|
|
||||||
object.rawObject!.setValue(
|
|
||||||
newValue,
|
|
||||||
forKvcKey: self.keyPath
|
|
||||||
)
|
)
|
||||||
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isEditableInContext() == true,
|
||||||
|
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||||
|
)
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKvcKey: self.keyPath
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,41 +514,49 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
|||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
internal let affectedByKeyPaths: () -> Set<String>
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
|
internal weak var parentObject: CoreStoreObject?
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
|
||||||
|
|
||||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal var nativeValue: NSOrderedSet {
|
internal var nativeValue: NSOrderedSet {
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
return object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { ($0 as! NSOrderedSet?) ?? [] }
|
|
||||||
)
|
)
|
||||||
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
|
)
|
||||||
|
return object.rawObject!.getValue(
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
didGetValue: { ($0 as! NSOrderedSet?) ?? [] }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
CoreStore.assert(
|
|
||||||
object.rawObject!.isEditableInContext() == true,
|
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
|
||||||
)
|
|
||||||
object.rawObject!.setValue(
|
|
||||||
newValue,
|
|
||||||
forKvcKey: self.keyPath
|
|
||||||
)
|
)
|
||||||
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isEditableInContext() == true,
|
||||||
|
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||||
|
)
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKvcKey: self.keyPath
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,41 +788,49 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
|||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
internal let affectedByKeyPaths: () -> Set<String>
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
|
internal weak var parentObject: CoreStoreObject?
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
|
||||||
|
|
||||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal var nativeValue: NSSet {
|
internal var nativeValue: NSSet {
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
return object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { ($0 as! NSSet?) ?? [] }
|
|
||||||
)
|
)
|
||||||
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
|
)
|
||||||
|
return object.rawObject!.getValue(
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
didGetValue: { ($0 as! NSSet?) ?? [] }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
CoreStore.assert(
|
|
||||||
object.rawObject!.isEditableInContext() == true,
|
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
|
||||||
)
|
|
||||||
object.rawObject!.setValue(
|
|
||||||
newValue,
|
|
||||||
forKvcKey: self.keyPath
|
|
||||||
)
|
)
|
||||||
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isEditableInContext() == true,
|
||||||
|
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||||
|
)
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKvcKey: self.keyPath
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1192,7 +1216,7 @@ internal protocol RelationshipProtocol: class {
|
|||||||
var deleteRule: NSDeleteRule { get }
|
var deleteRule: NSDeleteRule { get }
|
||||||
var inverse: (type: CoreStoreObject.Type, keyPath: () -> RawKeyPath?) { get }
|
var inverse: (type: CoreStoreObject.Type, keyPath: () -> RawKeyPath?) { get }
|
||||||
var affectedByKeyPaths: () -> Set<String> { get }
|
var affectedByKeyPaths: () -> Set<String> { get }
|
||||||
var parentObject: () -> CoreStoreObject { get set }
|
weak var parentObject: CoreStoreObject? { get set }
|
||||||
var versionHashModifier: String? { get }
|
var versionHashModifier: String? { get }
|
||||||
var renamingIdentifier: String? { get }
|
var renamingIdentifier: String? { get }
|
||||||
var minCount: Int { get }
|
var minCount: Int { get }
|
||||||
|
|||||||
@@ -118,19 +118,19 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
*/
|
*/
|
||||||
public init(
|
public init(
|
||||||
_ keyPath: RawKeyPath,
|
_ keyPath: RawKeyPath,
|
||||||
`default`: V,
|
`default`: @autoclosure @escaping () -> V,
|
||||||
isIndexed: Bool = false,
|
isIndexed: Bool = false,
|
||||||
isTransient: Bool = false,
|
isTransient: Bool = false,
|
||||||
versionHashModifier: String? = nil,
|
versionHashModifier: String? = nil,
|
||||||
renamingIdentifier: String? = nil,
|
renamingIdentifier: String? = nil,
|
||||||
customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() },
|
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
|
||||||
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) },
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.isIndexed = isIndexed
|
self.isIndexed = isIndexed
|
||||||
self.isTransient = isTransient
|
self.isTransient = isTransient
|
||||||
self.defaultValue = `default`.cs_toQueryableNativeType()
|
self.defaultValue = { `default`().cs_toQueryableNativeType() }
|
||||||
self.versionHashModifier = versionHashModifier
|
self.versionHashModifier = versionHashModifier
|
||||||
self.renamingIdentifier = renamingIdentifier
|
self.renamingIdentifier = renamingIdentifier
|
||||||
self.customGetter = customGetter
|
self.customGetter = customGetter
|
||||||
@@ -145,45 +145,59 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
return self.customGetter(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object,
|
|
||||||
{ () -> V in
|
|
||||||
|
|
||||||
return object.rawObject!.getValue(
|
CoreStore.assert(
|
||||||
forKvcKey: self.keyPath,
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
didGetValue: { V.cs_fromQueryableNativeType($0 as! V.QueryableNativeType)! }
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
)
|
)
|
||||||
}
|
let customGetter = (self.customGetter ?? { $1() })
|
||||||
)
|
return customGetter(
|
||||||
|
object,
|
||||||
|
{ () -> V in
|
||||||
|
|
||||||
|
return object.rawObject!.getValue(
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
didGetValue: { V.cs_fromQueryableNativeType($0 as! V.QueryableNativeType)! }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
CoreStore.assert(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object.rawObject!.isEditableInContext() == true,
|
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
|
||||||
)
|
|
||||||
self.customSetter(
|
|
||||||
object,
|
|
||||||
{ (newValue: V) -> Void in
|
|
||||||
|
|
||||||
object.rawObject!.setValue(
|
CoreStore.assert(
|
||||||
newValue,
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
forKvcKey: self.keyPath,
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
willSetValue: { $0.cs_toQueryableNativeType() }
|
)
|
||||||
)
|
CoreStore.assert(
|
||||||
},
|
self.isTransient || object.rawObject!.isEditableInContext() == true,
|
||||||
newValue
|
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||||
)
|
)
|
||||||
|
let customSetter = (self.customSetter ?? { $1($2) })
|
||||||
|
customSetter(
|
||||||
|
object,
|
||||||
|
{ (newValue: V) -> Void in
|
||||||
|
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
willSetValue: { $0.cs_toQueryableNativeType() }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
newValue
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,21 +214,65 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
internal let isOptional = false
|
internal let isOptional = false
|
||||||
internal let isIndexed: Bool
|
internal let isIndexed: Bool
|
||||||
internal let isTransient: Bool
|
internal let isTransient: Bool
|
||||||
internal let defaultValue: Any?
|
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
|
internal let defaultValue: () -> Any?
|
||||||
internal let affectedByKeyPaths: () -> Set<String>
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
|
internal weak var parentObject: CoreStoreObject?
|
||||||
|
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
guard let customGetter = self.customGetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
return { (_ id: Any) -> Any? in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
let value = customGetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{
|
||||||
|
rawObject.getValue(
|
||||||
|
forKvcKey: keyPath,
|
||||||
|
didGetValue: { V.cs_fromQueryableNativeType($0 as! V.QueryableNativeType!)! }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return value.cs_toQueryableNativeType()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
|
guard let customSetter = self.customSetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
customSetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ (userValue: V) -> Void in
|
||||||
|
|
||||||
|
rawObject.setValue(
|
||||||
|
userValue,
|
||||||
|
forKvcKey: keyPath,
|
||||||
|
willSetValue: { $0.cs_toQueryableNativeType() }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
V.cs_fromQueryableNativeType(newValue as! V.QueryableNativeType)!
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
|
private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)?
|
||||||
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
|
private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -263,19 +321,19 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
*/
|
*/
|
||||||
public init(
|
public init(
|
||||||
_ keyPath: RawKeyPath,
|
_ keyPath: RawKeyPath,
|
||||||
`default`: V? = nil,
|
`default`: @autoclosure @escaping () -> V? = nil,
|
||||||
isIndexed: Bool = false,
|
isIndexed: Bool = false,
|
||||||
isTransient: Bool = false,
|
isTransient: Bool = false,
|
||||||
versionHashModifier: String? = nil,
|
versionHashModifier: String? = nil,
|
||||||
renamingIdentifier: String? = nil,
|
renamingIdentifier: String? = nil,
|
||||||
customGetter: @escaping (_ `self`: O, _ getValue: () -> V?) -> V? = { $1() },
|
customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil,
|
||||||
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void = { $1($2) },
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.isIndexed = isIndexed
|
self.isIndexed = isIndexed
|
||||||
self.isTransient = isTransient
|
self.isTransient = isTransient
|
||||||
self.defaultValue = `default`?.cs_toQueryableNativeType()
|
self.defaultValue = { `default`()?.cs_toQueryableNativeType() }
|
||||||
self.versionHashModifier = versionHashModifier
|
self.versionHashModifier = versionHashModifier
|
||||||
self.renamingIdentifier = renamingIdentifier
|
self.renamingIdentifier = renamingIdentifier
|
||||||
self.customGetter = customGetter
|
self.customGetter = customGetter
|
||||||
@@ -290,45 +348,59 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
return self.customGetter(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object,
|
|
||||||
{ () -> V? in
|
|
||||||
|
|
||||||
return object.rawObject!.getValue(
|
CoreStore.assert(
|
||||||
forKvcKey: self.keyPath,
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
didGetValue: { ($0 as! V.QueryableNativeType?).flatMap(V.cs_fromQueryableNativeType) }
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
)
|
)
|
||||||
}
|
let customGetter = (self.customGetter ?? { $1() })
|
||||||
)
|
return customGetter(
|
||||||
|
object,
|
||||||
|
{ () -> V? in
|
||||||
|
|
||||||
|
return object.rawObject!.getValue(
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
didGetValue: { ($0 as! V.QueryableNativeType?).flatMap(V.cs_fromQueryableNativeType) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
CoreStore.assert(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object.rawObject!.isEditableInContext() == true,
|
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
|
||||||
)
|
|
||||||
self.customSetter(
|
|
||||||
object,
|
|
||||||
{ (newValue: V?) -> Void in
|
|
||||||
|
|
||||||
object.rawObject!.setValue(
|
CoreStore.assert(
|
||||||
newValue,
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
forKvcKey: self.keyPath,
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
willSetValue: { $0?.cs_toQueryableNativeType() }
|
)
|
||||||
)
|
CoreStore.assert(
|
||||||
},
|
self.isTransient || object.rawObject!.isEditableInContext() == true,
|
||||||
newValue
|
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||||
)
|
)
|
||||||
|
let customSetter = (self.customSetter ?? { $1($2) })
|
||||||
|
customSetter(
|
||||||
|
object,
|
||||||
|
{ (newValue: V?) -> Void in
|
||||||
|
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
willSetValue: { $0?.cs_toQueryableNativeType() }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
newValue
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,21 +416,65 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
internal let isOptional = true
|
internal let isOptional = true
|
||||||
internal let isIndexed: Bool
|
internal let isIndexed: Bool
|
||||||
internal let isTransient: Bool
|
internal let isTransient: Bool
|
||||||
internal let defaultValue: Any?
|
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
|
internal let defaultValue: () -> Any?
|
||||||
internal let affectedByKeyPaths: () -> Set<String>
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
|
internal weak var parentObject: CoreStoreObject?
|
||||||
|
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
guard let customGetter = self.customGetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
return { (_ id: Any) -> Any? in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
let value = customGetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{
|
||||||
|
rawObject.getValue(
|
||||||
|
forKvcKey: keyPath,
|
||||||
|
didGetValue: { ($0 as! V.QueryableNativeType?).flatMap(V.cs_fromQueryableNativeType) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return value?.cs_toQueryableNativeType()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
|
guard let customSetter = self.customSetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
customSetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ (userValue: V?) -> Void in
|
||||||
|
|
||||||
|
rawObject.setValue(
|
||||||
|
userValue,
|
||||||
|
forKvcKey: keyPath,
|
||||||
|
willSetValue: { $0?.cs_toQueryableNativeType() }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(newValue as! V.QueryableNativeType?).flatMap(V.cs_fromQueryableNativeType)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
|
private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)?
|
||||||
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
|
private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,8 +507,8 @@ public extension ValueContainer.Required where V: EmptyableAttributeType {
|
|||||||
isTransient: Bool = false,
|
isTransient: Bool = false,
|
||||||
versionHashModifier: String? = nil,
|
versionHashModifier: String? = nil,
|
||||||
renamingIdentifier: String? = nil,
|
renamingIdentifier: String? = nil,
|
||||||
customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() },
|
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
|
||||||
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) },
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
@@ -463,13 +579,13 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
*/
|
*/
|
||||||
public init(
|
public init(
|
||||||
_ keyPath: RawKeyPath,
|
_ keyPath: RawKeyPath,
|
||||||
`default`: V,
|
`default`: @autoclosure @escaping () -> V,
|
||||||
isIndexed: Bool = false,
|
isIndexed: Bool = false,
|
||||||
isTransient: Bool = false,
|
isTransient: Bool = false,
|
||||||
versionHashModifier: String? = nil,
|
versionHashModifier: String? = nil,
|
||||||
renamingIdentifier: String? = nil,
|
renamingIdentifier: String? = nil,
|
||||||
customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() },
|
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
|
||||||
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) },
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
@@ -490,44 +606,58 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
return self.customGetter(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object,
|
|
||||||
{ () -> V in
|
|
||||||
|
|
||||||
return object.rawObject!.getValue(
|
CoreStore.assert(
|
||||||
forKvcKey: self.keyPath,
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
didGetValue: { $0 as! V }
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
)
|
)
|
||||||
}
|
let customGetter = (self.customGetter ?? { $1() })
|
||||||
)
|
return customGetter(
|
||||||
|
object,
|
||||||
|
{ () -> V in
|
||||||
|
|
||||||
|
return object.rawObject!.getValue(
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
didGetValue: { $0 as! V }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
CoreStore.assert(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object.rawObject!.isEditableInContext() == true,
|
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
|
||||||
)
|
|
||||||
self.customSetter(
|
|
||||||
object,
|
|
||||||
{ (newValue: V) -> Void in
|
|
||||||
|
|
||||||
object.rawObject!.setValue(
|
CoreStore.assert(
|
||||||
newValue,
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
forKvcKey: self.keyPath
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
)
|
)
|
||||||
},
|
CoreStore.assert(
|
||||||
newValue
|
self.isTransient || object.rawObject!.isEditableInContext() == true,
|
||||||
)
|
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||||
|
)
|
||||||
|
let customSetter = (self.customSetter ?? { $1($2) })
|
||||||
|
customSetter(
|
||||||
|
object,
|
||||||
|
{ (newValue: V) -> Void in
|
||||||
|
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKvcKey: self.keyPath
|
||||||
|
)
|
||||||
|
},
|
||||||
|
newValue
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,21 +674,55 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
internal let isOptional = false
|
internal let isOptional = false
|
||||||
internal let isIndexed: Bool
|
internal let isIndexed: Bool
|
||||||
internal let isTransient: Bool
|
internal let isTransient: Bool
|
||||||
internal let defaultValue: Any?
|
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
|
internal let defaultValue: () -> Any?
|
||||||
internal let affectedByKeyPaths: () -> Set<String>
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
|
internal weak var parentObject: CoreStoreObject?
|
||||||
|
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
guard let customGetter = self.customGetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
return { (_ id: Any) -> Any? in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
return customGetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ rawObject.getValue(forKvcKey: keyPath) as! V }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
|
guard let customSetter = self.customSetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
customSetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ (userValue: V) -> Void in
|
||||||
|
|
||||||
|
rawObject.setValue(userValue, forKvcKey: keyPath)
|
||||||
|
},
|
||||||
|
newValue as! V
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
|
private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)?
|
||||||
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
|
private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -601,13 +765,13 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
*/
|
*/
|
||||||
public init(
|
public init(
|
||||||
_ keyPath: RawKeyPath,
|
_ keyPath: RawKeyPath,
|
||||||
`default`: V? = nil,
|
`default`: @autoclosure @escaping () -> V? = nil,
|
||||||
isIndexed: Bool = false,
|
isIndexed: Bool = false,
|
||||||
isTransient: Bool = false,
|
isTransient: Bool = false,
|
||||||
versionHashModifier: String? = nil,
|
versionHashModifier: String? = nil,
|
||||||
renamingIdentifier: String? = nil,
|
renamingIdentifier: String? = nil,
|
||||||
customGetter: @escaping (_ `self`: O, _ getValue: () -> V?) -> V? = { $1() },
|
customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil,
|
||||||
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void = { $1($2) },
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil,
|
||||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
@@ -628,44 +792,58 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
return self.customGetter(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object,
|
|
||||||
{ () -> V? in
|
|
||||||
|
|
||||||
return object.rawObject!.getValue(
|
CoreStore.assert(
|
||||||
forKvcKey: self.keyPath,
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
didGetValue: { $0 as! V? }
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
)
|
)
|
||||||
}
|
let customGetter = (self.customGetter ?? { $1() })
|
||||||
)
|
return customGetter(
|
||||||
|
object,
|
||||||
|
{ () -> V? in
|
||||||
|
|
||||||
|
return object.rawObject!.getValue(
|
||||||
|
forKvcKey: self.keyPath,
|
||||||
|
didGetValue: { $0 as! V? }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
CoreStore.assert(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object.rawObject!.isEditableInContext() == true,
|
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
|
||||||
)
|
|
||||||
self.customSetter(
|
|
||||||
object,
|
|
||||||
{ (newValue: V?) -> Void in
|
|
||||||
|
|
||||||
object.rawObject!.setValue(
|
CoreStore.assert(
|
||||||
newValue,
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
forKvcKey: self.keyPath
|
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||||
)
|
)
|
||||||
},
|
CoreStore.assert(
|
||||||
newValue
|
self.isTransient || object.rawObject!.isEditableInContext() == true,
|
||||||
)
|
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||||
|
)
|
||||||
|
let customSetter = (self.customSetter ?? { $1($2) })
|
||||||
|
customSetter(
|
||||||
|
object,
|
||||||
|
{ (newValue: V?) -> Void in
|
||||||
|
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKvcKey: self.keyPath
|
||||||
|
)
|
||||||
|
},
|
||||||
|
newValue
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,21 +860,63 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
internal let isOptional = true
|
internal let isOptional = true
|
||||||
internal let isIndexed: Bool
|
internal let isIndexed: Bool
|
||||||
internal let isTransient: Bool
|
internal let isTransient: Bool
|
||||||
internal let defaultValue: Any?
|
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
|
internal let defaultValue: () -> Any?
|
||||||
internal let affectedByKeyPaths: () -> Set<String>
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
|
internal weak var parentObject: CoreStoreObject?
|
||||||
|
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
guard let customGetter = self.customGetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
return { (_ id: Any) -> Any? in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
return customGetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ rawObject.getValue(forKvcKey: keyPath) as! V? }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
guard let customSetter = self.customSetter else {
|
||||||
|
|
||||||
|
guard let _ = self.customGetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
rawObject.setValue(newValue, forKvcKey: keyPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
customSetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ (userValue: V?) -> Void in
|
||||||
|
|
||||||
|
rawObject.setValue(userValue, forKvcKey: keyPath)
|
||||||
|
},
|
||||||
|
newValue as! V?
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
|
private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)?
|
||||||
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
|
private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -999,9 +1219,11 @@ internal protocol AttributeProtocol: class {
|
|||||||
var isOptional: Bool { get }
|
var isOptional: Bool { get }
|
||||||
var isIndexed: Bool { get }
|
var isIndexed: Bool { get }
|
||||||
var isTransient: Bool { get }
|
var isTransient: Bool { get }
|
||||||
var defaultValue: Any? { get }
|
|
||||||
var versionHashModifier: String? { get }
|
var versionHashModifier: String? { get }
|
||||||
var renamingIdentifier: String? { get }
|
var renamingIdentifier: String? { get }
|
||||||
|
var defaultValue: () -> Any? { get }
|
||||||
var affectedByKeyPaths: () -> Set<String> { get }
|
var affectedByKeyPaths: () -> Set<String> { get }
|
||||||
var parentObject: () -> CoreStoreObject { get set }
|
weak var parentObject: CoreStoreObject? { get set }
|
||||||
|
var getter: CoreStoreManagedObject.CustomGetter? { get }
|
||||||
|
var setter: CoreStoreManagedObject.CustomSetter? { get }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user