Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0

This commit is contained in:
John Rommel Estropia
2017-06-11 09:17:03 +09:00
10 changed files with 678 additions and 294 deletions

View File

@@ -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"

View File

@@ -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)

View File

@@ -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).

View File

@@ -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)
}
} }
} }

View File

@@ -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 {

View File

@@ -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]
)
} }
} }
} }

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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 }

View File

@@ -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 }
} }