added tool to convert existing NSManagedObjectModels to the new CoreStoreSchema

This commit is contained in:
John Estropia
2017-04-21 14:54:57 +09:00
parent 02a660e4a6
commit fe70b7a27d
16 changed files with 517 additions and 66 deletions

View File

@@ -31,7 +31,7 @@ import Foundation
public final class CoreStoreSchema: DynamicSchema {
public convenience init(modelVersion: String, entities: [DynamicEntity], versionLock: VersionLock? = nil) {
public convenience init(modelVersion: ModelVersion, entities: [DynamicEntity], versionLock: VersionLock? = nil) {
self.init(
modelVersion: modelVersion,
@@ -40,7 +40,7 @@ public final class CoreStoreSchema: DynamicSchema {
)
}
public required init(modelVersion: String, entitiesByConfiguration: [String: [DynamicEntity]], versionLock: VersionLock? = nil) {
public required init(modelVersion: ModelVersion, entitiesByConfiguration: [String: [DynamicEntity]], versionLock: VersionLock? = nil) {
var actualEntitiesByConfiguration: [String: Set<AnyEntity>] = [:]
for (configuration, entities) in entitiesByConfiguration {
@@ -129,14 +129,20 @@ public final class CoreStoreSchema: DynamicSchema {
internal init(_ entity: DynamicEntity) {
self.type = entity.type
self.entityName = entity.entityName
self.init(
type: entity.type,
entityName: entity.entityName,
isAbstract: entity.isAbstract,
versionHashModifier: entity.versionHashModifier
)
}
internal init(type: CoreStoreObject.Type, entityName: String) {
internal init(type: CoreStoreObject.Type, entityName: String, isAbstract: Bool, versionHashModifier: String?) {
self.type = type
self.entityName = entityName
self.isAbstract = isAbstract
self.versionHashModifier = versionHashModifier
}
@@ -146,6 +152,8 @@ public final class CoreStoreSchema: DynamicSchema {
return lhs.type == rhs.type
&& lhs.entityName == rhs.entityName
&& lhs.isAbstract == rhs.isAbstract
&& lhs.versionHashModifier == rhs.versionHashModifier
}
// MARK: Hashable
@@ -154,12 +162,16 @@ public final class CoreStoreSchema: DynamicSchema {
return ObjectIdentifier(self.type).hashValue
^ self.entityName.hashValue
^ self.isAbstract.hashValue
^ (self.versionHashModifier ?? "").hashValue
}
// MARK: DynamicEntity
internal let type: CoreStoreObject.Type
internal let entityName: EntityName
internal let isAbstract: Bool
internal let versionHashModifier: String?
}
@@ -193,6 +205,7 @@ public final class CoreStoreSchema: DynamicSchema {
let entityDescription = NSEntityDescription()
entityDescription.anyEntity = entity
entityDescription.name = entity.entityName
entityDescription.isAbstract = entity.isAbstract
entityDescription.managedObjectClassName = NSStringFromClass(NSManagedObject.self)
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
@@ -216,8 +229,8 @@ public final class CoreStoreSchema: DynamicSchema {
case let relationship as RelationshipProtocol:
let description = NSRelationshipDescription()
description.name = relationship.keyPath
description.minCount = 0
description.maxCount = relationship.isToMany ? 0 : 1
description.minCount = relationship.minCount
description.maxCount = relationship.maxCount
description.isOrdered = relationship.isOrdered
description.deleteRule = relationship.deleteRule
// TODO: versionHash, renamingIdentifier, etc

View File

@@ -34,6 +34,8 @@ public protocol DynamicEntity {
var type: CoreStoreObject.Type { get }
var entityName: EntityName { get }
var isAbstract: Bool { get }
var versionHashModifier: String? { get }
}
@@ -41,15 +43,17 @@ public protocol DynamicEntity {
public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
public init(_ entityName: String) {
public init(_ entityName: String, isAbstract: Bool = false, versionHashModifier: String? = nil) {
self.init(O.self, entityName)
self.init(O.self, entityName, isAbstract: isAbstract, versionHashModifier: versionHashModifier)
}
public init(_ type: O.Type, _ entityName: String) {
public init(_ type: O.Type, _ entityName: String, isAbstract: Bool = false, versionHashModifier: String? = nil) {
self.type = type
self.entityName = entityName
self.isAbstract = isAbstract
self.versionHashModifier = versionHashModifier
}
@@ -57,6 +61,8 @@ public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
public let type: CoreStoreObject.Type
public let entityName: EntityName
public let isAbstract: Bool
public let versionHashModifier: String?
// MARK: Equatable
@@ -65,6 +71,8 @@ public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
return lhs.type == rhs.type
&& lhs.entityName == rhs.entityName
&& lhs.isAbstract == rhs.isAbstract
&& lhs.versionHashModifier == rhs.versionHashModifier
}
// MARK: Hashable
@@ -72,5 +80,8 @@ public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
public var hashValue: Int {
return ObjectIdentifier(self.type).hashValue
^ self.entityName.hashValue
^ self.isAbstract.hashValue
^ (self.versionHashModifier ?? "").hashValue
}
}

View File

@@ -100,6 +100,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.accessRawObject().isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
self.accessRawObject()
.setValue(
newValue,
@@ -117,6 +121,8 @@ public enum RelationshipContainer<O: CoreStoreObject> {
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: () -> KeyPath?)
internal var accessRawObject: () -> NSManagedObject = {
@@ -157,24 +163,24 @@ public enum RelationshipContainer<O: CoreStoreObject> {
relationship.value = relationship2.value
}
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify) {
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule)
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
}
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify) {
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
}
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify) {
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
}
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify) {
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
}
// TODO: add subscripts, indexed operations for more performant single updates
@@ -206,6 +212,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.accessRawObject().isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
self.accessRawObject()
.setValue(
newValue,
@@ -224,6 +234,8 @@ public enum RelationshipContainer<O: CoreStoreObject> {
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: () -> KeyPath?)
internal var accessRawObject: () -> NSManagedObject = {
@@ -234,11 +246,15 @@ public enum RelationshipContainer<O: CoreStoreObject> {
// MARK: Private
private init(keyPath: String, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule) {
private init(keyPath: String, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule, minCount: Int, maxCount: Int) {
self.keyPath = keyPath
self.deleteRule = deleteRule.nativeValue
self.inverse = (D.self, inverseKeyPath)
let range = (max(0, minCount) ... maxCount)
self.minCount = range.lowerBound
self.maxCount = range.upperBound
}
}
@@ -269,24 +285,24 @@ public enum RelationshipContainer<O: CoreStoreObject> {
relationship.value = Set(relationship2.value)
}
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify) {
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule)
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
}
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify) {
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
}
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify) {
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
}
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify) {
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
}
// TODO: add subscripts, indexed operations for more performant single updates
@@ -318,6 +334,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.accessRawObject().isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
self.accessRawObject()
.setValue(
newValue,
@@ -336,6 +356,8 @@ public enum RelationshipContainer<O: CoreStoreObject> {
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: () -> KeyPath?)
internal var accessRawObject: () -> NSManagedObject = {
@@ -346,11 +368,15 @@ public enum RelationshipContainer<O: CoreStoreObject> {
// MARK: Private
private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule) {
private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, minCount: Int, maxCount: Int) {
self.keyPath = keyPath
self.deleteRule = deleteRule.nativeValue
self.inverse = (D.self, inverseKeyPath)
let range = (max(0, minCount) ... maxCount)
self.minCount = range.lowerBound
self.maxCount = range.upperBound
}
}
@@ -386,4 +412,6 @@ internal protocol RelationshipProtocol: class {
var deleteRule: NSDeleteRule { get }
var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get }
var accessRawObject: () -> NSManagedObject { get set }
var minCount: Int { get }
var maxCount: Int { get }
}

View File

@@ -86,11 +86,15 @@ public enum ValueContainer<O: CoreStoreObject> {
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.accessRawObject().isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
self.accessRawObject()
.setValue(
newValue,
forKvcKey: self.keyPath,
willSetValue: { $0.cs_toImportableNativeType() }
willSetValue: { ($0.cs_toImportableNativeType() as! CoreDataNativeType) }
)
}
}
@@ -163,11 +167,15 @@ public enum ValueContainer<O: CoreStoreObject> {
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
CoreStore.assert(
self.accessRawObject().isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
self.accessRawObject()
.setValue(
newValue,
forKvcKey: self.keyPath,
willSetValue: { $0?.cs_toImportableNativeType() }
willSetValue: { ($0?.cs_toImportableNativeType() as! CoreDataNativeType?) }
)
}
}