diff --git a/HardcoreData/DataStack.swift b/HardcoreData/DataStack.swift index 7f8c73f..ba8f42b 100644 --- a/HardcoreData/DataStack.swift +++ b/HardcoreData/DataStack.swift @@ -38,14 +38,14 @@ private let applicationName = ((NSBundle.mainBundle().objectForInfoDictionaryKey /** The DataStack encapsulates the data model for the Core Data stack. Each DataStack can have multiple data stores, usually specified as a "Configuration" in the model editor. Behind the scenes, the DataStack manages its own NSPersistentStoreCoordinator, a root NSManagedObjectContext for disk saves, and a shared NSManagedObjectContext acting as a model interface for NSManagedObjects. */ -public class DataStack: NSObject { +public class DataStack { // MARK: Public /** Initializes a DataStack from merged model in the app bundle. */ - public convenience override init() { + public convenience init() { self.init(managedObjectModel: NSManagedObjectModel.mergedModelFromBundles(NSBundle.allBundles())!) } @@ -74,8 +74,18 @@ public class DataStack: NSObject { self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator) self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext) self.transactionQueue = .createSerial("com.hardcoredata.datastack.transactionqueue") + self.entityNameMapping = (managedObjectModel.entities as! [NSEntityDescription]).reduce([EntityClassNameType: EntityNameType]()) { (var mapping, entityDescription) in + + if let entityName = entityDescription.name { + + mapping[entityDescription.managedObjectClassName] = entityName + } + return mapping + } - super.init() + println(self.entityNameMapping) + + self.rootSavingContext.parentStack = self } /** @@ -133,10 +143,14 @@ public class DataStack: NSObject { public func addSQLiteStore(fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult { return self.addSQLiteStore( - fileURL: applicationSupportDirectory.URLByAppendingPathComponent(fileName, isDirectory: false), + fileURL: applicationSupportDirectory.URLByAppendingPathComponent( + fileName, + isDirectory: false + ), configuration: configuration, automigrating: automigrating, - resetStoreOnMigrationFailure: resetStoreOnMigrationFailure) + resetStoreOnMigrationFailure: resetStoreOnMigrationFailure + ) } /** @@ -257,9 +271,18 @@ public class DataStack: NSObject { internal let mainContext: NSManagedObjectContext internal let transactionQueue: GCDQueue; + internal func entityNameForEntityClass(entityClass: NSManagedObject.Type) -> String? { + + return self.entityNameMapping[NSStringFromClass(entityClass)] + } + // MARK: Private + private typealias EntityClassNameType = String + private typealias EntityNameType = String + private let coordinator: NSPersistentStoreCoordinator private let rootSavingContext: NSManagedObjectContext + private let entityNameMapping: [EntityClassNameType: EntityNameType] } diff --git a/HardcoreData/DataTransaction.swift b/HardcoreData/DataTransaction.swift index 9f3a1df..a7bf6ba 100644 --- a/HardcoreData/DataTransaction.swift +++ b/HardcoreData/DataTransaction.swift @@ -105,13 +105,14 @@ public final class DataTransaction { HardcoreData.assert(!self.isCommitted, "Attempted to commit a DataTransaction more than once.") self.isCommitted = true - let result = self.context.saveSynchronously() - self.result = result - - GCDQueue.Main.async { + let semaphore = GCDSemaphore(0) + self.context.saveAsynchronouslyWithCompletion { (result) -> Void in + self.result = result completion(result: result) + semaphore.signal() } + semaphore.wait() } /** diff --git a/HardcoreData/HardcoreData.swift b/HardcoreData/HardcoreData.swift index 6c5112b..0762f0e 100644 --- a/HardcoreData/HardcoreData.swift +++ b/HardcoreData/HardcoreData.swift @@ -30,15 +30,17 @@ import GCDKit /** Okay, okay. This one's shorter. */ -typealias HCD = HardcoreData +public typealias HCD = HardcoreData -// MARK: HardcoreData +// MARK: - HardcoreData /** -The HardcoreData struct is the main entry point for all other APIs. +HardcoreData is the main entry point for all other APIs. */ -public struct HardcoreData { +public enum HardcoreData { + + // MARK: Public /** The default DataStack instance to be used. If defaultStack is not set before the first time accessed, a default-configured DataStack will be created. @@ -110,6 +112,8 @@ public struct HardcoreData { } + // MARK: Private + private static let defaultStackBarrierQueue = GCDQueue.createConcurrent("com.hardcoreData.defaultStackBarrierQueue") private static var defaultStackInstance: DataStack? diff --git a/HardcoreData/NSManagedObject+HardcoreData.swift b/HardcoreData/NSManagedObject+HardcoreData.swift index 675b6de..9722dc5 100644 --- a/HardcoreData/NSManagedObject+HardcoreData.swift +++ b/HardcoreData/NSManagedObject+HardcoreData.swift @@ -31,21 +31,14 @@ import CoreData extension NSManagedObject { - // MARK: - Entity Utilities - - public class var entityName: String { - - // TODO: map from model file - return NSStringFromClass(self).componentsSeparatedByString(".").last! - } - - // MARK: - Internal internal class func createInContext(context: NSManagedObjectContext) -> Self { - return self(entity: NSEntityDescription.entityForName(self.entityName, inManagedObjectContext: context)!, - insertIntoManagedObjectContext: context) + return self( + entity: context.entityDescriptionForEntityClass(self)!, + insertIntoManagedObjectContext: context + ) } internal func inContext(context: NSManagedObjectContext) -> Self? { diff --git a/HardcoreData/NSManagedObjectContext+HardcoreData.swift b/HardcoreData/NSManagedObjectContext+HardcoreData.swift index 456d8ba..a2c11f1 100644 --- a/HardcoreData/NSManagedObjectContext+HardcoreData.swift +++ b/HardcoreData/NSManagedObjectContext+HardcoreData.swift @@ -28,11 +28,11 @@ import CoreData import GCDKit -// MARK: - NSManagedObjectContext+HardcoreData +// MARK: - NSManagedObjectContext public extension NSManagedObjectContext { - // MARK: - Public + // MARK: NSObject // MARK: Transactions @@ -40,10 +40,10 @@ public extension NSManagedObjectContext { let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) context.parentContext = self - context.setupForHardcoreDataWithContextName("com.hardcoredata.temporarycontext") - context.shouldCascadeSavesToParent = true context.parentStack = self.parentStack context.parentTransaction = self.parentTransaction + context.setupForHardcoreDataWithContextName("com.hardcoredata.temporarycontext") + context.shouldCascadeSavesToParent = true return context } @@ -55,10 +55,19 @@ public extension NSManagedObjectContext { get { + if let parentContext = self.parentContext { + + return parentContext.parentStack + } return self.getAssociatedObjectForKey(&PropertyKeys.parentStack) } set { + if let parentContext = self.parentContext { + + return + } + self.setAssociatedWeakObject( newValue, forKey: &PropertyKeys.parentStack) @@ -79,10 +88,23 @@ public extension NSManagedObjectContext { } } + internal func entityDescriptionForEntityClass(entity: NSManagedObject.Type) -> NSEntityDescription? { + + if let entityName = self.parentStack?.entityNameForEntityClass(entity) { + + return NSEntityDescription.entityForName( + entityName, + inManagedObjectContext: self + ) + } + return nil + } + internal func temporaryContextInTransaction(transaction: DataTransaction?) -> NSManagedObjectContext { let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) context.parentContext = self + context.parentStack = self.parentStack context.setupForHardcoreDataWithContextName("com.hardcoredata.temporarycontext") context.shouldCascadeSavesToParent = true @@ -160,7 +182,14 @@ public extension NSManagedObjectContext { if let parentContext = self.parentContext { - parentContext.saveAsynchronouslyWithCompletion(completion) + let result = parentContext.saveSynchronously() + if let completion = completion { + + GCDQueue.Main.async { + + completion(result: result) + } + } return } } diff --git a/HardcoreData/NSManagedObjectContext+Querying.swift b/HardcoreData/NSManagedObjectContext+Querying.swift index eb34cfa..01cfa3d 100644 --- a/HardcoreData/NSManagedObjectContext+Querying.swift +++ b/HardcoreData/NSManagedObjectContext+Querying.swift @@ -41,9 +41,7 @@ extension NSManagedObjectContext { public func fetchOne(entity: T.Type, _ queryClauses: [FetchClause]) -> T? { let fetchRequest = NSFetchRequest() - fetchRequest.entity = NSEntityDescription.entityForName( - entity.entityName, - inManagedObjectContext: self) + fetchRequest.entity = self.entityDescriptionForEntityClass(entity) fetchRequest.fetchLimit = 1 fetchRequest.resultType = .ManagedObjectResultType @@ -75,9 +73,7 @@ extension NSManagedObjectContext { public func fetchAll(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? { let fetchRequest = NSFetchRequest() - fetchRequest.entity = NSEntityDescription.entityForName( - entity.entityName, - inManagedObjectContext: self) + fetchRequest.entity = self.entityDescriptionForEntityClass(entity) fetchRequest.fetchLimit = 0 fetchRequest.resultType = .ManagedObjectResultType @@ -109,9 +105,7 @@ extension NSManagedObjectContext { public func queryCount(entity: T.Type, _ queryClauses: [FetchClause]) -> Int { let fetchRequest = NSFetchRequest() - fetchRequest.entity = NSEntityDescription.entityForName( - entity.entityName, - inManagedObjectContext: self) + fetchRequest.entity = self.entityDescriptionForEntityClass(entity) for clause in queryClauses { diff --git a/HardcoreDataTests/HardcoreDataTests.swift b/HardcoreDataTests/HardcoreDataTests.swift index 81513f0..5fe00c2 100644 --- a/HardcoreDataTests/HardcoreDataTests.swift +++ b/HardcoreDataTests/HardcoreDataTests.swift @@ -43,7 +43,7 @@ class HardcoreDataTests: XCTestCase { let stack = DataStack() HardcoreData.defaultStack = stack - XCTAssertEqual(HardcoreData.defaultStack, stack, "HardcoreData.defaultStack == stack") + XCTAssert(HardcoreData.defaultStack === stack, "HardcoreData.defaultStack === stack") switch stack.addSQLiteStore("Config1Store.sqlite", configuration: "Config1", resetStoreOnMigrationFailure: true){ @@ -99,7 +99,7 @@ class HardcoreDataTests: XCTestCase { } let queryExpectation = self.expectationWithDescription("Query creation") - HardcoreData.performTransaction{ (transaction) -> Void in + HardcoreData.performTransaction { (transaction) -> Void in let obj1 = transaction.fetchOne(TestEntity1) XCTAssertNotNil(obj1, "obj1 != nil") @@ -130,6 +130,6 @@ class HardcoreDataTests: XCTestCase { } } - self.waitForExpectationsWithTimeout(10, handler: nil) + self.waitForExpectationsWithTimeout(100, handler: nil) } } diff --git a/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents b/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents index d7f316b..cdfa223 100644 --- a/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -1,6 +1,6 @@ - + @@ -13,13 +13,13 @@ - + - + \ No newline at end of file