WIP: prototype for ManagedObjectProtocol

This commit is contained in:
John Estropia
2017-04-05 21:56:19 +09:00
parent 258c237100
commit 8b77e4e5a0
44 changed files with 1244 additions and 823 deletions

View File

@@ -0,0 +1,66 @@
//
// CoreStoreStrings.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: - XcdatamodelFilename
/**
A `String` that pertains to the name of an *.xcdatamodeld file (without the file extension).
*/
public typealias XcdatamodelFilename = String
// MARK: - ModelConfiguration
/**
An `Optional<String>` that pertains to the name of a "Configuration" which particular groups of entities may belong to. When `nil`, pertains to the default configuration which includes all entities.
*/
public typealias ModelConfiguration = String?
// MARK: - ModelVersion
/**
An `String` that pertains to the name of a versioned *.xcdatamodeld file (without the file extension).
*/
public typealias ModelVersion = String
// MARK: - EntityName
/**
An `String` that pertains to an Entity name.
*/
public typealias EntityName = String
// MARK: - ClassName
/**
An `String` that pertains to a dynamically-accessable class name (usable with NSClassFromString(...)).
*/
public typealias ClassName = String

View File

@@ -0,0 +1,69 @@
//
// Attribute+Querying.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: - AttributeContainer.Required
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)
}
}
// MARK: - AttributeContainer.Optional
public extension AttributeContainer.Optional where V: CVarArg {
public static func == (_ attribute: AttributeContainer<O>.Optional<V>, _ value: V?) -> Where {
return Where(attribute.keyPath, isEqualTo: value)
}
}

View File

