Added setup and transactions demo

This commit is contained in:
John Rommel Estropia
2015-05-26 01:44:49 +09:00
parent cb867c07ab
commit 1cc4f21336
32 changed files with 1184 additions and 334 deletions
@@ -34,6 +34,125 @@ A `Form` clause binds the `NSManagedObject` entity type to the generics type sys
*/
public struct From<T: NSManagedObject> {
public init(){ }
public init(_ entity: T.Type) { }
// MARK: Public
public init(){
self.findPersistentStores = { _ in nil }
}
public init(_ entity: T.Type) {
self.findPersistentStores = { _ in nil }
}
public init(_ configurations: String...) {
self.init(configurations: configurations)
}
public init(_ configurations: [String]) {
self.init(configurations: configurations)
}
public init(_ entity: T.Type, _ configurations: String...) {
self.init(configurations: configurations)
}
public init(_ entity: T.Type, _ configurations: [String]) {
self.init(configurations: configurations)
}
public init(_ storeURLs: NSURL...) {
self.init(storeURLs: storeURLs)
}
public init(_ storeURLs: [NSURL]) {
self.init(storeURLs: storeURLs)
}
public init(_ entity: T.Type, _ storeURLs: NSURL...) {
self.init(storeURLs: storeURLs)
}
public init(_ entity: T.Type, _ storeURLs: [NSURL]) {
self.init(storeURLs: storeURLs)
}
public init(_ persistentStores: NSPersistentStore...) {
self.init(persistentStores: persistentStores)
}
public init(_ persistentStores: [NSPersistentStore]) {
self.init(persistentStores: persistentStores)
}
public init(_ entity: T.Type, _ persistentStores: NSPersistentStore...) {
self.init(persistentStores: persistentStores)
}
public init(_ entity: T.Type, _ persistentStores: [NSPersistentStore]) {
self.init(persistentStores: persistentStores)
}
// MARK: Internal
internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext) {
fetchRequest.entity = context.entityDescriptionForEntityClass(T.self)
fetchRequest.affectedStores = self.findPersistentStores(context: context)
}
// MARK: Private
private let findPersistentStores: (context: NSManagedObjectContext) -> [NSPersistentStore]?
private init(configurations: [String]) {
let configurationsSet = Set(configurations)
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return configurationsSet.contains($0.configurationName)
}
}
}
private init(storeURLs: [NSURL]) {
let storeURLsSet = Set(storeURLs)
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return $0.URL != nil && storeURLsSet.contains($0.URL!)
}
}
}
private init(persistentStores: [NSPersistentStore]) {
let persistentStores = Set(persistentStores)
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return persistentStores.contains($0)
}
}
}
}
@@ -43,7 +43,7 @@ public extension DataStack {
*/
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchOne(from, fetchClauses)
}
@@ -57,7 +57,7 @@ public extension DataStack {
*/
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchOne(from, fetchClauses)
}
@@ -71,7 +71,7 @@ public extension DataStack {
*/
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchAll(from, fetchClauses)
}
@@ -85,7 +85,7 @@ public extension DataStack {
*/
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchAll(from, fetchClauses)
}
@@ -99,7 +99,7 @@ public extension DataStack {
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchCount(from, fetchClauses)
}
@@ -113,7 +113,7 @@ public extension DataStack {
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchCount(from, fetchClauses)
}
@@ -127,7 +127,7 @@ public extension DataStack {
*/
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchObjectID(from, fetchClauses)
}
@@ -141,7 +141,7 @@ public extension DataStack {
*/
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchObjectID(from, fetchClauses)
}
@@ -155,7 +155,7 @@ public extension DataStack {
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchObjectIDs(from, fetchClauses)
}
@@ -169,39 +169,11 @@ public extension DataStack {
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchObjectIDs(from, fetchClauses)
}
/**
Deletes all `NSManagedObject`'s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: deleteClauses a series of `DeleteClause` instances for the delete request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number of `NSManagedObject`'s deleted
*/
public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: DeleteClause...) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a \(typeName(self)) outside the main queue.")
return self.mainContext.deleteAll(from, deleteClauses)
}
/**
Deletes all `NSManagedObject`'s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: deleteClauses a series of `DeleteClause` instances for the delete request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number of `NSManagedObject`'s deleted
*/
public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a \(typeName(self)) outside the main queue.")
return self.mainContext.deleteAll(from, deleteClauses)
}
/**
Queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
@@ -214,7 +186,7 @@ public extension DataStack {
*/
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
return self.mainContext.queryValue(from, selectClause, queryClauses)
}
@@ -231,7 +203,7 @@ public extension DataStack {
*/
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
return self.mainContext.queryValue(from, selectClause, queryClauses)
}
@@ -248,7 +220,7 @@ public extension DataStack {
*/
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
return self.mainContext.queryAttributes(from, selectClause, queryClauses)
}
@@ -265,7 +237,7 @@ public extension DataStack {
*/
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
return self.mainContext.queryAttributes(from, selectClause, queryClauses)
}
@@ -151,30 +151,6 @@ public extension HardcoreData {
return self.defaultStack.fetchObjectIDs(from, fetchClauses)
}
/**
Using the `defaultStack`, deletes all `NSManagedObject`'s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: deleteClauses a series of `DeleteClause` instances for the delete request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number of `NSManagedObject`'s deleted
*/
public static func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: DeleteClause...) -> Int? {
return self.defaultStack.deleteAll(from, deleteClauses)
}
/**
Deletes all `NSManagedObject`'s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: deleteClauses a series of `DeleteClause` instances for the delete request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number of `NSManagedObject`'s deleted
*/
public static func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
return self.defaultStack.deleteAll(from, deleteClauses)
}
/**
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
@@ -41,7 +41,8 @@ internal extension NSManagedObjectContext {
internal func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectResultType
@@ -75,7 +76,8 @@ internal extension NSManagedObjectContext {
internal func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType
@@ -109,7 +111,7 @@ internal extension NSManagedObjectContext {
internal func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
for clause in fetchClauses {
@@ -141,7 +143,8 @@ internal extension NSManagedObjectContext {
internal func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectIDResultType
@@ -175,7 +178,8 @@ internal extension NSManagedObjectContext {
internal func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectIDResultType
@@ -209,7 +213,8 @@ internal extension NSManagedObjectContext {
internal func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType
fetchRequest.returnsObjectsAsFaults = true
@@ -254,7 +259,8 @@ internal extension NSManagedObjectContext {
internal func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
selectClause.applyToFetchRequest(fetchRequest)
@@ -294,7 +300,8 @@ internal extension NSManagedObjectContext {
internal func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
selectClause.applyToFetchRequest(fetchRequest)
@@ -42,7 +42,7 @@ public extension DataStack {
*/
public func observeObject<T: NSManagedObject>(object: T) -> ManagedObjectController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectController(
dataStack: self,
@@ -71,11 +71,11 @@ public extension DataStack {
*/
public func observeObjectList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ManagedObjectListController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectListController(
dataStack: self,
entity: T.self,
from: from,
sectionedBy: nil,
fetchClauses: fetchClauses
)
@@ -104,11 +104,11 @@ public extension DataStack {
*/
public func observeSectionedList<T: NSManagedObject>(from: From<T>, _ sectionedBy: SectionedBy, _ fetchClauses: [FetchClause]) -> ManagedObjectListController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectListController(
dataStack: self,
entity: T.self,
from: from,
sectionedBy: sectionedBy,
fetchClauses: fetchClauses
)
@@ -87,7 +87,7 @@ public final class ManagedObjectController<T: NSManagedObject> {
*/
public func addObserver<U: ManagedObjectObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
self.removeObserver(observer)
@@ -155,7 +155,7 @@ public final class ManagedObjectController<T: NSManagedObject> {
*/
public func removeObserver<U: ManagedObjectObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to remove a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to remove an observer of type \(typeName(observer)) outside the main thread.")
let nilValue: AnyObject? = nil
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.willChangeObject, inObject: observer)
@@ -188,7 +188,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
*/
public func addObserver<U: ManagedObjectListChangeObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
self.removeObserver(observer)
@@ -231,7 +231,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
*/
public func addObserver<U: ManagedObjectListObjectObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
self.removeObserver(observer)
@@ -340,7 +340,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
*/
public func addObserver<U: ManagedObjectListSectionObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
self.removeObserver(observer)
@@ -478,7 +478,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
*/
public func removeObserver<U: ManagedObjectListChangeObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to remove a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to remove an observer of type \(typeName(observer)) outside the main thread.")
let nilValue: AnyObject? = nil
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.willChangeList, inObject: observer)
@@ -496,12 +496,13 @@ public final class ManagedObjectListController<T: NSManagedObject> {
// MARK: Internal
internal init(dataStack: DataStack, entity: T.Type, sectionedBy: SectionedBy?, fetchClauses: [FetchClause]) {
internal init(dataStack: DataStack, from: From<T>, sectionedBy: SectionedBy?, fetchClauses: [FetchClause]) {
let context = dataStack.mainContext
let fetchRequest = NSFetchRequest()
fetchRequest.entity = context.entityDescriptionForEntityClass(entity)
from.applyToFetchRequest(fetchRequest, context: context)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType
@@ -91,16 +91,16 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
// MARK: BaseDataTransaction
/**
Creates a new `NSManagedObject` with the specified entity type. This method should not be used after the `commit()` method was already called once.
Creates a new `NSManagedObject` with the specified entity type.
:param: entity the `NSManagedObject` type to be created
:param: into the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
:returns: a new `NSManagedObject` instance of the specified entity type.
*/
public override func create<T: NSManagedObject>(entity: T.Type) -> T {
public override func create<T: NSManagedObject>(into: Into<T>) -> T {
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(entity)> from an already committed \(typeName(self)).")
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(T.self)> from an already committed \(typeName(self)).")
return super.create(entity)
return super.create(into)
}
/**
@@ -28,6 +28,86 @@ import CoreData
import GCDKit
// MARK: - Into
/**
A `Into` clause contains the destination entity and destination persistent store for a `create(...)` method. A common usage is to just indicate the entity:
let person = transaction.create(Into(MyPersonEntity))
For cases where multiple `NSPersistentStore`'s contain the same entity, the destination configuration's name needs to be specified as well:
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
This helps the `NSManagedObjectContext` to determine which
*/
public struct Into<T: NSManagedObject> {
// MARK: Public
/**
Initializes an `Into` clause.
Sample Usage:
let person = transaction.create(Into<MyPersonEntity>())
*/
public init(){
self.configuration = nil
self.inferStoreIfPossible = true
}
/**
Initializes an `Into` clause with the specified entity type.
Sample Usage:
let person = transaction.create(Into(MyPersonEntity))
:param: entity the `NSManagedObject` type to be created
*/
public init(_ entity: T.Type) {
self.configuration = nil
self.inferStoreIfPossible = true
}
/**
Initializes an `Into` clause with the specified configuration.
Sample Usage:
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
:param: 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?) {
self.configuration = configuration
self.inferStoreIfPossible = false
}
/**
Initializes an `Into` clause with the specified entity type and configuration.
Sample Usage:
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
:param: entity the `NSManagedObject` type to be created
:param: 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?) {
self.configuration = configuration
self.inferStoreIfPossible = false
}
// MARK: Internal
internal let configuration: String?
internal let inferStoreIfPossible: Bool
}
// MARK: - BaseDataTransaction
/**
@@ -48,14 +128,50 @@ public /*abstract*/ class BaseDataTransaction {
/**
Creates a new `NSManagedObject` with the specified entity type.
:param: entity the `NSManagedObject` type to be created
:param: into the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
:returns: a new `NSManagedObject` instance of the specified entity type.
*/
public func create<T: NSManagedObject>(entity: T.Type) -> T {
public func create<T: NSManagedObject>(into: Into<T>) -> T {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(entity)> outside its designated queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(T.self)> outside its designated queue.")
return T.createInContext(self.context)
let context = self.context
let object = T.createInContext(context)
if into.inferStoreIfPossible {
switch context.parentStack!.persistentStoreForEntityClass(T.self, configuration: nil, inferStoreIfPossible: true) {
case (.Some(let persistentStore), _):
context.assignObject(object, toPersistentStore: persistentStore)
case (.None, true):
HardcoreData.assert(false, "Attempted to create an entity of type \(typeName(object)) with ambiguous destination persistent store, but the configuration name was not specified.")
default:
HardcoreData.assert(false, "Attempted to create an entity of type \(typeName(object)), but a destination persistent store containing the entity type could not be found.")
}
}
else {
switch context.parentStack!.persistentStoreForEntityClass(T.self, configuration: into.configuration, inferStoreIfPossible: false) {
case (.Some(let persistentStore), _):
context.assignObject(object, toPersistentStore: persistentStore)
default:
if let configuration = into.configuration {
HardcoreData.assert(false, "Attempted to create an entity of type \(typeName(object)) into the configuration \"\(configuration)\", which it doesn't belong to.")
}
else {
HardcoreData.assert(false, "Attempted to create an entity of type \(typeName(object)) into the default configuration, which it doesn't belong to.")
}
}
}
return object
}
/**
@@ -41,7 +41,7 @@ public extension DataStack {
*/
public func beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
AsynchronousDataTransaction(
mainContext: self.rootSavingContext,
@@ -57,7 +57,7 @@ public extension DataStack {
*/
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
return SynchronousDataTransaction(
mainContext: self.rootSavingContext,
@@ -72,7 +72,7 @@ public extension DataStack {
*/
public func beginDetached() -> DetachedDataTransaction {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
return DetachedDataTransaction(
mainContext: self.rootSavingContext,
@@ -70,16 +70,16 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
// MARK: BaseDataTransaction
/**
Creates a new `NSManagedObject` with the specified entity type. This method should not be used after the `commit()` method was already called once.
Creates a new `NSManagedObject` with the specified entity type.
:param: entity the `NSManagedObject` type to be created
:param: into the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
:returns: a new `NSManagedObject` instance of the specified entity type.
*/
public override func create<T: NSManagedObject>(entity: T.Type) -> T {
public override func create<T: NSManagedObject>(into: Into<T>) -> T {
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(entity)> from an already committed \(typeName(self)).")
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(T.self)> from an already committed \(typeName(self)).")
return super.create(entity)
return super.create(into)
}
/**
+82 -6
View File
@@ -28,6 +28,8 @@ import CoreData
import GCDKit
private let defaultConfigurationName = "PF_DEFAULT_CONFIGURATION_NAME"
private let applicationSupportDirectory = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first as! NSURL
private let applicationName = ((NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData")
@@ -81,14 +83,20 @@ public final class DataStack {
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext)
self.entityNameMapping = (managedObjectModel.entities as! [NSEntityDescription]).reduce([EntityClassNameType: EntityNameType]()) { (var mapping, entityDescription) in
var entityNameMapping = [EntityClassNameType: EntityNameType]()
var entityConfigurationsMapping = [EntityClassNameType: Set<String>]()
for entityDescription in managedObjectModel.entities as! [NSEntityDescription] {
let managedObjectClassName = entityDescription.managedObjectClassName
entityConfigurationsMapping[managedObjectClassName] = []
if let entityName = entityDescription.name {
mapping[entityDescription.managedObjectClassName] = entityName
entityNameMapping[managedObjectClassName] = entityName
}
return mapping
}
self.entityNameMapping = entityNameMapping
self.entityConfigurationsMapping = entityConfigurationsMapping
self.rootSavingContext.parentStack = self
}
@@ -117,6 +125,7 @@ public final class DataStack {
if let store = store {
self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store)
}
@@ -140,7 +149,7 @@ public final class DataStack {
Adds to the stack an SQLite store from the given SQLite file name.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to nil. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
:param: automigrating Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true.
:param: resetStoreOnMigrationFailure Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false
:returns: a `PersistentStoreResult` indicating success or failure.
@@ -162,7 +171,7 @@ public final class DataStack {
Adds to the stack an SQLite store from the given SQLite file URL.
:param: fileURL the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to nil. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
:param: automigrating Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true.
:param: resetStoreOnMigrationFailure Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
:returns: a `PersistentStoreResult` indicating success or failure.
@@ -176,7 +185,7 @@ public final class DataStack {
if store.type == NSSQLiteStoreType
&& isExistingStoreAutomigrating == automigrating
&& store.configurationName == (configuration ?? "PF_DEFAULT_CONFIGURATION_NAME") {
&& store.configurationName == (configuration ?? defaultConfigurationName) {
return PersistentStoreResult(store)
}
@@ -218,6 +227,7 @@ public final class DataStack {
if let store = store {
self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store)
}
@@ -253,6 +263,7 @@ public final class DataStack {
if let store = store {
self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store)
}
}
@@ -276,12 +287,77 @@ public final class DataStack {
return self.entityNameMapping[NSStringFromClass(entityClass)]
}
internal func persistentStoresForEntityClass(entityClass: NSManagedObject.Type) -> [NSPersistentStore]? {
var returnValue: [NSPersistentStore]? = nil
self.storeMetadataUpdateQueue.barrierSync {
let configurationsForEntity = self.entityConfigurationsMapping[NSStringFromClass(entityClass)] ?? []
returnValue = map(configurationsForEntity) {
return self.configurationStoreMapping[$0]!
}
}
return returnValue
}
internal func persistentStoreForEntityClass(entityClass: NSManagedObject.Type, configuration: String?, inferStoreIfPossible: Bool) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
var returnValue: (store: NSPersistentStore?, isAmbiguous: Bool) = (store: nil, isAmbiguous: false)
self.storeMetadataUpdateQueue.barrierSync {
let configurationsForEntity = self.entityConfigurationsMapping[NSStringFromClass(entityClass)] ?? []
if let configuration = configuration {
if configurationsForEntity.contains(configuration) {
returnValue = (store: self.configurationStoreMapping[configuration], isAmbiguous: false)
return
}
else if !inferStoreIfPossible {
return
}
}
switch configurationsForEntity.count {
case 0:
return
case 1 where inferStoreIfPossible:
returnValue = (store: self.configurationStoreMapping[configurationsForEntity.first!], isAmbiguous: false)
default:
returnValue = (store: nil, isAmbiguous: true)
}
}
return returnValue
}
// MARK: Private
private typealias EntityClassNameType = String
private typealias EntityNameType = String
private typealias ConfigurationNameType = String
private let coordinator: NSPersistentStoreCoordinator
private let entityNameMapping: [EntityClassNameType: EntityNameType]
private let storeMetadataUpdateQueue = GCDQueue.createConcurrent("com.hardcoreData.persistentStoreBarrierQueue")
private var configurationStoreMapping = [ConfigurationNameType: NSPersistentStore]()
private var entityConfigurationsMapping = [EntityClassNameType: Set<String>]()
private func updateMetadataForPersistentStore(persistentStore: NSPersistentStore) {
self.storeMetadataUpdateQueue.barrierAsync {
let configurationName = persistentStore.configurationName
self.configurationStoreMapping[configurationName] = persistentStore
for entityDescription in (self.coordinator.managedObjectModel.entitiesForConfiguration(configurationName) as? [NSEntityDescription] ?? []) {
self.entityConfigurationsMapping[entityDescription.managedObjectClassName]?.insert(configurationName)
}
}
}
}