mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-17 22:46:51 +01:00
Merge branch 'corestore4_develop' of github.com:JohnEstropia/CoreStore into corestore4_develop
This commit is contained in:
@@ -96,6 +96,14 @@ public final class DataStack: Equatable {
|
||||
return self.schemaHistory.currentModelVersion
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `DataStack`'s model schema.
|
||||
*/
|
||||
public var modelSchema: DynamicSchema {
|
||||
|
||||
return self.schemaHistory.schemaByVersion[self.schemaHistory.currentModelVersion]!
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `DataStack`'s model.
|
||||
*/
|
||||
@@ -141,7 +149,7 @@ public final class DataStack: Equatable {
|
||||
let actualType = anyEntity.type
|
||||
if (actualType as AnyClass).isSubclass(of: type) {
|
||||
|
||||
entityTypesByName[entityDescription.name!] = actualType
|
||||
entityTypesByName[entityDescription.name!] = (actualType as! CoreStoreObject.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -442,6 +450,21 @@ public final class DataStack: Equatable {
|
||||
}
|
||||
|
||||
|
||||
// MARK: 3rd Party Utilities
|
||||
|
||||
/**
|
||||
Allow external libraries to store custom data in the `DataStack`. App code should rarely have a need for this.
|
||||
```
|
||||
enum Static {
|
||||
static var myDataKey: Void?
|
||||
}
|
||||
CoreStore.defaultStack.userInfo[&Static.myDataKey] = myObject
|
||||
```
|
||||
- Important: Do not use this method to store thread-sensitive data.
|
||||
*/
|
||||
private let userInfo = UserInfo()
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (lhs: DataStack, rhs: DataStack) -> Bool {
|
||||
@@ -596,14 +619,14 @@ public final class DataStack: Equatable {
|
||||
- parameter model: the `NSManagedObjectModel` for the stack
|
||||
- parameter migrationChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
|
||||
*/
|
||||
@available(*, deprecated: 3.1, message: "Use the new DataStack.init(schemaHistory:) initializer passing a LegacyXcodeDataModel instance as argument")
|
||||
@available(*, deprecated: 3.1, message: "Use the new DataStack.init(schemaHistory:) initializer passing a LegacyXcodeDataModelSchema instance as argument")
|
||||
public convenience init(model: NSManagedObjectModel, migrationChain: MigrationChain = nil) {
|
||||
|
||||
let modelVersion = migrationChain.leafVersions.first!
|
||||
self.init(
|
||||
schemaHistory: SchemaHistory(
|
||||
allSchema: [
|
||||
LegacyXcodeDataModel(
|
||||
LegacyXcodeDataModelSchema(
|
||||
modelName: modelVersion,
|
||||
model: model
|
||||
)
|
||||
|
||||
@@ -29,15 +29,42 @@ import Foundation
|
||||
|
||||
// MARK: - CoreStoreObject
|
||||
|
||||
open class CoreStoreObject: DynamicObject, Hashable {
|
||||
/**
|
||||
The `CoreStoreObject` is an abstract class for creating CoreStore-managed objects that are more type-safe and more convenient than `NSManagedObject` subclasses. The model entities for `CoreStoreObject` subclasses are inferred from the subclasses' Swift declaration themselves; no .xcdatamodeld files needed. To declare persisted attributes and relationships for the `CoreStoreObject` subclass, declare properties of type `Value.Required<T>`, `Value.Optional<T>` for values, or `Relationship.ToOne<T>`, `Relationship.ToManyOrdered<T>`, `Relationship.ToManyUnordered<T>` for relationships.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let master = Relationship.ToOne<Person>("master")
|
||||
}
|
||||
|
||||
class Person: CoreStoreObject {
|
||||
let name = Value.Required<String>("name")
|
||||
let pet = Relationship.ToOne<Animal>("pet", inverse: { $0.master })
|
||||
}
|
||||
```
|
||||
`CoreStoreObject` entities for a model version should be added to `CoreStoreSchema` instance.
|
||||
- SeeAlso: CoreStoreSchema
|
||||
- SeeAlso: CoreStoreObject.Value
|
||||
- SeeAlso: CoreStoreObject.Relationship
|
||||
*/
|
||||
open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
||||
|
||||
public required init(_ object: NSManagedObject) {
|
||||
/**
|
||||
Do not call this directly. This is exposed as public only as a required initializer.
|
||||
- Important: subclasses that need a custom initializer should override both `init(_:)` and `init(asMeta:)`, and to call their corresponding super implementations.
|
||||
*/
|
||||
public required init(rawObject: NSManagedObject) {
|
||||
|
||||
self.isMeta = false
|
||||
self.rawObject = object
|
||||
self.initializeAttributes(Mirror(reflecting: self), { [unowned object] in object })
|
||||
self.rawObject = rawObject
|
||||
self.initializeAttributes(Mirror(reflecting: self), { [unowned self] in self })
|
||||
}
|
||||
|
||||
/**
|
||||
Do not call this directly. This is exposed as public only as a required initializer.
|
||||
- Important: subclasses that need a custom initializer should override both `init(_:)` and `init(asMeta:)`, and to call their corresponding super implementations.
|
||||
*/
|
||||
public required init(asMeta: Void) {
|
||||
|
||||
self.isMeta = true
|
||||
@@ -78,18 +105,18 @@ open class CoreStoreObject: DynamicObject, Hashable {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func initializeAttributes(_ mirror: Mirror, _ accessRawObject: @escaping () -> NSManagedObject) {
|
||||
private func initializeAttributes(_ mirror: Mirror, _ parentObject: @escaping () -> CoreStoreObject) {
|
||||
|
||||
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, accessRawObject) })
|
||||
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, parentObject) })
|
||||
for child in mirror.children {
|
||||
|
||||
switch child.value {
|
||||
|
||||
case let property as AttributeProtocol:
|
||||
property.accessRawObject = accessRawObject
|
||||
property.parentObject = parentObject
|
||||
|
||||
case let property as RelationshipProtocol:
|
||||
property.accessRawObject = accessRawObject
|
||||
property.parentObject = parentObject
|
||||
|
||||
default:
|
||||
continue
|
||||
|
||||
@@ -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: DynamicObject.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 type: DynamicObject.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] {
|
||||
@@ -210,17 +223,19 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
description.isIndexed = attribute.isIndexed
|
||||
description.defaultValue = attribute.defaultValue
|
||||
description.isTransient = attribute.isTransient
|
||||
// TODO: versionHash, renamingIdentifier, etc
|
||||
description.versionHashModifier = attribute.versionHashModifier
|
||||
description.renamingIdentifier = attribute.renamingIdentifier
|
||||
propertyDescriptions.append(description)
|
||||
|
||||
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
|
||||
description.versionHashModifier = relationship.versionHashModifier
|
||||
description.renamingIdentifier = relationship.renamingIdentifier
|
||||
propertyDescriptions.append(description)
|
||||
|
||||
default:
|
||||
@@ -230,7 +245,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
return propertyDescriptions
|
||||
}
|
||||
|
||||
entityDescription.properties = createProperties(for: entity.type)
|
||||
entityDescription.properties = createProperties(for: entity.type as! CoreStoreObject.Type)
|
||||
return entityDescription
|
||||
}
|
||||
|
||||
@@ -280,7 +295,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
for (entity, entityDescription) in entityDescriptionsByEntity {
|
||||
|
||||
let relationshipsByName = relationshipsByNameByEntity[entity]!
|
||||
for child in Mirror(reflecting: entity.type.meta).children {
|
||||
for child in Mirror(reflecting: (entity.type as! CoreStoreObject.Type).meta).children {
|
||||
|
||||
switch child.value {
|
||||
|
||||
@@ -347,7 +362,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
for (entity, entityDescription) in entityDescriptionsByEntity {
|
||||
|
||||
connectBaseEntity(
|
||||
mirror: Mirror(reflecting: entity.type.meta),
|
||||
mirror: Mirror(reflecting: (entity.type as! CoreStoreObject.Type).meta),
|
||||
entityDescription: entityDescription
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,9 +29,21 @@ import Foundation
|
||||
|
||||
// MARK: - DynamicSchema
|
||||
|
||||
/**
|
||||
`DynamicSchema` are types that provide `NSManagedObjectModel` instances for a particular model version. CoreStore currently supports concrete types:
|
||||
- `XcodeDataModelSchema`: describes models loaded from a .xcdatamodeld file.
|
||||
- `LegacyXcodeDataModelSchema`: describes models loaded directly from an existing `NSManagedObjectModel`. It is not advisable to continue using this model as its metadata are not available to CoreStore.
|
||||
- `CoreStoreSchema`: describes models written in `CoreStoreObject` Swift class declarations.
|
||||
*/
|
||||
public protocol DynamicSchema {
|
||||
|
||||
/**
|
||||
The version string for this model schema.
|
||||
*/
|
||||
var modelVersion: ModelVersion { get }
|
||||
|
||||
/**
|
||||
Do not call this directly. The `NSManagedObjectModel` for this schema may be created lazily and using this method directly may affect the integrity of the model.
|
||||
*/
|
||||
func rawModel() -> NSManagedObjectModel
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// LegacyXcodeDataModel.swift
|
||||
// LegacyXcodeDataModelSchema.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 John Rommel Estropia
|
||||
@@ -27,9 +27,9 @@ import CoreData
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - LegacyXcodeDataModel
|
||||
// MARK: - LegacyXcodeDataModelSchema
|
||||
|
||||
public final class LegacyXcodeDataModel: DynamicSchema {
|
||||
public final class LegacyXcodeDataModelSchema: DynamicSchema {
|
||||
|
||||
public required init(modelName: ModelVersion, model: NSManagedObjectModel) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// XcodeDataModel.swift
|
||||
// XcodeDataModelSchema.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 John Rommel Estropia
|
||||
@@ -27,9 +27,9 @@ import CoreData
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - XcodeDataModel
|
||||
// MARK: - XcodeDataModelSchema
|
||||
|
||||
public final class XcodeDataModel: DynamicSchema {
|
||||
public final class XcodeDataModelSchema: DynamicSchema {
|
||||
|
||||
public required init(modelVersion: ModelVersion, modelVersionFileURL: URL) {
|
||||
|
||||
@@ -37,35 +37,6 @@ public protocol DynamicObject: class {
|
||||
func cs_toRaw() -> NSManagedObject
|
||||
}
|
||||
|
||||
public extension DynamicObject where Self: CoreStoreObject {
|
||||
|
||||
@inline(__always)
|
||||
public static func keyPath<O: CoreStoreObject, V: ImportableAttributeType>(_ attribute: (Self) -> ValueContainer<O>.Required<V>) -> String {
|
||||
|
||||
return attribute(self.meta).keyPath
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func keyPath<O: CoreStoreObject, V: ImportableAttributeType>(_ attribute: (Self) -> ValueContainer<O>.Optional<V>) -> String {
|
||||
|
||||
return attribute(self.meta).keyPath
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func `where`(_ condition: (Self) -> Where) -> Where {
|
||||
|
||||
return condition(self.meta)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal static var meta: Self {
|
||||
|
||||
return self.init(asMeta: ())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSManagedObject
|
||||
|
||||
@@ -114,12 +85,23 @@ extension CoreStoreObject {
|
||||
|
||||
context.assign(object, to: store)
|
||||
}
|
||||
return self.init(object)
|
||||
return self.cs_fromRaw(object: object)
|
||||
}
|
||||
|
||||
public class func cs_fromRaw(object: NSManagedObject) -> Self {
|
||||
|
||||
return self.init(object)
|
||||
if let coreStoreObject = object.coreStoreObject {
|
||||
|
||||
@inline(__always)
|
||||
func forceCast<T: CoreStoreObject>(_ value: CoreStoreObject) -> T {
|
||||
|
||||
return value as! T
|
||||
}
|
||||
return forceCast(coreStoreObject)
|
||||
}
|
||||
let coreStoreObject = self.init(rawObject: object)
|
||||
object.coreStoreObject = coreStoreObject
|
||||
return coreStoreObject
|
||||
}
|
||||
|
||||
public func cs_toRaw() -> NSManagedObject {
|
||||
@@ -127,3 +109,14 @@ extension CoreStoreObject {
|
||||
return self.rawObject!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
internal extension DynamicObject where Self: CoreStoreObject {
|
||||
|
||||
internal static var meta: Self {
|
||||
|
||||
return self.init(asMeta: ())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,31 +32,37 @@ import ObjectiveC
|
||||
|
||||
public protocol DynamicEntity {
|
||||
|
||||
var type: CoreStoreObject.Type { get }
|
||||
var type: DynamicObject.Type { get }
|
||||
var entityName: EntityName { get }
|
||||
var isAbstract: Bool { get }
|
||||
var versionHashModifier: String? { get }
|
||||
}
|
||||
|
||||
|
||||
// MARK: Entity
|
||||
|
||||
public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
|
||||
public struct Entity<O: DynamicObject>: 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
|
||||
}
|
||||
|
||||
|
||||
// MARK: DynamicEntity
|
||||
|
||||
public let type: CoreStoreObject.Type
|
||||
public let type: DynamicObject.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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,52 +60,56 @@ 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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
public var value: D? {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return self.accessRawObject()
|
||||
.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { $0.flatMap({ D.cs_fromRaw(object: $0 as! NSManagedObject) }) }
|
||||
)
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { $0.flatMap({ D.cs_fromRaw(object: $0 as! NSManagedObject) }) }
|
||||
)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { $0?.rawObject }
|
||||
)
|
||||
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,
|
||||
willSetValue: { $0?.rawObject }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,9 +121,13 @@ 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 let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
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.")
|
||||
}
|
||||
@@ -127,11 +135,13 @@ 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, versionHashModifier: String?, renamingIdentifier: String?) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.deleteRule = deleteRule.nativeValue
|
||||
self.inverse = (D.self, inverseKeyPath)
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,24 +167,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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
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, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
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, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
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, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
// TODO: add subscripts, indexed operations for more performant single updates
|
||||
@@ -183,35 +193,39 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return self.accessRawObject()
|
||||
.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: {
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: {
|
||||
|
||||
guard let orderedSet = $0 as! NSOrderedSet? else {
|
||||
|
||||
guard let orderedSet = $0 as! NSOrderedSet? else {
|
||||
|
||||
return []
|
||||
}
|
||||
return orderedSet.map({ D.cs_fromRaw(object: $0 as! NSManagedObject) })
|
||||
return []
|
||||
}
|
||||
)
|
||||
return orderedSet.map({ D.cs_fromRaw(object: $0 as! NSManagedObject) })
|
||||
}
|
||||
)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { NSOrderedSet(array: $0.map({ $0.rawObject! })) }
|
||||
)
|
||||
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,
|
||||
willSetValue: { NSOrderedSet(array: $0.map({ $0.rawObject! })) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,9 +238,13 @@ 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 let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
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.")
|
||||
}
|
||||
@@ -234,11 +252,17 @@ 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, versionHashModifier: String?, renamingIdentifier: String?) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.deleteRule = deleteRule.nativeValue
|
||||
self.inverse = (D.self, inverseKeyPath)
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
|
||||
let range = (max(0, minCount) ... maxCount)
|
||||
self.minCount = range.lowerBound
|
||||
self.maxCount = range.upperBound
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,24 +293,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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
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, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
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, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
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, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
|
||||
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, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
}
|
||||
|
||||
// TODO: add subscripts, indexed operations for more performant single updates
|
||||
@@ -295,35 +319,39 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return self.accessRawObject()
|
||||
.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: {
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: {
|
||||
|
||||
guard let set = $0 as! NSSet? else {
|
||||
|
||||
guard let set = $0 as! NSSet? else {
|
||||
|
||||
return []
|
||||
}
|
||||
return Set(set.map({ D.cs_fromRaw(object: $0 as! NSManagedObject) }))
|
||||
return []
|
||||
}
|
||||
)
|
||||
return Set(set.map({ D.cs_fromRaw(object: $0 as! NSManagedObject) }))
|
||||
}
|
||||
)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { NSSet(array: $0.map({ $0.rawObject! })) }
|
||||
)
|
||||
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,
|
||||
willSetValue: { NSSet(array: $0.map({ $0.rawObject! })) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,9 +364,13 @@ 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 let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
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.")
|
||||
}
|
||||
@@ -346,11 +378,17 @@ 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, versionHashModifier: String?, renamingIdentifier: String?) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.deleteRule = deleteRule.nativeValue
|
||||
self.inverse = (D.self, inverseKeyPath)
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
|
||||
let range = (max(0, minCount) ... maxCount)
|
||||
self.minCount = range.lowerBound
|
||||
self.maxCount = range.upperBound
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,5 +423,9 @@ internal protocol RelationshipProtocol: class {
|
||||
var isOrdered: Bool { get }
|
||||
var deleteRule: NSDeleteRule { get }
|
||||
var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get }
|
||||
var accessRawObject: () -> NSManagedObject { get set }
|
||||
var parentObject: () -> CoreStoreObject { get set }
|
||||
var versionHashModifier: String? { get }
|
||||
var renamingIdentifier: String? { get }
|
||||
var minCount: Int { get }
|
||||
var maxCount: Int { get }
|
||||
}
|
||||
|
||||
@@ -29,14 +29,30 @@ import Foundation
|
||||
|
||||
// MARK: - SchemaHistory
|
||||
|
||||
/**
|
||||
The `SchemaHistory` encapsulates all model versions that is relevant to the data model, including past versions.
|
||||
- SeeAlso: SchemaHistory.currentModelVersion
|
||||
- SeeAlso: SchemaHistory.migrationChain
|
||||
*/
|
||||
public final class SchemaHistory: ExpressibleByArrayLiteral {
|
||||
|
||||
|
||||
// MARK: -
|
||||
|
||||
/**
|
||||
The version string for the current model version. The `DataStack` will try to migrate all `StorageInterface`s added to itself to this version, following the version steps provided by the `migrationChain`.
|
||||
*/
|
||||
public let currentModelVersion: ModelVersion
|
||||
|
||||
/**
|
||||
The version string for the current model version. The `DataStack` will try to migrate all `StorageInterface`s added to itself to this version, following the version steps provided by the `migrationChain`.
|
||||
*/
|
||||
public let migrationChain: MigrationChain
|
||||
|
||||
/**
|
||||
Initializes a `SchemaHistory` with all models declared in the specified (.xcdatamodeld) model file.
|
||||
- Important: Use this initializer only if all model versions are either `XcodeDataModelSchema`s or `LegacyXcodeDataModelSchema`s. Do not use this initializer if even one of the model versions is a `CoreStoreSchema`; use the `SchemaHistory.init(allSchema:migrationChain:exactCurrentModelVersion:)` initializer instead.
|
||||
- parameter modelName: the name of the (.xcdatamodeld) model file. If not specified, the application name (CFBundleName) will be used if it exists, or "CoreData" if it the bundle name was not set.
|
||||
- parameter bundle: an optional bundle to load models from. If not specified, the main bundle will be used.
|
||||
- parameter migrationChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
|
||||
*/
|
||||
public convenience init(modelName: XcodeDataModelFileName, bundle: Bundle = Bundle.main, migrationChain: MigrationChain = nil) {
|
||||
|
||||
guard let modelFilePath = bundle.path(forResource: modelName, ofType: "momd") else {
|
||||
@@ -92,7 +108,7 @@ public final class SchemaHistory: ExpressibleByArrayLiteral {
|
||||
for modelVersion in modelVersions {
|
||||
|
||||
let fileURL = modelFileURL.appendingPathComponent("\(modelVersion).mom", isDirectory: false)
|
||||
allSchema.append(XcodeDataModel(modelVersion: modelVersion, modelVersionFileURL: fileURL))
|
||||
allSchema.append(XcodeDataModelSchema(modelVersion: modelVersion, modelVersionFileURL: fileURL))
|
||||
}
|
||||
self.init(
|
||||
allSchema: allSchema,
|
||||
|
||||
@@ -37,6 +37,7 @@ infix operator .= : AssignmentPrecedence
|
||||
public extension DynamicObject where Self: CoreStoreObject {
|
||||
|
||||
public typealias Value = ValueContainer<Self>
|
||||
public typealias Transformable = TransformableContainer<Self>
|
||||
}
|
||||
|
||||
|
||||
@@ -58,40 +59,61 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
attribute.value = attribute2.value
|
||||
}
|
||||
|
||||
public init(_ keyPath: KeyPath, `default`: V = V.cs_emptyValue(), isIndexed: Bool = false, isTransient: Bool = false) {
|
||||
public init(_ keyPath: KeyPath, `default`: V = V.cs_emptyValue(), 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: (V) -> Void, _ newValue: V) -> Void = { $1($2) }) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.defaultValue = `default`.cs_toImportableNativeType()
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
}
|
||||
|
||||
public var value: V {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return self.accessRawObject()
|
||||
.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { V.cs_fromImportableNativeType($0 as! V.ImportableNativeType)! }
|
||||
)
|
||||
return self.customGetter(
|
||||
object,
|
||||
{ () -> V in
|
||||
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { V.cs_fromImportableNativeType($0 as! V.ImportableNativeType)! }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { $0.cs_toImportableNativeType() }
|
||||
)
|
||||
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_toImportableNativeType() }
|
||||
)
|
||||
},
|
||||
newValue
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,11 +131,19 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
internal let isIndexed: Bool
|
||||
internal let isTransient: Bool
|
||||
internal let defaultValue: Any?
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
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.")
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
|
||||
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
|
||||
}
|
||||
|
||||
|
||||
@@ -136,39 +166,60 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
attribute.value = attribute2.value
|
||||
}
|
||||
|
||||
public init(_ keyPath: KeyPath, `default`: V? = nil, isTransient: Bool = false) {
|
||||
public init(_ keyPath: KeyPath, `default`: V? = nil, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V?) -> V? = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void = { $1($2) }) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.isTransient = isTransient
|
||||
self.defaultValue = `default`?.cs_toImportableNativeType()
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
}
|
||||
|
||||
public var value: V? {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return self.accessRawObject()
|
||||
.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { ($0 as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) }
|
||||
)
|
||||
return self.customGetter(
|
||||
object,
|
||||
{ () -> V? in
|
||||
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { ($0 as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { $0?.cs_toImportableNativeType() }
|
||||
)
|
||||
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_toImportableNativeType() }
|
||||
)
|
||||
},
|
||||
newValue
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,11 +236,231 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
internal let isIndexed = false
|
||||
internal let isTransient: Bool
|
||||
internal let defaultValue: Any?
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
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.")
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
|
||||
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - TransformableContainer
|
||||
|
||||
public enum TransformableContainer<O: CoreStoreObject> {
|
||||
|
||||
// MARK: - Required
|
||||
|
||||
public final class Required<V: NSCoding & NSCopying>: AttributeProtocol {
|
||||
|
||||
public static func .= (_ attribute: TransformableContainer<O>.Required<V>, _ value: V) {
|
||||
|
||||
attribute.value = value
|
||||
}
|
||||
|
||||
public static func .=<O2: CoreStoreObject> (_ attribute: TransformableContainer<O>.Required<V>, _ attribute2: TransformableContainer<O2>.Required<V>) {
|
||||
|
||||
attribute.value = attribute2.value
|
||||
}
|
||||
|
||||
public init(_ keyPath: KeyPath, `default`: 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: (V) -> Void, _ newValue: V) -> Void = { $1($2) }) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = `default`
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
}
|
||||
|
||||
public var value: V {
|
||||
|
||||
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 }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: AttributeProtocol
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
|
||||
return .transformableAttributeType
|
||||
}
|
||||
|
||||
public let keyPath: KeyPath
|
||||
|
||||
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 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.")
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
|
||||
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Optional
|
||||
|
||||
public final class Optional<V: NSCoding & NSCopying>: AttributeProtocol {
|
||||
|
||||
public static func .= (_ attribute: TransformableContainer<O>.Optional<V>, _ value: V) {
|
||||
|
||||
attribute.value = value
|
||||
}
|
||||
|
||||
public static func .=<O2: CoreStoreObject> (_ attribute: TransformableContainer<O>.Optional<V>, _ attribute2: TransformableContainer<O2>.Optional<V>) {
|
||||
|
||||
attribute.value = attribute2.value
|
||||
}
|
||||
|
||||
public static func .=<O2: CoreStoreObject> (_ attribute: TransformableContainer<O>.Optional<V>, _ attribute2: TransformableContainer<O2>.Required<V>) {
|
||||
|
||||
attribute.value = attribute2.value
|
||||
}
|
||||
|
||||
public init(_ keyPath: KeyPath, `default`: 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: (V?) -> Void, _ newValue: V?) -> Void = { $1($2) }) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = `default`
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
}
|
||||
|
||||
public var value: V? {
|
||||
|
||||
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? }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: AttributeProtocol
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
|
||||
return .transformableAttributeType
|
||||
}
|
||||
|
||||
public let keyPath: KeyPath
|
||||
|
||||
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 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.")
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
|
||||
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,5 +476,7 @@ internal protocol AttributeProtocol: class {
|
||||
var isIndexed: Bool { get }
|
||||
var isTransient: Bool { get }
|
||||
var defaultValue: Any? { get }
|
||||
var accessRawObject: () -> NSManagedObject { get set }
|
||||
var versionHashModifier: String? { get }
|
||||
var renamingIdentifier: String? { get }
|
||||
var parentObject: () -> CoreStoreObject { get set }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user