@@ -121,7 +121,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/
public func fetchOne<T: NSManagedObject>(_ from: From<T>, _ fetchClauses: FetchClause...) -> T? {
public func fetchOne<T: ManagedObjectProtocol>(_ from: From<T>, _ fetchClauses: FetchClause...) -> T? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
@@ -137,7 +137,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/
public func fetchOne<T: NSManagedObject>(_ from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
public func fetchOne<T: ManagedObjectProtocol>(_ from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
CoreStore.assert(
self.isRunningInAllowedQueue(),

View File

@@ -39,18 +39,18 @@ import CoreData
let person = transaction.fetchOne(From<MyPersonEntity>("Configuration1"))
```
*/
public struct From<T: NSManagedObject> {
public struct From<T: ManagedObjectProtocol> {
/**
The associated `NSManagedObject` entity class
The associated `NSManagedObject` or `ManagedObject` entity class
*/
public let entityClass: AnyClass
public let entityClass: T.Type
/**
The `NSPersistentStore` configuration names to associate objects from.
May contain `String`s to pertain to named configurations, or `nil` to pertain to the default configuration
*/
public let configurations: [String?]?
public let configurations: [ModelConfiguration]?
/**
Initializes a `From` clause.
@@ -68,38 +68,22 @@ public struct From<T: NSManagedObject> {
```
let people = transaction.fetchAll(From<MyPersonEntity>())
```
- parameter entity: the associated `NSManagedObject` type
- parameter entity: the associated `NSManagedObject` or `ManagedObject` type
*/
public init(_ entity: T.Type) {
self.init(entityClass: entity, configurations: nil)
}
/**
Initializes a `From` clause with the specified entity class.
```
let people = transaction.fetchAll(From<MyPersonEntity>())
```
- parameter entityClass: the associated `NSManagedObject` entity class
*/
public init(_ entityClass: AnyClass) {
CoreStore.assert(
entityClass is T.Type,
"Attempted to create generic type \(cs_typeName(From<T>.self)) with entity class \(cs_typeName(entityClass))"
)
self.init(entityClass: entityClass, configurations: nil)
}
/**
Initializes a `From` clause with the specified configurations.
```
let people = transaction.fetchAll(From<MyPersonEntity>(nil, "Configuration1"))
```
- parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
- parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject` or `ManagedObject`'s entity type. Set to `nil` to use the default configuration.
- parameter otherConfigurations: an optional list of other configuration names to associate objects from (see `configuration` parameter)
*/
public init(_ configuration: String?, _ otherConfigurations: String?...) {
public init(_ configuration: ModelConfiguration, _ otherConfigurations: ModelConfiguration...) {
self.init(entityClass: T.self, configurations: [configuration] + otherConfigurations)
}
@@ -109,9 +93,9 @@ public struct From<T: NSManagedObject> {
```
let people = transaction.fetchAll(From<MyPersonEntity>(["Configuration1", "Configuration2"]))
```
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject` or `ManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ configurations: [String?]) {
public init(_ configurations: [ModelConfiguration]) {
self.init(entityClass: T.self, configurations: configurations)
}
@@ -121,11 +105,11 @@ public struct From<T: NSManagedObject> {
```
let people = transaction.fetchAll(From(MyPersonEntity.self, nil, "Configuration1"))
```
- parameter entity: the associated `NSManagedObject` type
- parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
- parameter entity: the associated `NSManagedObject` or `ManagedObject` type
- parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject` or `ManagedObject`'s entity type. Set to `nil` to use the default configuration.
- parameter otherConfigurations: an optional list of other configuration names to associate objects from (see `configuration` parameter)
*/
public init(_ entity: T.Type, _ configuration: String?, _ otherConfigurations: String?...) {
public init(_ entity: T.Type, _ configuration: ModelConfiguration, _ otherConfigurations: ModelConfiguration...) {
self.init(entityClass: entity, configurations: [configuration] + otherConfigurations)
}
@@ -135,55 +119,29 @@ public struct From<T: NSManagedObject> {
```
let people = transaction.fetchAll(From(MyPersonEntity.self, ["Configuration1", "Configuration1"]))
```
- parameter entity: the associated `NSManagedObject` type
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
- parameter entity: the associated `NSManagedObject` or `ManagedObject` type
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject` or `ManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entity: T.Type, _ configurations: [String?]) {
public init(_ entity: T.Type, _ configurations: [ModelConfiguration]) {
self.init(entityClass: entity, configurations: configurations)
}
/**
Initializes a `From` clause with the specified configurations.
```
let people = transaction.fetchAll(From(MyPersonEntity.self, nil, "Configuration1"))
```
- parameter entity: the associated `NSManagedObject` entity class
- parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
- parameter otherConfigurations: an optional list of other configuration names to associate objects from (see `configuration` parameter)
*/
public init(_ entityClass: AnyClass, _ configuration: String?, _ otherConfigurations: String?...) {
CoreStore.assert(
entityClass is T.Type,
"Attempted to create generic type \(cs_typeName(From<T>.self)) with entity class \(cs_typeName(entityClass))"
)
self.init(entityClass: entityClass, configurations: [configuration] + otherConfigurations)
}
/**
Initializes a `From` clause with the specified configurations.
```
let people = transaction.fetchAll(From(MyPersonEntity.self, ["Configuration1", "Configuration1"]))
```
- parameter entity: the associated `NSManagedObject` entity class
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entityClass: AnyClass, _ configurations: [String?]) {
CoreStore.assert(
entityClass is T.Type,
"Attempted to create generic type \(cs_typeName(From<T>.self)) with entity class \(cs_typeName(entityClass))"
)
self.init(entityClass: entityClass, configurations: configurations)
}
// MARK: Internal
internal let findPersistentStores: (_ context: NSManagedObjectContext) -> [NSPersistentStore]?
internal init(entityClass: T.Type, configurations: [ModelConfiguration]?, findPersistentStores: @escaping (_ context: NSManagedObjectContext) -> [NSPersistentStore]?) {
self.entityClass = entityClass
self.configurations = configurations
self.findPersistentStores = findPersistentStores
}
internal func applyToFetchRequest<ResultType: NSFetchRequestResult>(_ fetchRequest: NSFetchRequest<ResultType>, context: NSManagedObjectContext, applyAffectedStores: Bool = true) -> Bool {
fetchRequest.entity = context.entityDescriptionForEntityClass(self.entityClass)
fetchRequest.entity = context.parentStack!.entityDescription(for: EntityIdentifier(self.entityClass))!
guard applyAffectedStores else {
return true
@@ -206,30 +164,21 @@ public struct From<T: NSManagedObject> {
return stores?.isEmpty == false
}
internal func downcast() -> From<NSManagedObject> {
return From<NSManagedObject>(
entityClass: self.entityClass,
configurations: self.configurations,
findPersistentStores: self.findPersistentStores
)
}
// MARK: Private
private let findPersistentStores: (_ context: NSManagedObjectContext) -> [NSPersistentStore]?
private init(entityClass: AnyClass, configurations: [String?]?) {
private init(entityClass: T.Type, configurations: [ModelConfiguration]?) {
self.entityClass = entityClass
self.configurations = configurations
let entityIdentifier = EntityIdentifier(entityClass)
if let configurations = configurations {
let configurationsSet = Set(configurations.map { $0 ?? Into.defaultConfigurationName })
let configurationsSet = Set(configurations.map({ $0 ?? DataStack.defaultConfigurationName }))
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(entityClass)?.filter {
return context.parentStack?.persistentStores(for: entityIdentifier)?.filter {
return configurationsSet.contains($0.configurationName)
}
@@ -239,15 +188,8 @@ public struct From<T: NSManagedObject> {
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(entityClass)
return context.parentStack?.persistentStores(for: entityIdentifier)
}
}
}
private init(entityClass: AnyClass, configurations: [String?]?, findPersistentStores: @escaping (_ context: NSManagedObjectContext) -> [NSPersistentStore]?) {
self.entityClass = entityClass
self.configurations = configurations
self.findPersistentStores = findPersistentStores
}
}

View File

@@ -50,8 +50,7 @@ public extension BaseDataTransaction {
return try autoreleasepool {
let entityType = into.entityClass as! T.Type
let entityType = into.entityClass
guard entityType.shouldInsert(from: source, in: self) else {
return nil
@@ -111,7 +110,7 @@ public extension BaseDataTransaction {
return try sourceArray.flatMap { (source) -> T? in
let entityType = into.entityClass as! T.Type
let entityType = into.entityClass
guard entityType.shouldInsert(from: source, in: self) else {
return nil
@@ -145,7 +144,7 @@ public extension BaseDataTransaction {
return try autoreleasepool {
let entityType = into.entityClass as! T.Type
let entityType = into.entityClass
let uniqueIDKeyPath = entityType.uniqueIDKeyPath
guard let uniqueIDValue = try entityType.uniqueID(from: source, in: self) else {
@@ -198,7 +197,7 @@ public extension BaseDataTransaction {
return try autoreleasepool {
let entityType = into.entityClass as! T.Type
let entityType = into.entityClass
var importSourceByID = Dictionary<T.UniqueIDType, T.ImportSource>()
let sortedIDs = try autoreleasepool {

View File

@@ -66,7 +66,7 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll
}
else {
guard let from = (fetchRequest.entity.flatMap { $0.managedObjectClassName }).flatMap(NSClassFromString).flatMap(From.init) else {
guard let from = (fetchRequest.entity.flatMap { $0.managedObjectClassName }).flatMap(NSClassFromString).flatMap({ From<T>($0 as! T.Type) }) else {
CoreStore.abort("Attempted to create a \(CoreStoreFetchedResultsController.self) without a \(cs_typeName(From<T>.self)) clause or an \(cs_typeName(NSEntityDescription.self)).")
}

View File

@@ -54,25 +54,6 @@ internal extension NSManagedObjectContext {
}
}
@nonobjc
internal func entityDescriptionForEntityType(_ entity: NSManagedObject.Type) -> NSEntityDescription? {
return self.entityDescriptionForEntityClass(entity)
}
@nonobjc
internal func entityDescriptionForEntityClass(_ entity: AnyClass) -> NSEntityDescription? {
guard let entityName = self.parentStack?.entityNameForEntityClass(entity) else {
return nil
}
return NSEntityDescription.entity(
forEntityName: entityName,
in: self
)
}
@nonobjc
internal func setupForCoreStoreWithContextName(_ contextName: String) {

View File

@@ -95,14 +95,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
}
@nonobjc
public func fetchOne<T: NSManagedObject>(_ from: From<T>, _ fetchClauses: FetchClause...) -> T? {
public func fetchOne<T: ManagedObjectProtocol>(_ from: From<T>, _ fetchClauses: FetchClause...) -> T? {
return self.fetchOne(from, fetchClauses)
}
@nonobjc
public func fetchOne<T: NSManagedObject>(_ from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
public func fetchOne<T: ManagedObjectProtocol>(_ from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
let fetchRequest = CoreStoreFetchRequest()
let storeFound = from.applyToFetchRequest(fetchRequest, context: self)
@@ -116,6 +115,7 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
return nil
}
return self.fetchOne(fetchRequest.dynamicCast())
.flatMap(from.entityClass.cs_from)
}
@nonobjc

View File

@@ -157,22 +157,50 @@ internal extension NSManagedObjectModel {
}
@nonobjc
internal func entityNameForClass(_ entityClass: AnyClass) -> String {
internal var entityDescriptionsByEntityIdentifier: [EntityIdentifier: NSEntityDescription] {
return self.entityNameMapping[NSStringFromClass(entityClass)]!
}
@nonobjc
internal func entityTypesMapping() -> [String: NSManagedObject.Type] {
var mapping = [String: NSManagedObject.Type]()
self.entityNameMapping.forEach { (className, entityName) in
if let mapping: NSDictionary = cs_getAssociatedObjectForKey(&PropertyKeys.objectClassNamesByEntityName, inObject: self) {
mapping[entityName] = (NSClassFromString(className)! as! NSManagedObject.Type)
return mapping as! [EntityIdentifier: NSEntityDescription]
}
var mapping: [EntityIdentifier: NSEntityDescription] = [:]
self.entities.forEach { (entityDescription) in
let entityIdentifier = EntityIdentifier(entityDescription)
mapping[entityIdentifier] = entityDescription
}
cs_setAssociatedCopiedObject(
mapping as NSDictionary,
forKey: &PropertyKeys.objectClassNamesByEntityName,
inObject: self
)
return mapping
}
// TODO: remove
// @nonobjc
// internal func entityNames(for type: NSManagedObject.Type) -> Set<EntityName> {
//
// let className = NSStringFromClass(type)
// return Set(
// self.objectClassNamesByEntityName
// .filter({ $0.value == className })
// .map({ $0.key })
// )
// }
//
// @nonobjc
// internal func entityTypesMapping() -> [EntityName: NSManagedObject.Type] {
//
// var mapping = [EntityName: NSManagedObject.Type]()
// self.objectClassNamesByEntityName.forEach { (entityName, className) in
//
// mapping[entityName] = (NSClassFromString(className)! as! NSManagedObject.Type)
// }
// return mapping
// }
@nonobjc
internal func mergedModels() -> [NSManagedObjectModel] {
@@ -256,39 +284,9 @@ internal extension NSManagedObjectModel {
}
}
@nonobjc
private var entityNameMapping: [String: String] {
get {
if let mapping: NSDictionary = cs_getAssociatedObjectForKey(&PropertyKeys.entityNameMapping, inObject: self) {
return mapping as! [String: String]
}
var mapping = [String: String]()
self.entities.forEach { // TODO: use AnyEntity as mapping key
guard let entityName = $0.name else {
return
}
let className = $0.managedObjectClassName
mapping[className!] = entityName
}
cs_setAssociatedCopiedObject(
mapping as NSDictionary,
forKey: &PropertyKeys.entityNameMapping,
inObject: self
)
return mapping
}
}
private struct PropertyKeys {
static var entityNameMapping: Void?
static var objectClassNamesByEntityName: Void?
static var modelVersionFileURL: Void?
static var modelVersions: Void?

View File

@@ -72,7 +72,7 @@ internal extension NSPersistentStoreCoordinator {
}
@nonobjc
internal func addPersistentStoreSynchronously(_ storeType: String, configuration: String?, URL storeURL: URL?, options: [NSObject : AnyObject]?) throws -> NSPersistentStore {
internal func addPersistentStoreSynchronously(_ storeType: String, configuration: ModelConfiguration, URL storeURL: URL?, options: [NSObject : AnyObject]?) throws -> NSPersistentStore {
var store: NSPersistentStore?
var storeError: NSError?

View File

@@ -44,21 +44,9 @@ public extension CSCoreStore {
Returns the entity name-to-class type mapping from the `defaultStack`'s model.
*/
@objc
public static var entityClassesByName: [String: NSManagedObject.Type] {
public static func entityTypesByNameForType(_ type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
return CoreStore.entityTypesByName
}
/**
Returns the entity class for the given entity name from the `defaultStack`'s model.
- parameter name: the entity name
- returns: the `NSManagedObject` class for the given entity name, or `nil` if not found
*/
@objc
public static func entityClassWithName(_ name: String) -> NSManagedObject.Type? {
return CoreStore.entityTypesByName[name]
return CoreStore.entityTypesByName(for: type)
}
/**
@@ -137,4 +125,30 @@ public extension CSCoreStore {
return self.defaultStack.addSQLiteStorageAndWait(storage, error: error)
}
// MARK: Deprecated
/**
Returns the entity name-to-class type mapping from the `defaultStack`'s model.
*/
@available(*, deprecated: 3.1, message: "Use the new +entityTypesByNameForType: method passing `[NSManagedObject class]` as argument.")
@objc
public static var entityClassesByName: [EntityName: NSManagedObject.Type] {
return CoreStore.entityTypesByName
}
/**
Returns the entity class for the given entity name from the `defaultStack`'s model.
- parameter name: the entity name
- returns: the `NSManagedObject` class for the given entity name, or `nil` if not found
*/
@available(*, deprecated: 3.1, message: "Use the new +entityTypesByNameForType: method passing `[NSManagedObject class]` as argument.")
@objc
public static func entityClassWithName(_ name: EntityName) -> NSManagedObject.Type? {
return CoreStore.entityTypesByName[name]
}
}

View File

@@ -54,7 +54,7 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
- parameter versionChain: the version strings that indicate 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.
*/
@objc
public convenience init(modelName: String?, bundle: Bundle?, versionChain: [String]?) {
public convenience init(modelName: XcdatamodelFilename?, bundle: Bundle?, versionChain: [String]?) {
self.init(
DataStack(
@@ -73,7 +73,7 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
- parameter versionTree: the version strings that indicate 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.
*/
@objc
public convenience init(modelName: String?, bundle: Bundle?, versionTree: [String: String]?) {
public convenience init(modelName: XcdatamodelFilename?, bundle: Bundle?, versionTree: [String: String]?) {
self.init(
DataStack(
@@ -128,23 +128,12 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
}
/**
Returns the entity name-to-class type mapping from the stack's model.
Returns the entity name-to-class type mapping from the `CSDataStack`'s model.
*/
@objc
public var entityClassesByName: [String: NSManagedObject.Type] {
public func entityTypesByNameForType(_ type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
return self.bridgeToSwift.entityTypesByName
}
/**
Returns the entity class for the given entity name from the stack's's model.
- parameter name: the entity name
- returns: the `NSManagedObject` class for the given entity name, or `nil` if not found
*/
@objc
public func entityClassWithName(_ name: String) -> NSManagedObject.Type? {
return self.bridgeToSwift.entityTypesByName[name]
return self.bridgeToSwift.entityTypesByName(for: type)
}
/**
@@ -268,6 +257,31 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
self.bridgeToSwift = swiftValue
super.init()
}
// MARK: Deprecated
/**
Returns the entity name-to-class type mapping from the stack's model.
*/
@available(*, deprecated: 3.1, message: "Use the new -entityTypesByNameForType: method passing `[NSManagedObject class]` as argument.")
@objc
public var entityClassesByName: [EntityName: NSManagedObject.Type] {
return self.bridgeToSwift.entityTypesByName
}
/**
Returns the entity class for the given entity name from the stack's's model.
- parameter name: the entity name
- returns: the `NSManagedObject` class for the given entity name, or `nil` if not found
*/
@available(*, deprecated: 3.1, message: "Use the new -entityTypesByNameForType: method passing `[NSManagedObject class]` as argument.")
@objc
public func entityClassWithName(_ name: EntityName) -> NSManagedObject.Type? {
return self.bridgeToSwift.entityTypesByName[name]
}
}

View File

@@ -35,7 +35,7 @@ import CoreData
- SeeAlso: `From`
*/
@objc
public final class CSFrom: NSObject, CoreStoreObjectiveCType {
public final class CSFrom: NSObject {
/**
The associated `NSManagedObject` entity class
@@ -71,7 +71,7 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
- parameter entityClass: the `NSManagedObject` class type to be created
*/
@objc
public convenience init(entityClass: AnyClass) {
public convenience init(entityClass: NSManagedObject.Type) {
self.init(From(entityClass))
}
@@ -85,7 +85,7 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
- parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `[NSNull null]` to use the default configuration.
*/
@objc
public convenience init(entityClass: AnyClass, configuration: Any) {
public convenience init(entityClass: NSManagedObject.Type, configuration: Any) {
switch configuration {
@@ -111,9 +111,9 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
- parameter configurations: an array of the `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `[NSNull null]` to use the default configuration.
*/
@objc
public convenience init(entityClass: AnyClass, configurations: [Any]) {
public convenience init(entityClass: NSManagedObject.Type, configurations: [Any]) {
var arguments = [String?]()
var arguments = [ModelConfiguration]()
for configuration in configurations {
switch configuration {
@@ -154,7 +154,7 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
// MARK: - From
extension From: CoreStoreSwiftType {
extension From where T: NSManagedObject {
// MARK: CoreStoreSwiftType
@@ -162,4 +162,16 @@ extension From: CoreStoreSwiftType {
return CSFrom(self)
}
// MARK: FilePrivate
fileprivate func downcast() -> From<NSManagedObject> {
return From<NSManagedObject>(
entityClass: self.entityClass,
configurations: self.configurations,
findPersistentStores: self.findPersistentStores
)
}
}

View File

@@ -43,7 +43,7 @@ public final class CSInMemoryStore: NSObject, CSStorageInterface, CoreStoreObjec
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration.
*/
@objc
public convenience init(configuration: String?) {
public convenience init(configuration: ModelConfiguration) {
self.init(InMemoryStore(configuration: configuration))
}
@@ -70,7 +70,7 @@ public final class CSInMemoryStore: NSObject, CSStorageInterface, CoreStoreObjec
The configuration name in the model file
*/
@objc
public var configuration: String? {
public var configuration: ModelConfiguration {
return self.bridgeToSwift.configuration
}

View File

@@ -35,13 +35,13 @@ import CoreData
- SeeAlso: `Into`
*/
@objc
public final class CSInto: NSObject, CoreStoreObjectiveCType {
public final class CSInto: NSObject {
/**
The associated `NSManagedObject` entity class
*/
@objc
public var entityClass: AnyClass {
public var entityClass: NSManagedObject.Type {
return self.bridgeToSwift.entityClass
}
@@ -51,7 +51,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
May contain a `String` to pertain to a named configuration, or `nil` to pertain to the default configuration
*/
@objc
public var configuration: String? {
public var configuration: ModelConfiguration {
return self.bridgeToSwift.configuration
}
@@ -65,7 +65,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
- parameter entityClass: the `NSManagedObject` class type to be created
*/
@objc
public convenience init(entityClass: AnyClass) {
public convenience init(entityClass: NSManagedObject.Type) {
self.init(Into(entityClass))
}
@@ -80,7 +80,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
@objc
public convenience init(entityClass: AnyClass, configuration: String?) {
public convenience init(entityClass: NSManagedObject.Type, configuration: ModelConfiguration) {
self.init(Into(entityClass, configuration))
}
@@ -122,7 +122,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
// MARK: - Into
extension Into: CoreStoreSwiftType {
extension Into where T: NSManagedObject {
// MARK: CoreStoreSwiftType
@@ -130,4 +130,16 @@ extension Into: CoreStoreSwiftType {
return CSInto(self)
}
// MARK: FilePrivate
fileprivate func downcast() -> Into<NSManagedObject> {
return Into<NSManagedObject>(
entityClass: self.entityClass,
configuration: self.configuration,
inferStoreIfPossible: self.inferStoreIfPossible
)
}
}

View File

@@ -46,7 +46,7 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
- parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `CSLocalStorageOptionsNone`.
*/
@objc
public convenience init(fileURL: URL, configuration: String?, mappingModelBundles: [Bundle]?, localStorageOptions: Int) {
public convenience init(fileURL: URL, configuration: ModelConfiguration, mappingModelBundles: [Bundle]?, localStorageOptions: Int) {
self.init(
SQLiteStore(
@@ -68,7 +68,7 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
- parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `[CSLocalStorageOptions none]`.
*/
@objc
public convenience init(fileName: String, configuration: String?, mappingModelBundles: [Bundle]?, localStorageOptions: Int) {
public convenience init(fileName: String, configuration: ModelConfiguration, mappingModelBundles: [Bundle]?, localStorageOptions: Int) {
self.init(
SQLiteStore(
@@ -133,7 +133,7 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
/**
The configuration name in the model file
*/
public var configuration: String? {
public var configuration: ModelConfiguration {
return self.bridgeToSwift.configuration
}

View File

@@ -47,7 +47,7 @@ public protocol CSStorageInterface {
The configuration name in the model file
*/
@objc
var configuration: String? { get }
var configuration: ModelConfiguration { get }
/**
The options dictionary for the `NSPersistentStore`

View File

@@ -88,7 +88,7 @@ fileprivate func createFRC(fromContext context: NSManagedObjectContext, from: CS
let controller = CoreStoreFetchedResultsController(
context: context,
fetchRequest: CoreStoreFetchRequest().dynamicCast(),
from: from?.bridgeToSwift.downcast(),
from: from?.bridgeToSwift,
sectionBy: sectionBy?.bridgeToSwift,
applyFetchClauses: { (fetchRequest) in

View File

@@ -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:)")

View File

@@ -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:)")

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

View 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"
}
}

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

View 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)
}
}

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

View File

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

View File

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

View File

@@ -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`.

View File

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

View File

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

View File

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

View File

@@ -83,12 +83,12 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
// MARK: BaseDataTransaction
/**
Creates a new `NSManagedObject` with the specified entity type.
Creates a new `NSManagedObject` or `ManagedObject` with the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` instance of the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` or `ManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` or `ManagedObject` instance of the specified entity type.
*/
public override func create<T: NSManagedObject>(_ into: Into<T>) -> T {
public override func create<T: ManagedObjectProtocol>(_ into: Into<T>) -> T {
CoreStore.assert(
!self.isCommitted,

View File

@@ -45,32 +45,36 @@ public /*abstract*/ class BaseDataTransaction {
}
/**
Creates a new `NSManagedObject` with the specified entity type.
Creates a new `NSManagedObject` or `ManagedObject` with the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` instance of the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` or `ManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` or `ManagedObject` instance of the specified entity type.
*/
public func create<T: NSManagedObject>(_ into: Into<T>) -> T {
public func create<T: ManagedObjectProtocol>(_ into: Into<T>) -> T {
let entityClass = (into.entityClass as! T.Type)
let entityClass = into.entityClass
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to create an entity of type \(cs_typeName(entityClass)) outside its designated queue."
)
let context = self.context
let dataStack = context.parentStack!
let entityIdentifier = EntityIdentifier(entityClass)
if into.inferStoreIfPossible {
switch context.parentStack!.persistentStoreForEntityClass(
entityClass,
switch dataStack.persistentStore(
for: entityIdentifier,
configuration: nil,
inferStoreIfPossible: true
) {
case (let persistentStore?, _):
let object = entityClass.createInContext(context)
context.assign(object, to: persistentStore)
return object
return entityClass.cs_forceCreate(
entityDescription: dataStack.entityDescription(for: entityIdentifier)!,
into: context,
assignTo: persistentStore
)
case (nil, true):
CoreStore.abort("Attempted to create an entity of type \(cs_typeName(entityClass)) with ambiguous destination persistent store, but the configuration name was not specified.")
@@ -81,17 +85,19 @@ public /*abstract*/ class BaseDataTransaction {
}
else {
switch context.parentStack!.persistentStoreForEntityClass(
entityClass,
switch dataStack.persistentStore(
for: entityIdentifier,
configuration: into.configuration
?? type(of: into).defaultConfigurationName,
?? DataStack.defaultConfigurationName,
inferStoreIfPossible: false
) {
case (let persistentStore?, _):
let object = entityClass.createInContext(context)
context.assign(object, to: persistentStore)
return object
return entityClass.cs_forceCreate(
entityDescription: dataStack.entityDescription(for: entityIdentifier)!,
into: context,
assignTo: persistentStore
)
case (nil, true):
CoreStore.abort("Attempted to create an entity of type \(cs_typeName(entityClass)) with ambiguous destination persistent store, but the configuration name was not specified.")
@@ -143,7 +149,7 @@ public /*abstract*/ class BaseDataTransaction {
)
CoreStore.assert(
into.inferStoreIfPossible
|| (into.configuration ?? Into.defaultConfigurationName) == objectID.persistentStore?.configurationName,
|| (into.configuration ?? DataStack.defaultConfigurationName) == objectID.persistentStore?.configurationName,
"Attempted to update an entity of type \(cs_typeName(into.entityClass)) but the specified persistent store do not match the `NSManagedObjectID`."
)
return self.fetchExisting(objectID) as? T
@@ -160,11 +166,10 @@ public /*abstract*/ class BaseDataTransaction {
self.isRunningInAllowedQueue(),
"Attempted to delete an entity outside its designated queue."
)
guard let object = object else {
return
}
self.context.fetchExisting(object)?.deleteFromContext()
let context = self.context
object
.flatMap(context.fetchExisting)
.flatMap(context.delete)
}
/**
@@ -192,7 +197,7 @@ public /*abstract*/ class BaseDataTransaction {
)
let context = self.context
objects.forEach { context.fetchExisting($0)?.deleteFromContext() }
objects.forEach { context.fetchExisting($0).flatMap(context.delete) }
}
/**

View File

@@ -39,18 +39,18 @@ import CoreData
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
```
*/
public struct Into<T: NSManagedObject>: Hashable {
public struct Into<T: ManagedObjectProtocol>: Hashable {
/**
The associated `NSManagedObject` entity class
The associated `NSManagedObject` or `ManagedObject` entity class
*/
public let entityClass: AnyClass
public let entityClass: T.Type
/**
The `NSPersistentStore` configuration name to associate objects from.
May contain a `String` to pertain to a named configuration, or `nil` to pertain to the default configuration
*/
public let configuration: String?
public let configuration: ModelConfiguration
/**
Initializes an `Into` clause.
@@ -58,7 +58,7 @@ public struct Into<T: NSManagedObject>: Hashable {
let person = transaction.create(Into<MyPersonEntity>())
```
*/
public init(){
public init() {
self.init(entityClass: T.self, configuration: nil, inferStoreIfPossible: true)
}
@@ -75,22 +75,6 @@ public struct Into<T: NSManagedObject>: Hashable {
self.init(entityClass: entity, configuration: nil, inferStoreIfPossible: true)
}
/**
Initializes an `Into` clause with the specified entity class.
```
let person = transaction.create(Into(MyPersonEntity.self))
```
- parameter entityClass: the `NSManagedObject` class type to be created
*/
public init(_ entityClass: AnyClass) {
CoreStore.assert(
entityClass is T.Type,
"Attempted to create generic type \(cs_typeName(Into<T>.self)) with entity class \(cs_typeName(entityClass))"
)
self.init(entityClass: entityClass, configuration: nil, inferStoreIfPossible: true)
}
/**
Initializes an `Into` clause with the specified configuration.
```
@@ -98,7 +82,7 @@ public struct Into<T: NSManagedObject>: Hashable {
```
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ configuration: String?) {
public init(_ configuration: ModelConfiguration) {
self.init(entityClass: T.self, configuration: configuration, inferStoreIfPossible: false)
}
@@ -111,32 +95,15 @@ public struct Into<T: NSManagedObject>: Hashable {
- parameter entity: the `NSManagedObject` type to be created
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entity: T.Type, _ configuration: String?) {
public init(_ entity: T.Type, _ configuration: ModelConfiguration) {
self.init(entityClass: entity, configuration: configuration, inferStoreIfPossible: false)
}
/**
Initializes an `Into` clause with the specified entity class and configuration.
```
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
```
- parameter entityClass: the `NSManagedObject` class type to be created
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entityClass: AnyClass, _ configuration: String?) {
CoreStore.assert(
entityClass is T.Type,
"Attempted to create generic type \(cs_typeName(Into<T>.self)) with entity class \(cs_typeName(entityClass))"
)
self.init(entityClass: entityClass, configuration: configuration, inferStoreIfPossible: false)
}
// MARK: Equatable
public static func == <T: NSManagedObject, U: NSManagedObject>(lhs: Into<T>, rhs: Into<U>) -> Bool {
public static func == <U: ManagedObjectProtocol, V: ManagedObjectProtocol>(lhs: Into<U>, rhs: Into<V>) -> Bool {
return lhs.entityClass == rhs.entityClass
&& lhs.configuration == rhs.configuration
@@ -156,26 +123,9 @@ public struct Into<T: NSManagedObject>: Hashable {
// MARK: Internal
internal static var defaultConfigurationName: String {
return "PF_DEFAULT_CONFIGURATION_NAME"
}
internal let inferStoreIfPossible: Bool
internal func downcast() -> Into<NSManagedObject> {
return Into<NSManagedObject>(
entityClass: self.entityClass,
configuration: self.configuration,
inferStoreIfPossible: self.inferStoreIfPossible
)
}
// MARK: Private
private init(entityClass: AnyClass, configuration: String?, inferStoreIfPossible: Bool) {
internal init(entityClass: T.Type, configuration: ModelConfiguration, inferStoreIfPossible: Bool) {
self.entityClass = entityClass
self.configuration = configuration

View File

@@ -43,22 +43,4 @@ public extension NSManagedObject {
return self.managedObjectContext?.parentTransaction as? UnsafeDataTransaction
}
// MARK: Internal
@nonobjc
internal class func createInContext(_ context: NSManagedObjectContext) -> Self {
return self.init(
entity: context.entityDescriptionForEntityType(self)!,
insertInto: context
)
}
@nonobjc
internal func deleteFromContext() {
self.managedObjectContext?.delete(self)
}
}

View File

@@ -50,12 +50,12 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
// MARK: BaseDataTransaction
/**
Creates a new `NSManagedObject` with the specified entity type.
Creates a new `NSManagedObject` or `ManagedObject` with the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` instance of the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` or `ManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` or `ManagedObject` instance of the specified entity type.
*/
public override func create<T: NSManagedObject>(_ into: Into<T>) -> T {
public override func create<T: ManagedObjectProtocol>(_ into: Into<T>) -> T {
CoreStore.assert(
!self.isCommitted,