WIP: Migrations

This commit is contained in:
John Estropia
2017-04-20 20:26:11 +09:00
parent a543a4c94a
commit 02a660e4a6
11 changed files with 205 additions and 77 deletions

View File

@@ -29,8 +29,31 @@ 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 {
/**
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(_ object: NSManagedObject) {
self.isMeta = false
@@ -38,6 +61,10 @@ open class CoreStoreObject: DynamicObject, Hashable {
self.initializeAttributes(Mirror(reflecting: self), { [unowned object] in object })
}
/**
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

View File

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

View File

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

View File

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

View File

@@ -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
@@ -127,3 +98,14 @@ extension CoreStoreObject {
return self.rawObject!
}
}
// MARK: - Internal
internal extension DynamicObject where Self: CoreStoreObject {
internal static var meta: Self {
return self.init(asMeta: ())
}
}

View File

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