Merge branch 'develop' into prototype/Swift_3_2

# Conflicts:
#	Sources/CoreStoreManagedObject.swift
#	Sources/CoreStoreSchema.swift
#	Sources/NSEntityDescription+DynamicModel.swift
#	Sources/Value.swift
This commit is contained in:
John Rommel Estropia
2017-06-11 09:16:34 +09:00
10 changed files with 678 additions and 294 deletions

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "CoreStore"
s.version = "4.0.3"
s.version = "4.0.4"
s.license = "MIT"
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
s.homepage = "https://github.com/JohnEstropia/CoreStore"

View File

@@ -51,15 +51,49 @@ class Dog: Animal {
}
class Person: CoreStoreObject {
let title = Value.Required<String>("title", default: "Mr.")
let name = Value.Required<String>(
"name",
customGetter: { (`self`, getValue) in
let title = Value.Required<String>(
"title",
default: "Mr.",
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 })
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>())
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"
XCTAssertEqual(person.name.value, "Mr. John") // Custom getter
XCTAssertEqual(person.name.value, "John")
XCTAssertEqual(person.displayName.value, "Mr. John") // Custom getter
person.title .= "Sir"
XCTAssertEqual(person.name.value, "Sir John")
XCTAssertEqual(person.displayName.value, "Sir John")
person.pets.value.insert(dog)
XCTAssertEqual(person.pets.count, 1)

View File

@@ -19,6 +19,7 @@ Unleashing the real power of Core Data with the elegance and safety of Swift
</p>
* **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).

View File

@@ -13,28 +13,14 @@ import CoreData
@objc internal class CoreStoreManagedObject: NSManagedObject {
@nonobjc
internal class func cs_setKeyPathsForValuesAffectingKeys(_ keyPathsForValuesAffectingKeys: [RawKeyPath: Set<RawKeyPath>], for managedObjectClass: CoreStoreManagedObject.Type) {
Static.queue.sync(flags: .barrier) {
Static.cache[ObjectIdentifier(managedObjectClass)] = keyPathsForValuesAffectingKeys
}
}
internal typealias CustomGetter = @convention(block) (_ rawObject: Any) -> Any?
internal typealias CustomSetter = @convention(block) (_ rawObject: Any, _ newValue: Any?) -> Void
internal typealias CustomGetterSetter = (getter: CustomGetter?, setter: CustomSetter?)
// MARK: NSManagedObject
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
@nonobjc @inline(__always)
internal static func cs_subclassName(for entity: DynamicEntity, in modelVersion: ModelVersion) -> 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)
}
return "_\(NSStringFromClass(CoreStoreManagedObject.self))__\(modelVersion)__\(NSStringFromClass(entity.type))__\(entity.entityName)"
}
}

View File

