mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-18 23:17:02 +01:00
WIP: prototype for ManagedObjectProtocol
This commit is contained in:
@@ -42,9 +42,17 @@ public extension CoreStore {
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `defaultStack`'s model.
|
||||
*/
|
||||
public static var entityTypesByName: [String: NSManagedObject.Type] {
|
||||
public static func entityTypesByName(for type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
|
||||
|
||||
return self.defaultStack.entityTypesByName
|
||||
return self.defaultStack.entityTypesByName(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `defaultStack`'s model.
|
||||
*/
|
||||
public static func entityTypesByName(for type: ManagedObject.Type) -> [EntityName: ManagedObject.Type] {
|
||||
|
||||
return self.defaultStack.entityTypesByName(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,6 +63,14 @@ public extension CoreStore {
|
||||
return self.defaultStack.entityDescription(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `NSEntityDescription` for the specified `ManagedObject` subclass from `defaultStack`'s model.
|
||||
*/
|
||||
public static func entityDescription(for type: ManagedObject.Type) -> NSEntityDescription? {
|
||||
|
||||
return self.defaultStack.entityDescription(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `SQLiteStore` with default parameters and adds it to the `defaultStack`. This method blocks until completion.
|
||||
```
|
||||
@@ -155,6 +171,18 @@ public extension CoreStore {
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `defaultStack`'s model.
|
||||
*/
|
||||
@available(*, deprecated: 3.1, message: "Use the new CoreStore.entityTypesByName(for:) method passing `NSManagedObject.self` as argument.")
|
||||
public static var entityTypesByName: [EntityName: NSManagedObject.Type] {
|
||||
|
||||
return self.defaultStack.entityTypesByName
|
||||
}
|
||||
|
||||
|
||||
// MARK: Obsolete
|
||||
|
||||
@available(*, obsoleted: 3.0.0, renamed: "entityDescription(for:)")
|
||||
|
||||
@@ -41,7 +41,7 @@ public final class DataStack: Equatable {
|
||||
- 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: String = DataStack.applicationName, bundle: Bundle = Bundle.main, migrationChain: MigrationChain = nil) {
|
||||
public convenience init(modelName: XcdatamodelFilename = DataStack.applicationName, bundle: Bundle = Bundle.main, migrationChain: MigrationChain = nil) {
|
||||
|
||||
let model = NSManagedObjectModel.fromBundle(
|
||||
bundle,
|
||||
@@ -51,12 +51,12 @@ public final class DataStack: Equatable {
|
||||
self.init(model: model, migrationChain: migrationChain)
|
||||
}
|
||||
|
||||
public convenience init(dynamicModel: ModelVersion) {
|
||||
public convenience init(dynamicModel: ObjectModel) {
|
||||
|
||||
self.init(model: dynamicModel.createModel())
|
||||
}
|
||||
|
||||
public convenience init(dynamicModels: [ModelVersion], migrationChain: MigrationChain = nil) {
|
||||
public convenience init(dynamicModels: [ObjectModel], migrationChain: MigrationChain = nil) {
|
||||
|
||||
CoreStore.assert(
|
||||
migrationChain.valid,
|
||||
@@ -106,9 +106,49 @@ public final class DataStack: Equatable {
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `DataStack`'s model.
|
||||
*/
|
||||
public var entityTypesByName: [String: NSManagedObject.Type] {
|
||||
public func entityTypesByName(for type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
|
||||
|
||||
return self.model.entityTypesMapping()
|
||||
var entityTypesByName: [EntityName: NSManagedObject.Type] = [:]
|
||||
for (entityIdentifier, entityDescription) in self.model.entityDescriptionsByEntityIdentifier {
|
||||
|
||||
switch entityIdentifier.category {
|
||||
|
||||
case .coreData:
|
||||
let actualType = NSClassFromString(entityDescription.managedObjectClassName!)! as! NSManagedObject.Type
|
||||
if (actualType as AnyClass).isSubclass(of: type) {
|
||||
|
||||
entityTypesByName[entityDescription.name!] = actualType
|
||||
}
|
||||
|
||||
case .coreStore:
|
||||
continue
|
||||
}
|
||||
}
|
||||
return entityTypesByName
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `DataStack`'s model.
|
||||
*/
|
||||
public func entityTypesByName(for type: ManagedObject.Type) -> [EntityName: ManagedObject.Type] {
|
||||
|
||||
var entityTypesByName: [EntityName: ManagedObject.Type] = [:]
|
||||
for (entityIdentifier, entityDescription) in self.model.entityDescriptionsByEntityIdentifier {
|
||||
|
||||
switch entityIdentifier.category {
|
||||
|
||||
case .coreData:
|
||||
continue
|
||||
|
||||
case .coreStore:
|
||||
let actualType = NSClassFromString(entityDescription.managedObjectClassName!)! as! ManagedObject.Type
|
||||
if (actualType as AnyClass).isSubclass(of: type) {
|
||||
|
||||
entityTypesByName[entityDescription.name!] = actualType
|
||||
}
|
||||
}
|
||||
}
|
||||
return entityTypesByName
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,10 +156,15 @@ public final class DataStack: Equatable {
|
||||
*/
|
||||
public func entityDescription(for type: NSManagedObject.Type) -> NSEntityDescription? {
|
||||
|
||||
return NSEntityDescription.entity(
|
||||
forEntityName: self.model.entityNameForClass(type),
|
||||
in: self.mainContext
|
||||
)
|
||||
return self.entityDescription(for: EntityIdentifier(type))
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `NSEntityDescription` for the specified `ManagedObject` subclass.
|
||||
*/
|
||||
public func entityDescription(for type: ManagedObject.Type) -> NSEntityDescription? {
|
||||
|
||||
return self.entityDescription(for: EntityIdentifier(type))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,6 +453,8 @@ public final class DataStack: Equatable {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal static var defaultConfigurationName = "PF_DEFAULT_CONFIGURATION_NAME"
|
||||
|
||||
internal static let applicationName = (Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String) ?? "CoreData"
|
||||
|
||||
internal let coordinator: NSPersistentStoreCoordinator
|
||||
@@ -434,34 +481,27 @@ public final class DataStack: Equatable {
|
||||
.first
|
||||
}
|
||||
|
||||
internal func entityNameForEntityClass(_ entityClass: AnyClass) -> String? {
|
||||
|
||||
return self.model.entityNameForClass(entityClass)
|
||||
}
|
||||
|
||||
internal func persistentStoresForEntityClass(_ entityClass: AnyClass) -> [NSPersistentStore]? {
|
||||
internal func persistentStores(for entityIdentifier: EntityIdentifier) -> [NSPersistentStore]? {
|
||||
|
||||
var returnValue: [NSPersistentStore]? = nil
|
||||
self.storeMetadataUpdateQueue.sync(flags: .barrier) {
|
||||
|
||||
returnValue = self.entityConfigurationsMapping[NSStringFromClass(entityClass)]?.map {
|
||||
|
||||
return self.configurationStoreMapping[$0]!
|
||||
} ?? []
|
||||
returnValue = self.finalConfigurationsByEntityIdentifier[entityIdentifier]?
|
||||
.map({ self.persistentStoresByFinalConfiguration[$0]! }) ?? []
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
|
||||
internal func persistentStoreForEntityClass(_ entityClass: AnyClass, configuration: String?, inferStoreIfPossible: Bool) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
|
||||
internal func persistentStore(for entityIdentifier: EntityIdentifier, configuration: ModelConfiguration, inferStoreIfPossible: Bool) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
|
||||
|
||||
return self.storeMetadataUpdateQueue.sync(flags: .barrier) { () -> (store: NSPersistentStore?, isAmbiguous: Bool) in
|
||||
|
||||
let configurationsForEntity = self.entityConfigurationsMapping[NSStringFromClass(entityClass)] ?? []
|
||||
let configurationsForEntity = self.finalConfigurationsByEntityIdentifier[entityIdentifier] ?? []
|
||||
if let configuration = configuration {
|
||||
|
||||
if configurationsForEntity.contains(configuration) {
|
||||
|
||||
return (store: self.configurationStoreMapping[configuration], isAmbiguous: false)
|
||||
return (store: self.persistentStoresByFinalConfiguration[configuration], isAmbiguous: false)
|
||||
}
|
||||
else if !inferStoreIfPossible {
|
||||
|
||||
@@ -475,7 +515,7 @@ public final class DataStack: Equatable {
|
||||
return (store: nil, isAmbiguous: false)
|
||||
|
||||
case 1 where inferStoreIfPossible:
|
||||
return (store: self.configurationStoreMapping[configurationsForEntity.first!], isAmbiguous: false)
|
||||
return (store: self.persistentStoresByFinalConfiguration[configurationsForEntity.first!], isAmbiguous: false)
|
||||
|
||||
default:
|
||||
return (store: nil, isAmbiguous: true)
|
||||
@@ -496,7 +536,7 @@ public final class DataStack: Equatable {
|
||||
self.storeMetadataUpdateQueue.async(flags: .barrier) {
|
||||
|
||||
let configurationName = persistentStore.configurationName
|
||||
self.configurationStoreMapping[configurationName] = persistentStore
|
||||
self.persistentStoresByFinalConfiguration[configurationName] = persistentStore
|
||||
for entityDescription in (self.coordinator.managedObjectModel.entities(forConfigurationName: configurationName) ?? []) {
|
||||
|
||||
let managedObjectClassName = entityDescription.managedObjectClassName!
|
||||
@@ -504,18 +544,23 @@ public final class DataStack: Equatable {
|
||||
NSClassFromString(managedObjectClassName) != nil,
|
||||
"The class \(cs_typeName(managedObjectClassName)) for the entity \(cs_typeName(entityDescription.name)) does not exist. Check if the subclass type and module name are properly configured."
|
||||
)
|
||||
|
||||
if self.entityConfigurationsMapping[managedObjectClassName] == nil {
|
||||
let entityIdentifier = EntityIdentifier(entityDescription)
|
||||
if self.finalConfigurationsByEntityIdentifier[entityIdentifier] == nil {
|
||||
|
||||
self.entityConfigurationsMapping[managedObjectClassName] = []
|
||||
self.finalConfigurationsByEntityIdentifier[entityIdentifier] = []
|
||||
}
|
||||
self.entityConfigurationsMapping[managedObjectClassName]?.insert(configurationName)
|
||||
self.finalConfigurationsByEntityIdentifier[entityIdentifier]?.insert(configurationName)
|
||||
}
|
||||
}
|
||||
storage.didAddToDataStack(self)
|
||||
return persistentStore
|
||||
}
|
||||
|
||||
internal func entityDescription(for entityIdentifier: EntityIdentifier) -> NSEntityDescription? {
|
||||
|
||||
return self.model.entityDescriptionsByEntityIdentifier[entityIdentifier]
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@@ -526,8 +571,8 @@ public final class DataStack: Equatable {
|
||||
// return true
|
||||
// }()
|
||||
|
||||
private var configurationStoreMapping = [String: NSPersistentStore]()
|
||||
private var entityConfigurationsMapping = [String: Set<String>]() // TODO: change key to AnyEntity
|
||||
private var persistentStoresByFinalConfiguration = [String: NSPersistentStore]()
|
||||
private var finalConfigurationsByEntityIdentifier = [EntityIdentifier: Set<String>]()
|
||||
|
||||
deinit {
|
||||
|
||||
@@ -545,6 +590,18 @@ public final class DataStack: Equatable {
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `DataStack`'s model.
|
||||
*/
|
||||
@available(*, deprecated: 3.1, message: "Use the new DataStack.entityTypesByName(for:) method passing `NSManagedObject.self` as argument.")
|
||||
public var entityTypesByName: [EntityName: NSManagedObject.Type] {
|
||||
|
||||
return self.entityTypesByName(for: NSManagedObject.self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Obsolete
|
||||
|
||||
@available(*, obsoleted: 3.0.0, renamed: "entityDescription(for:)")
|
||||
|
||||
166
Sources/Setup/Dynamic Models/Attribute.swift
Normal file
166
Sources/Setup/Dynamic Models/Attribute.swift
Normal file
@@ -0,0 +1,166 @@
|
||||
//
|
||||
// Attribute.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: Operators
|
||||
|
||||
infix operator .= : AssignmentPrecedence
|
||||
postfix operator *
|
||||
|
||||
|
||||
// MARK: - AttributeContainer
|
||||
|
||||
public enum AttributeContainer<O: ManagedObjectProtocol> {
|
||||
|
||||
// MARK: - Required
|
||||
|
||||
public final class Required<V: ImportableAttributeType>: AttributeProtocol {
|
||||
|
||||
public static func .= (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) {
|
||||
|
||||
attribute.value = value
|
||||
}
|
||||
|
||||
public static postfix func * (_ attribute: AttributeContainer<O>.Required<V>) -> V {
|
||||
|
||||
return attribute.value
|
||||
}
|
||||
|
||||
public let keyPath: String
|
||||
|
||||
public init(_ keyPath: String, `default`: V = V.cs_emptyValue()) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = `default`.cs_toImportableNativeType()
|
||||
}
|
||||
|
||||
public var value: V {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.accessRawObject()
|
||||
let key = self.keyPath
|
||||
let value = object.value(forKey: key)! as! V.ImportableNativeType
|
||||
return V.cs_fromImportableNativeType(value)!
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.accessRawObject()
|
||||
let key = self.keyPath
|
||||
object.setValue(newValue.cs_toImportableNativeType(), forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
|
||||
return V.cs_rawAttributeType
|
||||
}
|
||||
|
||||
internal let defaultValue: Any?
|
||||
internal let isOptional = false
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
|
||||
fatalError("\(O.self) property values should not be accessed")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Optional
|
||||
|
||||
public final class Optional<V: ImportableAttributeType>: AttributeProtocol {
|
||||
|
||||
public static func .= (_ attribute: AttributeContainer<O>.Optional<V>, _ value: V?) {
|
||||
|
||||
attribute.value = value
|
||||
}
|
||||
|
||||
public static postfix func * (_ attribute: AttributeContainer<O>.Optional<V>) -> V? {
|
||||
|
||||
return attribute.value
|
||||
}
|
||||
|
||||
public let keyPath: String
|
||||
|
||||
public init(_ keyPath: String, `default`: V? = nil) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = `default`?.cs_toImportableNativeType()
|
||||
}
|
||||
|
||||
public var value: V? {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.accessRawObject()
|
||||
let key = self.keyPath
|
||||
guard let value = object.value(forKey: key) as! V.ImportableNativeType? else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return V.cs_fromImportableNativeType(value)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.accessRawObject()
|
||||
let key = self.keyPath
|
||||
object.setValue(newValue?.cs_toImportableNativeType(), forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
|
||||
return V.cs_rawAttributeType
|
||||
}
|
||||
|
||||
internal let defaultValue: Any?
|
||||
internal let isOptional = true
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
|
||||
fatalError("\(O.self) property values should not be accessed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - AttributeProtocol
|
||||
|
||||
internal protocol AttributeProtocol: class {
|
||||
|
||||
static var attributeType: NSAttributeType { get }
|
||||
|
||||
var keyPath: String { get }
|
||||
var isOptional: Bool { get }
|
||||
var defaultValue: Any? { get }
|
||||
var accessRawObject: () -> NSManagedObject { get set }
|
||||
}
|
||||
198
Sources/Setup/Dynamic Models/Entity.swift
Normal file
198
Sources/Setup/Dynamic Models/Entity.swift
Normal file
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// Entity.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
import ObjectiveC
|
||||
|
||||
|
||||
// MARK: - EntityProtocol
|
||||
|
||||
public protocol EntityProtocol {
|
||||
|
||||
var entityDescription: NSEntityDescription { get }
|
||||
}
|
||||
|
||||
|
||||
// MARK: Entity
|
||||
|
||||
public struct Entity<O: ManagedObject>: EntityProtocol {
|
||||
|
||||
public let entityDescription: NSEntityDescription
|
||||
internal var dynamicClass: AnyClass {
|
||||
|
||||
return NSClassFromString(self.entityDescription.managedObjectClassName!)!
|
||||
}
|
||||
|
||||
public init(_ entityName: String) {
|
||||
|
||||
let dynamicClassName = String(reflecting: O.self)
|
||||
.appending("__\(entityName)")
|
||||
.replacingOccurrences(of: ".", with: "_")
|
||||
.replacingOccurrences(of: "<", with: "_")
|
||||
.replacingOccurrences(of: ">", with: "_")
|
||||
// TODO: assign entityName through ModelVersion and
|
||||
// TODO: set NSEntityDescription.userInfo AnyEntity
|
||||
let newClass: AnyClass?
|
||||
|
||||
if NSClassFromString(dynamicClassName) == nil {
|
||||
|
||||
newClass = objc_allocateClassPair(NSManagedObject.self, dynamicClassName, 0)
|
||||
}
|
||||
else {
|
||||
|
||||
newClass = nil
|
||||
}
|
||||
|
||||
defer {
|
||||
|
||||
if let newClass = newClass {
|
||||
|
||||
objc_registerClassPair(newClass)
|
||||
}
|
||||
}
|
||||
|
||||
let entityDescription = NSEntityDescription()
|
||||
entityDescription.userInfo = [
|
||||
EntityIdentifier.UserInfoKey.CoreStoreManagedObjectName: String(reflecting: O.self)
|
||||
]
|
||||
entityDescription.name = entityName
|
||||
entityDescription.managedObjectClassName = NSStringFromClass(NSManagedObject.self)
|
||||
// entityDescription.managedObjectClassName = dynamicClassName // TODO: return to NSManagedObject
|
||||
entityDescription.properties = type(of: self).initializeAttributes(Mirror(reflecting: O.meta))
|
||||
|
||||
self.entityDescription = entityDescription
|
||||
}
|
||||
|
||||
private static func initializeAttributes(_ mirror: Mirror) -> [NSAttributeDescription] {
|
||||
|
||||
var attributeDescriptions: [NSAttributeDescription] = []
|
||||
for child in mirror.children {
|
||||
|
||||
guard case let property as AttributeProtocol = child.value else {
|
||||
|
||||
continue
|
||||
}
|
||||
let attributeDescription = NSAttributeDescription()
|
||||
attributeDescription.name = property.keyPath
|
||||
attributeDescription.attributeType = type(of: property).attributeType
|
||||
attributeDescription.isOptional = property.isOptional
|
||||
attributeDescription.defaultValue = property.defaultValue
|
||||
attributeDescriptions.append(attributeDescription)
|
||||
}
|
||||
if let baseEntityAttributeDescriptions = mirror.superclassMirror.flatMap(self.initializeAttributes) {
|
||||
|
||||
attributeDescriptions.append(contentsOf: baseEntityAttributeDescriptions)
|
||||
}
|
||||
return attributeDescriptions
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - EntityIdentifier
|
||||
|
||||
internal struct EntityIdentifier: Hashable {
|
||||
|
||||
// MARK: - Category
|
||||
|
||||
internal enum Category: Int {
|
||||
|
||||
case coreData
|
||||
case coreStore
|
||||
}
|
||||
|
||||
|
||||
// MARK: -
|
||||
|
||||
internal let category: Category
|
||||
internal let interfacedClassName: String
|
||||
|
||||
internal init(_ type: NSManagedObject.Type) {
|
||||
|
||||
self.category = .coreData
|
||||
self.interfacedClassName = String(reflecting: type)
|
||||
}
|
||||
|
||||
internal init(_ type: ManagedObject.Type) {
|
||||
|
||||
self.category = .coreStore
|
||||
self.interfacedClassName = String(reflecting: type)
|
||||
}
|
||||
|
||||
internal init(_ type: ManagedObjectProtocol.Type) {
|
||||
|
||||
switch type {
|
||||
|
||||
case let type as NSManagedObject.Type:
|
||||
self.init(type)
|
||||
|
||||
case let type as ManagedObject.Type:
|
||||
self.init(type)
|
||||
|
||||
default:
|
||||
CoreStore.abort("\(cs_typeName(ManagedObjectProtocol.self)) is not meant to be implemented by external types.")
|
||||
}
|
||||
}
|
||||
|
||||
internal init(_ entityDescription: NSEntityDescription) {
|
||||
|
||||
if let coreStoreManagedObjectName = entityDescription.userInfo?[EntityIdentifier.UserInfoKey.CoreStoreManagedObjectName] as! String? {
|
||||
|
||||
self.category = .coreStore
|
||||
self.interfacedClassName = coreStoreManagedObjectName
|
||||
}
|
||||
else {
|
||||
|
||||
self.category = .coreData
|
||||
self.interfacedClassName = entityDescription.managedObjectClassName!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
static func == (lhs: EntityIdentifier, rhs: EntityIdentifier) -> Bool {
|
||||
|
||||
return lhs.category == rhs.category
|
||||
&& lhs.interfacedClassName == rhs.interfacedClassName
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
var hashValue: Int {
|
||||
|
||||
return self.category.hashValue
|
||||
^ self.interfacedClassName.hashValue
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate enum UserInfoKey {
|
||||
|
||||
fileprivate static let CoreStoreManagedObjectName = "CoreStoreManagedObjectName"
|
||||
}
|
||||
}
|
||||
68
Sources/Setup/Dynamic Models/ManagedObject.swift
Normal file
68
Sources/Setup/Dynamic Models/ManagedObject.swift
Normal file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// ManagedObject.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - ManagedObject
|
||||
|
||||
open class ManagedObject: ManagedObjectProtocol {
|
||||
|
||||
public required init(_ object: NSManagedObject) {
|
||||
|
||||
self.isMeta = false
|
||||
self.rawObject = object
|
||||
self.initializeAttributes(Mirror(reflecting: self), { [unowned object] in object })
|
||||
}
|
||||
|
||||
public required init(asMeta: Void) {
|
||||
|
||||
self.isMeta = true
|
||||
self.rawObject = nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal let rawObject: NSManagedObject?
|
||||
internal let isMeta: Bool
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func initializeAttributes(_ mirror: Mirror, _ accessRawObject: @escaping () -> NSManagedObject) {
|
||||
|
||||
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, accessRawObject) })
|
||||
for child in mirror.children {
|
||||
|
||||
guard case let property as AttributeProtocol = child.value else {
|
||||
|
||||
continue
|
||||
}
|
||||
property.accessRawObject = accessRawObject
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Sources/Setup/Dynamic Models/ManagedObjectProtocol.swift
Normal file
119
Sources/Setup/Dynamic Models/ManagedObjectProtocol.swift
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// ManagedObjectProtocol.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - ManagedObjectProtocol
|
||||
|
||||
public protocol ManagedObjectProtocol: class {
|
||||
|
||||
static func cs_forceCreate(entityDescription: NSEntityDescription, into context: NSManagedObjectContext, assignTo store: NSPersistentStore) -> Self
|
||||
|
||||
static func cs_from(object: NSManagedObject) -> Self
|
||||
}
|
||||
|
||||
public extension ManagedObjectProtocol where Self: ManagedObject {
|
||||
|
||||
public typealias Attribute = AttributeContainer<Self>
|
||||
|
||||
@inline(__always)
|
||||
public static func keyPath<O: ManagedObject, V: ImportableAttributeType>(_ attribute: (Self) -> AttributeContainer<O>.Required<V>) -> String {
|
||||
|
||||
return attribute(self.meta).keyPath
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func keyPath<O: ManagedObject, V: ImportableAttributeType>(_ attribute: (Self) -> AttributeContainer<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
|
||||
|
||||
extension NSManagedObject: ManagedObjectProtocol {
|
||||
|
||||
// MARK: ManagedObjectProtocol
|
||||
|
||||
public static func cs_from(object: NSManagedObject) -> Self {
|
||||
|
||||
@inline(__always)
|
||||
func forceCast<T: NSManagedObject>(_ value: Any) -> T {
|
||||
|
||||
return value as! T
|
||||
}
|
||||
return forceCast(object)
|
||||
}
|
||||
|
||||
public static func cs_forceCreate(entityDescription: NSEntityDescription, into context: NSManagedObjectContext, assignTo store: NSPersistentStore) -> Self {
|
||||
|
||||
let object = self.init(entity: entityDescription, insertInto: context)
|
||||
defer {
|
||||
|
||||
context.assign(object, to: store)
|
||||
}
|
||||
return object
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ManagedObject
|
||||
|
||||
extension ManagedObject {
|
||||
|
||||
// MARK: ManagedObjectProtocol
|
||||
|
||||
public static func cs_from(object: NSManagedObject) -> Self {
|
||||
|
||||
return self.init(object)
|
||||
}
|
||||
|
||||
public static func cs_forceCreate(entityDescription: NSEntityDescription, into context: NSManagedObjectContext, assignTo store: NSPersistentStore) -> Self {
|
||||
|
||||
let type = NSClassFromString(entityDescription.managedObjectClassName!)! as! NSManagedObject.Type
|
||||
let object = type.init(entity: entityDescription, insertInto: context)
|
||||
defer {
|
||||
|
||||
context.assign(object, to: store)
|
||||
}
|
||||
return self.init(object)
|
||||
}
|
||||
}
|
||||
76
Sources/Setup/Dynamic Models/ObjectModel.swift
Normal file
76
Sources/Setup/Dynamic Models/ObjectModel.swift
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// ObjectModel.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - ObjectModel
|
||||
|
||||
public final class ObjectModel {
|
||||
|
||||
public let version: String
|
||||
|
||||
public convenience init(version: String, entities: [EntityProtocol]) {
|
||||
|
||||
self.init(version: version, configurationEntities: [DataStack.defaultConfigurationName: entities])
|
||||
}
|
||||
|
||||
public required init(version: String, configurationEntities: [String: [EntityProtocol]]) {
|
||||
|
||||
self.version = version
|
||||
|
||||
var entityConfigurations: [String: Set<NSEntityDescription>] = [:]
|
||||
for (configuration, entities) in configurationEntities {
|
||||
|
||||
entityConfigurations[configuration] = Set(entities.map({ $0.entityDescription }))
|
||||
}
|
||||
let allEntities = Set(entityConfigurations.map({ $0.value }).joined())
|
||||
entityConfigurations[DataStack.defaultConfigurationName] = allEntities
|
||||
|
||||
self.entityConfigurations = entityConfigurations
|
||||
self.entities = allEntities
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal let entities: Set<NSEntityDescription>
|
||||
internal let entityConfigurations: [String: Set<NSEntityDescription>]
|
||||
|
||||
internal func createModel() -> NSManagedObjectModel {
|
||||
|
||||
let model = NSManagedObjectModel()
|
||||
model.entities = self.entities.sorted(by: { $0.name! < $1.name! })
|
||||
for (configuration, entities) in self.entityConfigurations {
|
||||
|
||||
model.setEntities(
|
||||
entities.sorted(by: { $0.name! < $1.name! }),
|
||||
forConfigurationName: configuration
|
||||
)
|
||||
}
|
||||
return model
|
||||
}
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
//
|
||||
// Prototype.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Created by John Estropia on 2017/04/03.
|
||||
// Copyright © 2017 John Rommel Estropia. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
import ObjectiveC
|
||||
|
||||
|
||||
public protocol ManagedObjectProtocol: class {}
|
||||
|
||||
public protocol EntityProtocol {
|
||||
|
||||
var entityDescription: NSEntityDescription { get }
|
||||
}
|
||||
|
||||
internal protocol AttributeProtocol: class {
|
||||
|
||||
static var attributeType: NSAttributeType { get }
|
||||
var keyPath: String { get }
|
||||
var defaultValue: Any? { get }
|
||||
var accessRawObject: () -> NSManagedObject { get set }
|
||||
}
|
||||
|
||||
open class CoreStoreManagedObject: ManagedObjectProtocol {
|
||||
|
||||
internal let rawObject: NSManagedObject?
|
||||
internal let isMeta: Bool
|
||||
|
||||
public required init(_ object: NSManagedObject?) {
|
||||
|
||||
self.isMeta = object == nil
|
||||
self.rawObject = object
|
||||
|
||||
guard let object = object else {
|
||||
|
||||
return
|
||||
}
|
||||
self.initializeAttributes(Mirror(reflecting: self), { [unowned object] in object })
|
||||
}
|
||||
|
||||
private func initializeAttributes(_ mirror: Mirror, _ accessRawObject: @escaping () -> NSManagedObject) {
|
||||
|
||||
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, accessRawObject) })
|
||||
for child in mirror.children {
|
||||
|
||||
guard case let property as AttributeProtocol = child.value else {
|
||||
|
||||
continue
|
||||
}
|
||||
property.accessRawObject = accessRawObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct Entity<O: CoreStoreManagedObject>: EntityProtocol {
|
||||
|
||||
public let entityDescription: NSEntityDescription
|
||||
internal var dynamicClass: AnyClass {
|
||||
|
||||
return NSClassFromString(self.entityDescription.managedObjectClassName!)!
|
||||
}
|
||||
|
||||
public init(_ entityName: String) {
|
||||
|
||||
let dynamicClassName = String(reflecting: O.self)
|
||||
.appending("__\(entityName)")
|
||||
.replacingOccurrences(of: ".", with: "_")
|
||||
.replacingOccurrences(of: "<", with: "_")
|
||||
.replacingOccurrences(of: ">", with: "_")
|
||||
// TODO: assign entityName through ModelVersion and
|
||||
// TODO: set NSEntityDescription.userInfo AnyEntity
|
||||
let newClass: AnyClass?
|
||||
|
||||
if NSClassFromString(dynamicClassName) == nil {
|
||||
|
||||
newClass = objc_allocateClassPair(NSManagedObject.self, dynamicClassName, 0)
|
||||
}
|
||||
else {
|
||||
|
||||
newClass = nil
|
||||
}
|
||||
|
||||
defer {
|
||||
|
||||
if let newClass = newClass {
|
||||
|
||||
objc_registerClassPair(newClass)
|
||||
}
|
||||
}
|
||||
|
||||
let entityDescription = NSEntityDescription()
|
||||
entityDescription.name = entityName
|
||||
entityDescription.managedObjectClassName = dynamicClassName // TODO: return to NSManagedObject
|
||||
entityDescription.properties = type(of: self).initializeAttributes(Mirror(reflecting: O.meta))
|
||||
|
||||
self.entityDescription = entityDescription
|
||||
}
|
||||
|
||||
private static func initializeAttributes(_ mirror: Mirror) -> [NSAttributeDescription] {
|
||||
|
||||
var attributeDescriptions: [NSAttributeDescription] = []
|
||||
for child in mirror.children {
|
||||
|
||||
guard case let property as AttributeProtocol = child.value else {
|
||||
|
||||
continue
|
||||
}
|
||||
let attributeDescription = NSAttributeDescription()
|
||||
attributeDescription.name = property.keyPath
|
||||
attributeDescription.attributeType = type(of: property).attributeType
|
||||
attributeDescription.isOptional = false
|
||||
attributeDescription.defaultValue = property.defaultValue
|
||||
attributeDescriptions.append(attributeDescription)
|
||||
}
|
||||
if let baseEntityAttributeDescriptions = mirror.superclassMirror.flatMap(self.initializeAttributes) {
|
||||
|
||||
attributeDescriptions.append(contentsOf: baseEntityAttributeDescriptions)
|
||||
}
|
||||
return attributeDescriptions
|
||||
}
|
||||
}
|
||||
|
||||
public enum AttributeContainer<O: ManagedObjectProtocol> {
|
||||
|
||||
public final class Required<V: ImportableAttributeType>: AttributeProtocol {
|
||||
|
||||
static var attributeType: NSAttributeType { return V.cs_rawAttributeType }
|
||||
|
||||
let keyPath: String
|
||||
let defaultValue: Any?
|
||||
var accessRawObject: () -> NSManagedObject = { fatalError("\(O.self) property values should not be accessed") }
|
||||
|
||||
var value: V {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.accessRawObject()
|
||||
let key = self.keyPath
|
||||
let value = object.value(forKey: key)! as! V.ImportableNativeType
|
||||
return V.cs_fromImportableNativeType(value)!
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.accessRawObject()
|
||||
let key = self.keyPath
|
||||
object.setValue(newValue.cs_toImportableNativeType(), forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
public init(_ keyPath: String, `default`: V = V.cs_emptyValue()) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = `default`
|
||||
}
|
||||
}
|
||||
|
||||
public final class Optional<V: ImportableAttributeType>: AttributeProtocol {
|
||||
|
||||
static var attributeType: NSAttributeType { return V.cs_rawAttributeType }
|
||||
|
||||
let keyPath: String
|
||||
let defaultValue: Any?
|
||||
var accessRawObject: () -> NSManagedObject = { fatalError("\(O.self) property values should not be accessed") }
|
||||
|
||||
var value: V? {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.accessRawObject()
|
||||
let key = self.keyPath
|
||||
guard let value = object.value(forKey: key) as! V.ImportableNativeType? else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return V.cs_fromImportableNativeType(value)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.accessRawObject()
|
||||
let key = self.keyPath
|
||||
object.setValue(newValue?.cs_toImportableNativeType(), forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
public init(_ keyPath: String, `default`: V? = nil) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = `default`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension ManagedObjectProtocol where Self: CoreStoreManagedObject {
|
||||
|
||||
public typealias Attribute = AttributeContainer<Self>
|
||||
|
||||
internal static var meta: Self {
|
||||
|
||||
return self.init(nil)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func keyPath<O: CoreStoreManagedObject, V: ImportableAttributeType>(_ attribute: (Self) -> AttributeContainer<O>.Required<V>) -> String {
|
||||
|
||||
return attribute(self.meta).keyPath
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func keyPath<O: CoreStoreManagedObject, V: ImportableAttributeType>(_ attribute: (Self) -> AttributeContainer<O>.Optional<V>) -> String {
|
||||
|
||||
return attribute(self.meta).keyPath
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func `where`(_ condition: (Self) -> Where) -> Where {
|
||||
|
||||
return condition(self.meta)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//: ### Convenience Operators
|
||||
|
||||
infix operator .= : AssignmentPrecedence
|
||||
public func .= <O: ManagedObjectProtocol, V: ImportableAttributeType>(_ attribute: AttributeContainer<O>.Required<V>, _ value: V) {
|
||||
|
||||
attribute.value = value
|
||||
}
|
||||
public func .= <O: ManagedObjectProtocol, V: ImportableAttributeType>(_ attribute: AttributeContainer<O>.Optional<V>, _ value: V?) {
|
||||
|
||||
attribute.value = value
|
||||
}
|
||||
|
||||
postfix operator *
|
||||
public postfix func * <O: ManagedObjectProtocol, V: ImportableAttributeType>(_ attribute: AttributeContainer<O>.Required<V>) -> V {
|
||||
|
||||
return attribute.value
|
||||
}
|
||||
public postfix func * <O: ManagedObjectProtocol, V: ImportableAttributeType>(_ attribute: AttributeContainer<O>.Optional<V>) -> V? {
|
||||
|
||||
return attribute.value
|
||||
}
|
||||
|
||||
public extension AttributeContainer.Required where V: CVarArg {
|
||||
|
||||
public static func == (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
|
||||
|
||||
return Where(attribute.keyPath, isEqualTo: value)
|
||||
}
|
||||
public static func < (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
|
||||
|
||||
return Where("%K < %@", attribute.keyPath, value)
|
||||
}
|
||||
public static func > (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
|
||||
|
||||
return Where("%K > %@", attribute.keyPath, value)
|
||||
}
|
||||
public static func <= (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
|
||||
|
||||
return Where("%K <= %@", attribute.keyPath, value)
|
||||
}
|
||||
public static func >= (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
|
||||
|
||||
return Where("%K >= %@", attribute.keyPath, value)
|
||||
}
|
||||
public static func != (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
|
||||
|
||||
return Where("%K != %@", attribute.keyPath, value)
|
||||
}
|
||||
}
|
||||
public extension AttributeContainer.Optional where V: CVarArg {
|
||||
|
||||
public static func == (_ attribute: AttributeContainer<O>.Optional<V>, _ value: V?) -> Where {
|
||||
|
||||
return Where(attribute.keyPath, isEqualTo: value)
|
||||
}
|
||||
}
|
||||
|
||||
public final class ModelVersion {
|
||||
|
||||
public let version: String
|
||||
internal let entities: Set<NSEntityDescription>
|
||||
internal let entityConfigurations: [String: Set<NSEntityDescription>]
|
||||
|
||||
public convenience init(version: String, entities: [EntityProtocol]) {
|
||||
|
||||
self.init(version: version, configurationEntities: [Into.defaultConfigurationName: entities])
|
||||
}
|
||||
|
||||
public required init(version: String, configurationEntities: [String: [EntityProtocol]]) {
|
||||
|
||||
self.version = version
|
||||
|
||||
var entityConfigurations: [String: Set<NSEntityDescription>] = [:]
|
||||
for (configuration, entities) in configurationEntities {
|
||||
|
||||
entityConfigurations[configuration] = Set(entities.map({ $0.entityDescription }))
|
||||
}
|
||||
let allEntities = Set(entityConfigurations.map({ $0.value }).joined())
|
||||
entityConfigurations[Into.defaultConfigurationName] = allEntities
|
||||
|
||||
self.entityConfigurations = entityConfigurations
|
||||
self.entities = allEntities
|
||||
}
|
||||
|
||||
internal func createModel() -> NSManagedObjectModel {
|
||||
|
||||
let model = NSManagedObjectModel()
|
||||
model.entities = self.entities.sorted(by: { $0.name! < $1.name! })
|
||||
for (configuration, entities) in self.entityConfigurations {
|
||||
|
||||
model.setEntities(
|
||||
entities.sorted(by: { $0.name! < $1.name! }),
|
||||
forConfigurationName: configuration
|
||||
)
|
||||
}
|
||||
return model
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public final class ICloudStore: CloudStorage {
|
||||
- parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration.
|
||||
- parameter cloudStorageOptions: When the `ICloudStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
|
||||
*/
|
||||
public required init?(ubiquitousContentName: String, ubiquitousContentTransactionLogsSubdirectory: String, ubiquitousContainerID: String? = nil, ubiquitousPeerToken: String? = nil, configuration: String? = nil, cloudStorageOptions: CloudStorageOptions = nil) {
|
||||
public required init?(ubiquitousContentName: String, ubiquitousContentTransactionLogsSubdirectory: String, ubiquitousContainerID: String? = nil, ubiquitousPeerToken: String? = nil, configuration: ModelConfiguration = nil, cloudStorageOptions: CloudStorageOptions = nil) {
|
||||
|
||||
CoreStore.assert(
|
||||
!ubiquitousContentName.isEmpty,
|
||||
@@ -258,7 +258,7 @@ public final class ICloudStore: CloudStorage {
|
||||
/**
|
||||
The configuration name in the model file
|
||||
*/
|
||||
public let configuration: String?
|
||||
public let configuration: ModelConfiguration
|
||||
|
||||
/**
|
||||
The options dictionary for the `NSPersistentStore`. For `SQLiteStore`s, this is always set to
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class InMemoryStore: StorageInterface, DefaultInitializableStore {
|
||||
Initializes an `InMemoryStore` for the specified configuration
|
||||
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration.
|
||||
*/
|
||||
public init(configuration: String?) {
|
||||
public init(configuration: ModelConfiguration) {
|
||||
|
||||
self.configuration = configuration
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public final class InMemoryStore: StorageInterface, DefaultInitializableStore {
|
||||
/**
|
||||
The configuration name in the model file
|
||||
*/
|
||||
public let configuration: String?
|
||||
public let configuration: ModelConfiguration
|
||||
|
||||
/**
|
||||
The options dictionary for the `NSPersistentStore`. For `InMemoryStore`s, this is always set to `nil`.
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore {
|
||||
- parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration.
|
||||
- parameter localStorageOptions: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
|
||||
*/
|
||||
public init(fileURL: URL, configuration: String? = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) {
|
||||
public init(fileURL: URL, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) {
|
||||
|
||||
self.fileURL = fileURL
|
||||
self.configuration = configuration
|
||||
@@ -61,7 +61,7 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore {
|
||||
- parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration.
|
||||
- parameter localStorageOptions: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
|
||||
*/
|
||||
public init(fileName: String, configuration: String? = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) {
|
||||
public init(fileName: String, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) {
|
||||
|
||||
self.fileURL = LegacySQLiteStore.defaultRootDirectory.appendingPathComponent(
|
||||
fileName,
|
||||
@@ -118,7 +118,7 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore {
|
||||
/**
|
||||
The configuration name in the model file
|
||||
*/
|
||||
public let configuration: String?
|
||||
public let configuration: ModelConfiguration
|
||||
|
||||
/**
|
||||
The options dictionary for the `NSPersistentStore`. For `SQLiteStore`s, this is always set to
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore {
|
||||
- parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models (*.xcmappingmodel) for migration.
|
||||
- parameter localStorageOptions: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
|
||||
*/
|
||||
public init(fileURL: URL, configuration: String? = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) {
|
||||
public init(fileURL: URL, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) {
|
||||
|
||||
self.fileURL = fileURL
|
||||
self.configuration = configuration
|
||||
@@ -60,7 +60,7 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore {
|
||||
- parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models (*.xcmappingmodel) for migration
|
||||
- parameter localStorageOptions: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
|
||||
*/
|
||||
public init(fileName: String, configuration: String? = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) {
|
||||
public init(fileName: String, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) {
|
||||
|
||||
self.fileURL = SQLiteStore.defaultRootDirectory
|
||||
.appendingPathComponent(fileName, isDirectory: false)
|
||||
@@ -96,7 +96,7 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore {
|
||||
/**
|
||||
The configuration name in the model file
|
||||
*/
|
||||
public let configuration: String?
|
||||
public let configuration: ModelConfiguration
|
||||
|
||||
/**
|
||||
The options dictionary for the `NSPersistentStore`. For `SQLiteStore`s, this is always set to
|
||||
|
||||
@@ -41,7 +41,7 @@ public protocol StorageInterface: class {
|
||||
/**
|
||||
The configuration name in the model file
|
||||
*/
|
||||
var configuration: String? { get }
|
||||
var configuration: ModelConfiguration { get }
|
||||
|
||||
/**
|
||||
The options dictionary for the `NSPersistentStore`
|
||||
@@ -166,7 +166,7 @@ internal extension LocalStorage {
|
||||
internal func matchesPersistentStore(_ persistentStore: NSPersistentStore) -> Bool {
|
||||
|
||||
return persistentStore.type == type(of: self).storeType
|
||||
&& persistentStore.configurationName == (self.configuration ?? Into.defaultConfigurationName)
|
||||
&& persistentStore.configurationName == (self.configuration ?? DataStack.defaultConfigurationName)
|
||||
&& persistentStore.url == self.fileURL
|
||||
}
|
||||
}
|
||||
@@ -250,7 +250,7 @@ internal extension CloudStorage {
|
||||
internal func matchesPersistentStore(_ persistentStore: NSPersistentStore) -> Bool {
|
||||
|
||||
guard persistentStore.type == type(of: self).storeType
|
||||
&& persistentStore.configurationName == (self.configuration ?? Into.defaultConfigurationName) else {
|
||||
&& persistentStore.configurationName == (self.configuration ?? DataStack.defaultConfigurationName) else {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user