lazily evaluate NSEntityDescription-required fields from CoreStoreObject attributes

This commit is contained in:
John Estropia
2020-01-09 17:00:43 +09:00
parent f119a3adec
commit c544e0cce8
7 changed files with 174 additions and 186 deletions

View File

@@ -30,16 +30,19 @@ import CoreData
// MARK: - AttributeProtocol
internal protocol AttributeProtocol: PropertyProtocol {
static var attributeType: NSAttributeType { get }
var isOptional: Bool { get }
var isTransient: Bool { get }
var allowsExternalBinaryDataStorage: Bool { get }
var versionHashModifier: () -> String? { get }
var renamingIdentifier: () -> String? { get }
var defaultValue: () -> Any? { get }
var affectedByKeyPaths: () -> Set<String> { get }
typealias EntityDescriptionValues = (
attributeType: NSAttributeType,
isOptional: Bool,
isTransient: Bool,
allowsExternalBinaryDataStorage: Bool,
versionHashModifier: String?,
renamingIdentifier: String?,
affectedByKeyPaths: Set<String>,
defaultValue: Any?
)
var entityDescriptionValues: () -> EntityDescriptionValues { get }
var rawObject: CoreStoreManagedObject? { get set }
var getter: CoreStoreManagedObject.CustomGetter? { get }
var setter: CoreStoreManagedObject.CustomSetter? { get }

View File

@@ -44,6 +44,17 @@ public final class CSError: NSError {
*/
@objc
public static let errorDomain = CoreStoreErrorDomain
public var bridgeToSwift: CoreStoreError {
if let swift = self.swiftError {
return swift
}
let swift = CoreStoreError(_bridgedNSError: self) ?? .unknown
self.swiftError = swift
return swift
}
// MARK: NSObject
@@ -88,21 +99,7 @@ public final class CSError: NSError {
}
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
extension CSError: CoreStoreObjectiveCType {
// MARK: CoreStoreObjectiveCType
public var bridgeToSwift: CoreStoreError {
if let swift = self.swiftError {
return swift
}
let swift = CoreStoreError(_bridgedNSError: self) ?? .unknown
self.swiftError = swift
return swift
}
}
extension CSError: CoreStoreObjectiveCType {}
// MARK: - CSErrorCode
@@ -156,16 +153,7 @@ public enum CSErrorCode: Int {
// MARK: - CoreStoreError
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
extension CoreStoreError: CoreStoreSwiftType, _ObjectiveCBridgeableError {
// MARK: CoreStoreSwiftType
public var bridgeToObjectiveC: CSError {
return CSError(self)
}
extension CoreStoreError: _ObjectiveCBridgeableError {
// MARK: _ObjectiveCBridgeableError
@@ -265,9 +253,11 @@ extension CoreStoreError: CoreStoreSwiftType, _ObjectiveCBridgeableError {
}
// MARK: Internal
// MARK: - Error
extension Error {
// MARK: Internal
internal var bridgeToSwift: CoreStoreError {
@@ -303,3 +293,17 @@ extension Error {
}
}
}
// MARK: - CoreStoreError
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
extension CoreStoreError: CoreStoreSwiftType {
// MARK: CoreStoreSwiftType
public var bridgeToObjectiveC: CSError {
return CSError(self)
}
}

View File

@@ -292,17 +292,18 @@ public final class CoreStoreSchema: DynamicSchema {
!NSManagedObject.instancesRespond(to: Selector(attribute.keyPath)),
"Attribute Property name \"\(String(reflecting: entity.type)).\(attribute.keyPath)\" is not allowed because it collides with \"\(String(reflecting: NSManagedObject.self)).\(attribute.keyPath)\""
)
let entityDescriptionValues = attribute.entityDescriptionValues()
let description = NSAttributeDescription()
description.name = attribute.keyPath
description.attributeType = Swift.type(of: attribute).attributeType
description.isOptional = attribute.isOptional
description.defaultValue = attribute.defaultValue()
description.isTransient = attribute.isTransient
description.allowsExternalBinaryDataStorage = attribute.allowsExternalBinaryDataStorage
description.versionHashModifier = attribute.versionHashModifier()
description.renamingIdentifier = attribute.renamingIdentifier()
description.attributeType = entityDescriptionValues.attributeType
description.isOptional = entityDescriptionValues.isOptional
description.defaultValue = entityDescriptionValues.defaultValue
description.isTransient = entityDescriptionValues.isTransient
description.allowsExternalBinaryDataStorage = entityDescriptionValues.allowsExternalBinaryDataStorage
description.versionHashModifier = entityDescriptionValues.versionHashModifier
description.renamingIdentifier = entityDescriptionValues.renamingIdentifier
propertyDescriptions.append(description)
keyPathsByAffectedKeyPaths[attribute.keyPath] = attribute.affectedByKeyPaths()
keyPathsByAffectedKeyPaths[attribute.keyPath] = entityDescriptionValues.affectedByKeyPaths
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
case let relationship as RelationshipProtocol:
@@ -310,16 +311,17 @@ public final class CoreStoreSchema: DynamicSchema {
!NSManagedObject.instancesRespond(to: Selector(relationship.keyPath)),
"Relationship Property name \"\(String(reflecting: entity.type)).\(relationship.keyPath)\" is not allowed because it collides with \"\(String(reflecting: NSManagedObject.self)).\(relationship.keyPath)\""
)
let entityDescriptionValues = relationship.entityDescriptionValues()
let description = NSRelationshipDescription()
description.name = relationship.keyPath
description.minCount = relationship.minCount
description.maxCount = relationship.maxCount
description.isOrdered = relationship.isOrdered
description.deleteRule = relationship.deleteRule
description.versionHashModifier = relationship.versionHashModifier()
description.renamingIdentifier = relationship.renamingIdentifier()
description.minCount = entityDescriptionValues.minCount
description.maxCount = entityDescriptionValues.maxCount
description.isOrdered = entityDescriptionValues.isOrdered
description.deleteRule = entityDescriptionValues.deleteRule
description.versionHashModifier = entityDescriptionValues.versionHashModifier
description.renamingIdentifier = entityDescriptionValues.renamingIdentifier
propertyDescriptions.append(description)
keyPathsByAffectedKeyPaths[relationship.keyPath] = relationship.affectedByKeyPaths()
keyPathsByAffectedKeyPaths[relationship.keyPath] = entityDescriptionValues.affectedByKeyPaths
default:
continue
@@ -384,12 +386,12 @@ public final class CoreStoreSchema: DynamicSchema {
switch property {
case let relationship as RelationshipProtocol:
let (destinationType, destinationKeyPath) = relationship.inverse
let (destinationType, destinationKeyPath) = relationship.entityDescriptionValues().inverse
let destinationEntity = findEntity(for: destinationType)
let description = relationshipsByName[relationship.keyPath]!
description.destinationEntity = entityDescriptionsByEntity[destinationEntity]!
if let destinationKeyPath = destinationKeyPath() {
if let destinationKeyPath = destinationKeyPath {
let inverseRelationshipDescription = findInverseRelationshipMatching(
destinationEntity: destinationEntity,

View File

@@ -258,15 +258,7 @@ public enum RelationshipContainer<O: CoreStoreObject> {
// MARK: RelationshipProtocol
internal let isToMany = false
internal let isOrdered = false
internal let deleteRule: NSDeleteRule
internal let minCount: Int = 0
internal let maxCount: Int = 1
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?)
internal let versionHashModifier: () -> String?
internal let renamingIdentifier: () -> String?
internal let affectedByKeyPaths: () -> Set<String>
internal let entityDescriptionValues: () -> RelationshipProtocol.EntityDescriptionValues
internal var rawObject: CoreStoreManagedObject?
internal var nativeValue: NSManagedObject? {
@@ -324,11 +316,19 @@ public enum RelationshipContainer<O: CoreStoreObject> {
private init(keyPath: KeyPathString, inverseKeyPath: @escaping () -> KeyPathString?, deleteRule: DeleteRule, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
self.keyPath = keyPath
self.deleteRule = deleteRule.nativeValue
self.inverse = (D.self, inverseKeyPath)
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
self.affectedByKeyPaths = affectedByKeyPaths
self.entityDescriptionValues = {
(
isToMany: false,
isOrdered: false,
deleteRule: deleteRule.nativeValue,
inverse: (type: D.self, keyPath: inverseKeyPath()),
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths(),
minCount: 0,
maxCount: 1
)
}
}
}
@@ -553,16 +553,7 @@ public enum RelationshipContainer<O: CoreStoreObject> {
// MARK: RelationshipProtocol
internal let isToMany = true
internal let isOptional = true
internal let isOrdered = true
internal let deleteRule: NSDeleteRule
internal let minCount: Int
internal let maxCount: Int
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?)
internal let versionHashModifier: () -> String?
internal let renamingIdentifier: () -> String?
internal let affectedByKeyPaths: () -> Set<String>
internal let entityDescriptionValues: () -> RelationshipProtocol.EntityDescriptionValues
internal var rawObject: CoreStoreManagedObject?
internal var nativeValue: NSOrderedSet {
@@ -620,15 +611,20 @@ public enum RelationshipContainer<O: CoreStoreObject> {
private init(keyPath: String, minCount: Int, maxCount: Int, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
self.keyPath = keyPath
self.deleteRule = deleteRule.nativeValue
self.inverse = (D.self, inverseKeyPath)
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
let range = (Swift.max(0, minCount) ... maxCount)
self.minCount = range.lowerBound
self.maxCount = range.upperBound
self.affectedByKeyPaths = affectedByKeyPaths
self.entityDescriptionValues = {
let range = (Swift.max(0, minCount) ... maxCount)
return (
isToMany: true,
isOrdered: true,
deleteRule: deleteRule.nativeValue,
inverse: (type: D.self, keyPath: inverseKeyPath()),
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths(),
minCount: range.lowerBound,
maxCount: range.upperBound
)
}
}
}
@@ -853,17 +849,8 @@ public enum RelationshipContainer<O: CoreStoreObject> {
// MARK: RelationshipProtocol
internal let isToMany = true
internal let isOptional = true
internal let isOrdered = false
internal let deleteRule: NSDeleteRule
internal let minCount: Int
internal let maxCount: Int
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?)
internal let versionHashModifier: () -> String?
internal let renamingIdentifier: () -> String?
internal let affectedByKeyPaths: () -> Set<String>
internal let entityDescriptionValues: () -> RelationshipProtocol.EntityDescriptionValues
internal var rawObject: CoreStoreManagedObject?
internal var nativeValue: NSSet {
@@ -921,15 +908,20 @@ public enum RelationshipContainer<O: CoreStoreObject> {
private init(keyPath: KeyPathString, inverseKeyPath: @escaping () -> KeyPathString?, deleteRule: DeleteRule, minCount: Int, maxCount: Int, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
self.keyPath = keyPath
self.deleteRule = deleteRule.nativeValue
self.inverse = (D.self, inverseKeyPath)
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
let range = (Swift.max(0, minCount) ... maxCount)
self.minCount = range.lowerBound
self.maxCount = range.upperBound
self.affectedByKeyPaths = affectedByKeyPaths
self.entityDescriptionValues = {
let range = (Swift.max(0, minCount) ... maxCount)
return (
isToMany: true,
isOrdered: false,
deleteRule: deleteRule.nativeValue,
inverse: (type: D.self, keyPath: inverseKeyPath()),
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths(),
minCount: range.lowerBound,
maxCount: range.upperBound
)
}
}
}

View File

@@ -31,15 +31,20 @@ import CoreData
internal protocol RelationshipProtocol: PropertyProtocol {
var isToMany: Bool { get }
var isOrdered: Bool { get }
var deleteRule: NSDeleteRule { get }
var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?) { get }
var affectedByKeyPaths: () -> Set<String> { get }
typealias EntityDescriptionValues = (
isToMany: Bool,
isOrdered: Bool,
deleteRule: NSDeleteRule,
inverse: (type: CoreStoreObject.Type, KeyPathString?),
versionHashModifier: String?,
renamingIdentifier: String?,
affectedByKeyPaths: Set<String>,
minCount: Int,
maxCount: Int
)
var entityDescriptionValues: () -> EntityDescriptionValues { get }
var rawObject: CoreStoreManagedObject? { get set }
var versionHashModifier: () -> String? { get }
var renamingIdentifier: () -> String? { get }
var minCount: Int { get }
var maxCount: Int { get }
var valueForSnapshot: Any? { get }
}

View File

@@ -127,14 +127,20 @@ public enum TransformableContainer<O: CoreStoreObject> {
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
self.defaultValue = initial
self.isTransient = isTransient
self.allowsExternalBinaryDataStorage = allowsExternalBinaryDataStorage
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
self.entityDescriptionValues = {
(
attributeType: .transformableAttributeType,
isOptional: false,
isTransient: isTransient,
allowsExternalBinaryDataStorage: allowsExternalBinaryDataStorage,
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths(),
defaultValue: initial()
)
}
self.customGetter = customGetter
self.customSetter = customSetter
self.affectedByKeyPaths = affectedByKeyPaths
}
/**
@@ -215,19 +221,8 @@ public enum TransformableContainer<O: CoreStoreObject> {
// MARK: AttributeProtocol
internal static var attributeType: NSAttributeType {
return .transformableAttributeType
}
internal let isOptional = false
internal let isTransient: Bool
internal let allowsExternalBinaryDataStorage: Bool
internal let versionHashModifier: () -> String?
internal let renamingIdentifier: () -> String?
internal let defaultValue: () -> Any?
internal let affectedByKeyPaths: () -> Set<String>
internal let entityDescriptionValues: () -> AttributeProtocol.EntityDescriptionValues
internal var rawObject: CoreStoreManagedObject?
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = Internals.with { [unowned self] in
@@ -349,14 +344,20 @@ public enum TransformableContainer<O: CoreStoreObject> {
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
self.defaultValue = initial
self.isTransient = isTransient
self.allowsExternalBinaryDataStorage = allowsExternalBinaryDataStorage
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
self.entityDescriptionValues = {
(
attributeType: .transformableAttributeType,
isOptional: true,
isTransient: isTransient,
allowsExternalBinaryDataStorage: allowsExternalBinaryDataStorage,
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths(),
defaultValue: initial()
)
}
self.customGetter = customGetter
self.customSetter = customSetter
self.affectedByKeyPaths = affectedByKeyPaths
}
/**
@@ -438,18 +439,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
// MARK: AttributeProtocol
internal static var attributeType: NSAttributeType {
return .transformableAttributeType
}
internal let isOptional = true
internal let isTransient: Bool
internal let allowsExternalBinaryDataStorage: Bool
internal let versionHashModifier: () -> String?
internal let renamingIdentifier: () -> String?
internal let defaultValue: () -> Any?
internal let affectedByKeyPaths: () -> Set<String>
internal let entityDescriptionValues: () -> AttributeProtocol.EntityDescriptionValues
internal var rawObject: CoreStoreManagedObject?
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = Internals.with { [unowned self] in

View File

@@ -121,13 +121,20 @@ public enum ValueContainer<O: CoreStoreObject> {
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
self.isTransient = isTransient
self.defaultValue = { initial().cs_toQueryableNativeType() }
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
self.entityDescriptionValues = {
(
attributeType: V.cs_rawAttributeType,
isOptional: false,
isTransient: isTransient,
allowsExternalBinaryDataStorage: false,
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths(),
defaultValue: initial().cs_toQueryableNativeType()
)
}
self.customGetter = customGetter
self.customSetter = customSetter
self.affectedByKeyPaths = affectedByKeyPaths
}
/**
@@ -210,19 +217,8 @@ public enum ValueContainer<O: CoreStoreObject> {
// MARK: AttributeProtocol
internal static var attributeType: NSAttributeType {
return V.cs_rawAttributeType
}
internal let isOptional = false
internal let isTransient: Bool
internal let allowsExternalBinaryDataStorage = false
internal let versionHashModifier: () -> String?
internal let renamingIdentifier: () -> String?
internal let defaultValue: () -> Any?
internal let affectedByKeyPaths: () -> Set<String>
internal let entityDescriptionValues: () -> AttributeProtocol.EntityDescriptionValues
internal var rawObject: CoreStoreManagedObject?
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = Internals.with { [unowned self] in
@@ -344,13 +340,20 @@ public enum ValueContainer<O: CoreStoreObject> {
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
self.isTransient = isTransient
self.defaultValue = { initial()?.cs_toQueryableNativeType() }
self.versionHashModifier = versionHashModifier
self.renamingIdentifier = renamingIdentifier
self.entityDescriptionValues = {
(
attributeType: V.cs_rawAttributeType,
isOptional: true,
isTransient: isTransient,
allowsExternalBinaryDataStorage: false,
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths(),
defaultValue: initial()?.cs_toQueryableNativeType()
)
}
self.customGetter = customGetter
self.customSetter = customSetter
self.affectedByKeyPaths = affectedByKeyPaths
}
/**
@@ -432,19 +435,8 @@ public enum ValueContainer<O: CoreStoreObject> {
// MARK: AttributeProtocol
internal static var attributeType: NSAttributeType {
return V.cs_rawAttributeType
}
internal let isOptional = true
internal let isTransient: Bool
internal let allowsExternalBinaryDataStorage = false
internal let versionHashModifier: () -> String?
internal let renamingIdentifier: () -> String?
internal let defaultValue: () -> Any?
internal let affectedByKeyPaths: () -> Set<String>
internal let entityDescriptionValues: () -> AttributeProtocol.EntityDescriptionValues
internal var rawObject: CoreStoreManagedObject?
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = Internals.with { [unowned self] in