@@ -68,8 +68,8 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
public required init(rawObject: NSManagedObject) {
self.isMeta = false
self.rawObject = rawObject
self.initializeAttributes(Mirror(reflecting: self), { [unowned self] in self })
self.rawObject = (rawObject as! CoreStoreManagedObject)
self.initializeAttributes(Mirror(reflecting: self), self)
}
/**
@@ -110,13 +110,13 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
// MARK: Internal
internal let rawObject: NSManagedObject?
internal let rawObject: CoreStoreManagedObject?
internal let isMeta: Bool
// MARK: Private
private func initializeAttributes(_ mirror: Mirror, _ parentObject: @escaping () -> CoreStoreObject) {
private func initializeAttributes(_ mirror: Mirror, _ parentObject: CoreStoreObject) {
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, parentObject) })
for child in mirror.children {

View File

@@ -208,17 +208,22 @@ public final class CoreStoreSchema: DynamicSchema {
}
let rawModel = NSManagedObjectModel()
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
var allCustomGettersSetters: [DynamicEntity: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:]
for entity in self.allEntities {
let entityDescription = self.entityDescription(
let (entityDescription, customGetterSetterByKeyPaths) = self.entityDescription(
for: entity,
initializer: CoreStoreSchema.firstPassCreateEntityDescription
initializer: CoreStoreSchema.firstPassCreateEntityDescription(from:in:)
)
entityDescriptionsByEntity[entity] = (entityDescription.copy() as! NSEntityDescription)
allCustomGettersSetters[entity] = customGetterSetterByKeyPaths
}
CoreStoreSchema.secondPassConnectRelationshipAttributes(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! })
for (configuration, entities) in self.entitiesByConfiguration {
@@ -248,29 +253,33 @@ public final class CoreStoreSchema: DynamicSchema {
private let allEntities: Set<DynamicEntity>
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
private var customGettersSettersByEntity: [DynamicEntity: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:]
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] {
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
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()
entityDescription.coreStoreEntity = entity
entityDescription.name = entity.entityName
entityDescription.isAbstract = entity.isAbstract
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 customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter] = [:]
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
var propertyDescriptions: [NSPropertyDescription] = []
@@ -284,12 +293,13 @@ public final class CoreStoreSchema: DynamicSchema {
description.attributeType = type(of: attribute).attributeType
description.isOptional = attribute.isOptional
description.isIndexed = attribute.isIndexed
description.defaultValue = attribute.defaultValue
description.defaultValue = attribute.defaultValue()
description.isTransient = attribute.isTransient
description.versionHashModifier = attribute.versionHashModifier
description.renamingIdentifier = attribute.renamingIdentifier
propertyDescriptions.append(description)
keyPathsByAffectedKeyPaths[attribute.keyPath] = attribute.affectedByKeyPaths()
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
case let relationship as RelationshipProtocol:
let description = NSRelationshipDescription()
@@ -309,9 +319,9 @@ public final class CoreStoreSchema: DynamicSchema {
}
return propertyDescriptions
}
entityDescription.keyPathsByAffectedKeyPaths = keyPathsByAffectedKeyPaths
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]) {
@@ -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 className = entityDescription.managedObjectClassName!
@@ -445,7 +455,10 @@ public final class CoreStoreSchema: DynamicSchema {
}
if let superEntity = superEntity {
createManagedObjectSubclass(for: superEntity)
createManagedObjectSubclass(
for: superEntity,
customGetterSetterByKeyPaths: superEntity.coreStoreEntity.flatMap({ allCustomGettersSetters[$0] })
)
}
let superClass = cs_lazy { () -> CoreStoreManagedObject.Type in
@@ -456,19 +469,85 @@ public final class CoreStoreSchema: DynamicSchema {
}
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)
managedObjectClass.cs_setKeyPathsForValuesAffectingKeys(
entityDescription.keyPathsByAffectedKeyPaths,
for: managedObjectClass
)
}
for (_, entityDescription) in entityDescriptionsByEntity {
defer {
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]
)
}
}
}

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>4.0.3</string>
<string>4.0.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@@ -75,14 +75,15 @@ internal extension NSEntityDescription {
}
}
@nonobjc
internal var keyPathsByAffectedKeyPaths: [RawKeyPath: Set<RawKeyPath>] {
get {
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 [:]
}
@@ -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
@@ -108,6 +130,8 @@ internal extension NSEntityDescription {
fileprivate static let CoreStoreManagedObjectVersionHashModifier = "CoreStoreManagedObjectVersionHashModifier"
fileprivate static let CoreStoreManagedObjectKeyPathsByAffectedKeyPaths = "CoreStoreManagedObjectKeyPathsByAffectedKeyPaths"
fileprivate static let CoreStoreManagedObjectCustomGetterSetterByKeyPaths = "CoreStoreManagedObjectCustomGetterSetterByKeyPaths"
}
private func cs_setUserInfo(_ closure: (_ userInfo: inout [AnyHashable: Any]) -> Void) {

View File

@@ -245,41 +245,49 @@ public enum RelationshipContainer<O: CoreStoreObject> {
internal let versionHashModifier: String?
internal let renamingIdentifier: String?
internal let affectedByKeyPaths: () -> Set<String>
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 weak var parentObject: CoreStoreObject?
internal var nativeValue: NSManagedObject? {
get {
let object = self.parentObject() as! O
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? }
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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 {
let object = self.parentObject() as! O
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
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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 renamingIdentifier: String?
internal let affectedByKeyPaths: () -> Set<String>
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 weak var parentObject: CoreStoreObject?
internal var nativeValue: NSOrderedSet {
get {
let object = self.parentObject() as! O
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?) ?? [] }
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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 {
let object = self.parentObject() as! O
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
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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 renamingIdentifier: String?
internal let affectedByKeyPaths: () -> Set<String>
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 weak var parentObject: CoreStoreObject?
internal var nativeValue: NSSet {
get {
let object = self.parentObject() as! O
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?) ?? [] }
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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 {
let object = self.parentObject() as! O
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
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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 inverse: (type: CoreStoreObject.Type, keyPath: () -> RawKeyPath?) { get }
var affectedByKeyPaths: () -> Set<String> { get }
var parentObject: () -> CoreStoreObject { get set }
weak var parentObject: CoreStoreObject? { get set }
var versionHashModifier: String? { get }
var renamingIdentifier: String? { get }
var minCount: Int { get }

View File

@@ -118,19 +118,19 @@ public enum ValueContainer<O: CoreStoreObject> {
*/
public init(
_ keyPath: RawKeyPath,
`default`: V,
`default`: @autoclosure @escaping () -> V,
isIndexed: Bool = false,
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() },
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) },
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
self.isIndexed = isIndexed
self.isTransient = isTransient
self.defaultValue = `default`.cs_toQueryableNativeType()
self.defaultValue = { `default`().cs_toQueryableNativeType() }
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
self.customGetter = customGetter
@@ -145,45 +145,59 @@ public enum ValueContainer<O: CoreStoreObject> {
get {
let object = self.parentObject() as! O
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.customGetter(
object,
{ () -> V in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { V.cs_fromQueryableNativeType($0 as! V.QueryableNativeType)! }
)
}
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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."
)
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 {
let object = self.parentObject() as! O
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."
)
self.customSetter(
object,
{ (newValue: V) -> Void in
object.rawObject!.setValue(
newValue,
forKvcKey: self.keyPath,
willSetValue: { $0.cs_toQueryableNativeType() }
)
},
newValue
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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(
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,
willSetValue: { $0.cs_toQueryableNativeType() }
)
},
newValue
)
}
}
}
@@ -200,21 +214,65 @@ public enum ValueContainer<O: CoreStoreObject> {
internal let isOptional = false
internal let isIndexed: Bool
internal let isTransient: Bool
internal let defaultValue: Any?
internal let versionHashModifier: String?
internal let renamingIdentifier: String?
internal let defaultValue: () -> Any?
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
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)?
private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)?
}
@@ -263,19 +321,19 @@ public enum ValueContainer<O: CoreStoreObject> {
*/
public init(
_ keyPath: RawKeyPath,
`default`: V? = nil,
`default`: @autoclosure @escaping () -> V? = nil,
isIndexed: Bool = false,
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: @escaping (_ `self`: O, _ getValue: () -> V?) -> V? = { $1() },
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void = { $1($2) },
customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
self.isIndexed = isIndexed
self.isTransient = isTransient
self.defaultValue = `default`?.cs_toQueryableNativeType()
self.defaultValue = { `default`()?.cs_toQueryableNativeType() }
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
self.customGetter = customGetter
@@ -290,45 +348,59 @@ public enum ValueContainer<O: CoreStoreObject> {
get {
let object = self.parentObject() as! O
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.customGetter(
object,
{ () -> V? in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { ($0 as! V.QueryableNativeType?).flatMap(V.cs_fromQueryableNativeType) }
)
}
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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."
)
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 {
let object = self.parentObject() as! O
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."
)
self.customSetter(
object,
{ (newValue: V?) -> Void in
object.rawObject!.setValue(
newValue,
forKvcKey: self.keyPath,
willSetValue: { $0?.cs_toQueryableNativeType() }
)
},
newValue
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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(
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,
willSetValue: { $0?.cs_toQueryableNativeType() }
)
},
newValue
)
}
}
}
@@ -344,21 +416,65 @@ public enum ValueContainer<O: CoreStoreObject> {
internal let isOptional = true
internal let isIndexed: Bool
internal let isTransient: Bool
internal let defaultValue: Any?
internal let versionHashModifier: String?
internal let renamingIdentifier: String?
internal let defaultValue: () -> Any?
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
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)?
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,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() },
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) },
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.init(
@@ -463,13 +579,13 @@ public enum TransformableContainer<O: CoreStoreObject> {
*/
public init(
_ keyPath: RawKeyPath,
`default`: V,
`default`: @autoclosure @escaping () -> V,
isIndexed: Bool = false,
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() },
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) },
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
@@ -490,44 +606,58 @@ public enum TransformableContainer<O: CoreStoreObject> {
get {
let object = self.parentObject() as! O
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.customGetter(
object,
{ () -> V in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { $0 as! V }
)
}
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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."
)
let customGetter = (self.customGetter ?? { $1() })
return customGetter(
object,
{ () -> V in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { $0 as! V }
)
}
)
}
}
set {
let object = self.parentObject() as! O
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."
)
self.customSetter(
object,
{ (newValue: V) -> Void in
object.rawObject!.setValue(
newValue,
forKvcKey: self.keyPath
)
},
newValue
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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(
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 isIndexed: Bool
internal let isTransient: Bool
internal let defaultValue: Any?
internal let versionHashModifier: String?
internal let renamingIdentifier: String?
internal let defaultValue: () -> Any?
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
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)?
private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)?
}
@@ -601,13 +765,13 @@ public enum TransformableContainer<O: CoreStoreObject> {
*/
public init(
_ keyPath: RawKeyPath,
`default`: V? = nil,
`default`: @autoclosure @escaping () -> V? = nil,
isIndexed: Bool = false,
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: @escaping (_ `self`: O, _ getValue: () -> V?) -> V? = { $1() },
customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void = { $1($2) },
customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
@@ -628,44 +792,58 @@ public enum TransformableContainer<O: CoreStoreObject> {
get {
let object = self.parentObject() as! O
CoreStore.assert(
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.customGetter(
object,
{ () -> V? in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { $0 as! V? }
)
}
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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."
)
let customGetter = (self.customGetter ?? { $1() })
return customGetter(
object,
{ () -> V? in
return object.rawObject!.getValue(
forKvcKey: self.keyPath,
didGetValue: { $0 as! V? }
)
}
)
}
}
set {
let object = self.parentObject() as! O
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."
)
self.customSetter(
object,
{ (newValue: V?) -> Void in
object.rawObject!.setValue(
newValue,
forKvcKey: self.keyPath
)
},
newValue
self.parentObject != nil,
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
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(
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 isIndexed: Bool
internal let isTransient: Bool
internal let defaultValue: Any?
internal let versionHashModifier: String?
internal let renamingIdentifier: String?
internal let defaultValue: () -> Any?
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
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)?
private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)?
}
}
@@ -999,9 +1219,11 @@ internal protocol AttributeProtocol: class {
var isOptional: Bool { get }
var isIndexed: Bool { get }
var isTransient: Bool { get }
var defaultValue: Any? { get }
var versionHashModifier: String? { get }
var renamingIdentifier: String? { get }
var defaultValue: () -> Any? { 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 }
}