From 061a8632359dc1a161455f60cbf00b66855cd321 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sat, 21 Feb 2015 21:14:19 +0900 Subject: [PATCH] update --- HardcoreData.xcodeproj/project.pbxproj | 26 ++++-- .../WorkspaceSettings.xcsettings | 10 ++ HardcoreData/DataStack+Transaction.swift | 45 +++++++++ HardcoreData/DataStack.swift | 90 ++---------------- HardcoreData/DataTransaction+Querying.swift | 2 + HardcoreData/DataTransaction.swift | 24 +++-- HardcoreData/DefaultLogger.swift | 2 + HardcoreData/HardcoreData.swift | 64 ++----------- HardcoreData/HardcoreDataLogger.swift | 4 + .../NSManagedObject+HardcoreData.swift | 3 + .../NSManagedObjectContext+HardcoreData.swift | 47 +++++----- .../NSManagedObjectContext+Querying.swift | 2 + HardcoreData/NSObject+HardcoreData.swift | 37 +++++++- ...sistentStoreCoordinator+HardcoreData.swift | 2 +- HardcoreData/NotificationObserver.swift | 4 +- HardcoreData/PersistentStoreResult.swift | 10 +- HardcoreData/QueryClause.swift | 35 ------- HardcoreData/SaveResult.swift | 2 +- HardcoreData/SortedBy.swift | 4 + HardcoreDataTests/HardcoreDataTests.swift | 91 ++++++++++++------- .../Model.xcdatamodel/contents | 2 +- 21 files changed, 248 insertions(+), 258 deletions(-) create mode 100644 HardcoreData.xcodeproj/project.xcworkspace/xcuserdata/johnestropia.xcuserdatad/WorkspaceSettings.xcsettings create mode 100644 HardcoreData/DataStack+Transaction.swift delete mode 100644 HardcoreData/QueryClause.swift diff --git a/HardcoreData.xcodeproj/project.pbxproj b/HardcoreData.xcodeproj/project.pbxproj index d7e2c00..40b43e5 100644 --- a/HardcoreData.xcodeproj/project.pbxproj +++ b/HardcoreData.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ B5CFD36E1A0775F000B7885F /* SaveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFD36D1A0775F000B7885F /* SaveResult.swift */; }; B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */; }; B5CFF24019FD383100D6DFC4 /* DataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */; }; + B5D022661A90CD340070CA63 /* DataStack+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D022651A90CD340070CA63 /* DataStack+Transaction.swift */; }; B5D1E22A19FA9E63003B2874 /* PersistentStoreResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D1E22919FA9E63003B2874 /* PersistentStoreResult.swift */; }; B5D1E22C19FA9FBC003B2874 /* NSError+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D1E22B19FA9FBC003B2874 /* NSError+HardcoreData.swift */; }; B5D372841A39CD6900F583D9 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5D372821A39CD6900F583D9 /* Model.xcdatamodeld */; }; @@ -27,7 +28,6 @@ B5D8080E1A3471A500A44484 /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D808021A34715700A44484 /* GCDKit.framework */; }; B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D808151A34947300A44484 /* NotificationObserver.swift */; }; B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */; }; - B5D853791A8CF6930055EB32 /* QueryClause.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D853781A8CF6930055EB32 /* QueryClause.swift */; }; B5E126551A7DCE1400AD8B39 /* Where.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E126541A7DCE1400AD8B39 /* Where.swift */; }; B5E126571A7DCE5900AD8B39 /* SortedBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E126561A7DCE5900AD8B39 /* SortedBy.swift */; }; B5E209E01A0726460089C9D4 /* NSManagedObject+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */; }; @@ -68,13 +68,14 @@ 2F03A53519C5C6DA005002A5 /* HardcoreData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HardcoreData.h; sourceTree = ""; }; 2F03A53B19C5C6DA005002A5 /* HardcoreDataTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HardcoreDataTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 2F03A53E19C5C6DA005002A5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 2F03A53F19C5C6DA005002A5 /* HardcoreDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HardcoreDataTests.swift; sourceTree = ""; }; + 2F03A53F19C5C6DA005002A5 /* HardcoreDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = HardcoreDataTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 2F03A54C19C5C872005002A5 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; - 2F291E2619C6D3CF007AF63F /* HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HardcoreData.swift; sourceTree = ""; }; + 2F291E2619C6D3CF007AF63F /* HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = HardcoreData.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; B57078AF1A50392D007E33F2 /* FetchClause.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchClause.swift; sourceTree = ""; }; B5CFD36D1A0775F000B7885F /* SaveResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveResult.swift; sourceTree = ""; }; B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+HardcoreData.swift"; sourceTree = ""; }; B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataTransaction.swift; sourceTree = ""; }; + B5D022651A90CD340070CA63 /* DataStack+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Transaction.swift"; sourceTree = ""; }; B5D1E22919FA9E63003B2874 /* PersistentStoreResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PersistentStoreResult.swift; sourceTree = ""; }; B5D1E22B19FA9FBC003B2874 /* NSError+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSError+HardcoreData.swift"; sourceTree = ""; }; B5D372831A39CD6900F583D9 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; @@ -85,9 +86,8 @@ B5D39A0319FD00DE000E91BB /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; B5D5E0CE1A4D6AAB006468AF /* TestEntity2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestEntity2.swift; sourceTree = ""; }; B5D806C51A34715700A44484 /* GCDKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = GCDKit.xcodeproj; sourceTree = ""; }; - B5D808151A34947300A44484 /* NotificationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationObserver.swift; sourceTree = ""; }; + B5D808151A34947300A44484 /* NotificationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NotificationObserver.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+HardcoreData.swift"; sourceTree = ""; }; - B5D853781A8CF6930055EB32 /* QueryClause.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryClause.swift; sourceTree = ""; }; B5E126541A7DCE1400AD8B39 /* Where.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Where.swift; sourceTree = ""; }; B5E126561A7DCE5900AD8B39 /* SortedBy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedBy.swift; sourceTree = ""; }; B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+HardcoreData.swift"; sourceTree = ""; }; @@ -145,7 +145,6 @@ isa = PBXGroup; children = ( B5D399F019FC818E000E91BB /* DataStack.swift */, - B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */, 2F03A53519C5C6DA005002A5 /* HardcoreData.h */, 2F291E2619C6D3CF007AF63F /* HardcoreData.swift */, B5D1E22B19FA9FBC003B2874 /* NSError+HardcoreData.swift */, @@ -153,7 +152,7 @@ B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */, B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */, B5D1E22919FA9E63003B2874 /* PersistentStoreResult.swift */, - B5CFD36D1A0775F000B7885F /* SaveResult.swift */, + B5D022621A90BCC60070CA63 /* Saving and Processing */, B5F409E51A8B11B600A228EA /* Logging */, B5E126531A7DCCE400AD8B39 /* Fetching and Querying */, B5D808141A34945A00A44484 /* Internal */, @@ -201,6 +200,16 @@ name = Frameworks; sourceTree = ""; }; + B5D022621A90BCC60070CA63 /* Saving and Processing */ = { + isa = PBXGroup; + children = ( + B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */, + B5D022651A90CD340070CA63 /* DataStack+Transaction.swift */, + B5CFD36D1A0775F000B7885F /* SaveResult.swift */, + ); + name = "Saving and Processing"; + sourceTree = ""; + }; B5D806BB1A34715700A44484 /* Libraries */ = { isa = PBXGroup; children = ( @@ -239,7 +248,6 @@ isa = PBXGroup; children = ( B57078AF1A50392D007E33F2 /* FetchClause.swift */, - B5D853781A8CF6930055EB32 /* QueryClause.swift */, B5E126541A7DCE1400AD8B39 /* Where.swift */, B5E126561A7DCE5900AD8B39 /* SortedBy.swift */, B5F409F01A8B27A600A228EA /* CustomizeQuery.swift */, @@ -392,11 +400,11 @@ B5CFF24019FD383100D6DFC4 /* DataTransaction.swift in Sources */, B5D399F519FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift in Sources */, B5CFD36E1A0775F000B7885F /* SaveResult.swift in Sources */, + B5D022661A90CD340070CA63 /* DataStack+Transaction.swift in Sources */, B5D1E22C19FA9FBC003B2874 /* NSError+HardcoreData.swift in Sources */, B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */, B5E126571A7DCE5900AD8B39 /* SortedBy.swift in Sources */, B5F409EF1A8B243D00A228EA /* DataTransaction+Querying.swift in Sources */, - B5D853791A8CF6930055EB32 /* QueryClause.swift in Sources */, 2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */, B5F409E91A8B11CE00A228EA /* HardcoreDataLogger.swift in Sources */, B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */, diff --git a/HardcoreData.xcodeproj/project.xcworkspace/xcuserdata/johnestropia.xcuserdatad/WorkspaceSettings.xcsettings b/HardcoreData.xcodeproj/project.xcworkspace/xcuserdata/johnestropia.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..659c876 --- /dev/null +++ b/HardcoreData.xcodeproj/project.xcworkspace/xcuserdata/johnestropia.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,10 @@ + + + + + HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges + + SnapshotAutomaticallyBeforeSignificantChanges + + + diff --git a/HardcoreData/DataStack+Transaction.swift b/HardcoreData/DataStack+Transaction.swift new file mode 100644 index 0000000..9cd6958 --- /dev/null +++ b/HardcoreData/DataStack+Transaction.swift @@ -0,0 +1,45 @@ +// +// DataStack+Transaction.swift +// HardcoreData +// +// Created by John Rommel Estropia on 2015/02/15. +// Copyright (c) 2015 John Rommel Estropia. All rights reserved. +// + +import Foundation +import CoreData + + +// MARK: - DataStack+Transaction + +extension DataStack { + + // MARK: Public + + /** + Begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made. + + :param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext. + */ + public func performTransaction(closure: (transaction: DataTransaction) -> Void) { + + DataTransaction( + mainContext: self.mainContext, + queue: self.transactionQueue, + closure: closure).perform() + } + + /** + Begins a transaction synchronously where NSManagedObject creates, updates, and deletes can be made. + + :param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext. + :returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously + */ + public func performTransactionAndWait(closure: (transaction: DataTransaction) -> Void) -> SaveResult? { + + return DataTransaction( + mainContext: self.mainContext, + queue: self.transactionQueue, + closure: closure).performAndWait() + } +} \ No newline at end of file diff --git a/HardcoreData/DataStack.swift b/HardcoreData/DataStack.swift index aad6c12..0f5c872 100644 --- a/HardcoreData/DataStack.swift +++ b/HardcoreData/DataStack.swift @@ -33,14 +33,14 @@ private let applicationSupportDirectory = NSFileManager.defaultManager().URLsFor private let applicationName = ((NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData") -// MARK: DataStack +// MARK: - DataStack /** 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 { - // MARK: - Public + // MARK: Public /** Initializes a DataStack from merged model in the app bundle. @@ -251,93 +251,15 @@ public class DataStack: NSObject { return PersistentStoreResult(.UnknownError) } - /** - Begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made. - :param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext. - */ - public func performTransaction(closure: (transaction: DataTransaction) -> ()) { - - DataTransaction( - mainContext: self.mainContext, - queue: self.transactionQueue, - closure: closure).perform() - } + // MARK: Internal - /** - Begins a transaction synchronously where NSManagedObject creates, updates, and deletes can be made. + public let mainContext: NSManagedObjectContext + internal let transactionQueue: GCDQueue; - :param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext. - :returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously - */ - public func performTransactionAndWait(closure: (transaction: DataTransaction) -> ()) -> SaveResult? { - - return DataTransaction( - mainContext: self.mainContext, - queue: self.transactionQueue, - closure: closure).performAndWait() - } - // MARK: - Internal + // MARK: Private private let coordinator: NSPersistentStoreCoordinator private let rootSavingContext: NSManagedObjectContext - private let mainContext: NSManagedObjectContext - private let transactionQueue: GCDQueue; } - - -// MARK: - DataStack+DataContextProvider - -//extension DataStack: ObjectQueryable { -// -// public func firstObject(entity: T.Type) -> T? { -// -// return self.mainContext.firstObject(entity) -// } -// -// public func firstObject(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> T? { -// -// return self.mainContext.firstObject(entity, customizeFetch: customizeFetch) -// } -// -// public func firstObject(query: ObjectQuery) -> T? { -// -// return self.mainContext.firstObject(query) -// } -// -// public func firstObject(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> T? { -// -// return self.mainContext.firstObject(query, customizeFetch: customizeFetch) -// } -// -// public func allObjects(entity: T.Type) -> [T]? { -// -// return self.mainContext.allObjects(entity) -// } -// -// public func allObjects(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> [T]? { -// -// return self.mainContext.allObjects(entity, customizeFetch: customizeFetch) -// } -// -// public func allObjects(query: ObjectQuery) -> [T]? { -// -// return self.mainContext.allObjects(query) -// } -// -// public func allObjects(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> [T]? { -// -// return self.mainContext.allObjects(query, customizeFetch: customizeFetch) -// } -// -// public func countObjects(entity: T.Type) -> Int { -// -// return self.mainContext.countObjects(entity) -// } -// -// public func countObjects(query: ObjectQuery) -> Int { -// -// return self.mainContext.countObjects(query) -// } -//} diff --git a/HardcoreData/DataTransaction+Querying.swift b/HardcoreData/DataTransaction+Querying.swift index c7ea244..ee7bd0c 100644 --- a/HardcoreData/DataTransaction+Querying.swift +++ b/HardcoreData/DataTransaction+Querying.swift @@ -27,6 +27,8 @@ import Foundation import CoreData +// MARK: - DataTransaction+Querying + extension DataTransaction { public func fetchOne(entity: T.Type, _ queryClauses: FetchClause...) -> T? { diff --git a/HardcoreData/DataTransaction.swift b/HardcoreData/DataTransaction.swift index f1566a4..9f3a1df 100644 --- a/HardcoreData/DataTransaction.swift +++ b/HardcoreData/DataTransaction.swift @@ -99,15 +99,17 @@ public final class DataTransaction { :param: completion the block executed after the save completes. Success or failure is reported by the SaveResult argument of the block. */ - public func commit(completion: (result: SaveResult) -> ()) { + public func commit(completion: (result: SaveResult) -> Void) { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to commit a DataTransaction outside a transaction queue.") HardcoreData.assert(!self.isCommitted, "Attempted to commit a DataTransaction more than once.") self.isCommitted = true - self.context.saveAsynchronouslyWithCompletion { (result) -> () in + let result = self.context.saveSynchronously() + self.result = result + + GCDQueue.Main.async { - self.result = result completion(result: result) } } @@ -129,19 +131,23 @@ public final class DataTransaction { // MARK: - Internal - internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: DataTransaction) -> ()) { + internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: DataTransaction) -> Void) { self.mainContext = mainContext self.transactionQueue = queue - self.context = mainContext.temporaryContext() + + let context = mainContext.temporaryContext() + context.retainsRegisteredObjects = true + self.context = context + self.closure = closure - self.context.parentTransaction = self + context.parentTransaction = self } internal func perform() { - self.transactionQueue.barrierAsync { + self.transactionQueue.async { self.closure(transaction: self) } @@ -149,7 +155,7 @@ public final class DataTransaction { internal func performAndWait() -> SaveResult? { - self.transactionQueue.barrierSync { + self.transactionQueue.sync { self.closure(transaction: self) } @@ -163,5 +169,5 @@ public final class DataTransaction { private var result: SaveResult? private let mainContext: NSManagedObjectContext private let transactionQueue: GCDQueue - private let closure: (transaction: DataTransaction) -> () + private let closure: (transaction: DataTransaction) -> Void } diff --git a/HardcoreData/DefaultLogger.swift b/HardcoreData/DefaultLogger.swift index 0f84137..3af499d 100644 --- a/HardcoreData/DefaultLogger.swift +++ b/HardcoreData/DefaultLogger.swift @@ -26,6 +26,8 @@ import Foundation +// MARK: - DefaultLogger + public final class DefaultLogger: HardcoreDataLogger { public func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: UWord, functionName: StaticString) { diff --git a/HardcoreData/HardcoreData.swift b/HardcoreData/HardcoreData.swift index ff1a548..3288163 100644 --- a/HardcoreData/HardcoreData.swift +++ b/HardcoreData/HardcoreData.swift @@ -27,6 +27,9 @@ import CoreData import GCDKit +/** +Okay, okay. This one's shorter. +*/ typealias HCD = HardcoreData @@ -71,6 +74,8 @@ public struct HardcoreData { public static var logger: HardcoreDataLogger = DefaultLogger() + // MARK: Internal + internal static func log(level: LogLevel, message: String, fileName: StaticString = __FILE__, lineNumber: UWord = __LINE__, functionName: StaticString = __FUNCTION__) { self.logger.log( @@ -118,7 +123,7 @@ extension HardcoreData { :param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext. */ - public static func performTransaction(closure: (transaction: DataTransaction) -> ()) { + public static func performTransaction(closure: (transaction: DataTransaction) -> Void) { self.defaultStack.performTransaction(closure) } @@ -129,63 +134,8 @@ extension HardcoreData { :param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext. :returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously */ - public static func performTransactionAndWait(closure: (transaction: DataTransaction) -> ()) -> SaveResult? { + public static func performTransactionAndWait(closure: (transaction: DataTransaction) -> Void) -> SaveResult? { return self.defaultStack.performTransactionAndWait(closure) } } - - -//extension HardcoreData { -// -// public static func firstObject(entity: T.Type) -> T? { -// -// return self.defaultStack.firstObject(entity) -// } -// -// public static func firstObject(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> T? { -// -// return self.defaultStack.firstObject(entity, customizeFetch: customizeFetch) -// } -// -// public static func firstObject(query: ObjectQuery) -> T? { -// -// return self.defaultStack.firstObject(query) -// } -// -// public static func firstObject(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> T? { -// -// return self.defaultStack.firstObject(query, customizeFetch: customizeFetch) -// } -// -// public static func allObjects(entity: T.Type) -> [T]? { -// -// return self.defaultStack.allObjects(entity) -// } -// -// public static func allObjects(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> [T]? { -// -// return self.defaultStack.allObjects(entity, customizeFetch: customizeFetch) -// } -// -// public static func allObjects(query: ObjectQuery) -> [T]? { -// -// return self.defaultStack.allObjects(query) -// } -// -// public static func allObjects(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> [T]? { -// -// return self.defaultStack.allObjects(query, customizeFetch: customizeFetch) -// } -// -// public static func countObjects(entity: T.Type) -> Int { -// -// return self.defaultStack.countObjects(entity) -// } -// -// public static func countObjects(query: ObjectQuery) -> Int { -// -// return self.defaultStack.countObjects(query) -// } -//} - diff --git a/HardcoreData/HardcoreDataLogger.swift b/HardcoreData/HardcoreDataLogger.swift index e2bb84b..5478919 100644 --- a/HardcoreData/HardcoreDataLogger.swift +++ b/HardcoreData/HardcoreDataLogger.swift @@ -26,6 +26,8 @@ import Foundation +// MARK: - LogLevel + public enum LogLevel { case Trace @@ -35,6 +37,8 @@ public enum LogLevel { } +// MARK: - HardcoreDataLogger + public protocol HardcoreDataLogger { func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: UWord, functionName: StaticString) diff --git a/HardcoreData/NSManagedObject+HardcoreData.swift b/HardcoreData/NSManagedObject+HardcoreData.swift index fab0118..675b6de 100644 --- a/HardcoreData/NSManagedObject+HardcoreData.swift +++ b/HardcoreData/NSManagedObject+HardcoreData.swift @@ -58,6 +58,9 @@ extension NSManagedObject { self.managedObjectContext?.deleteObject(self) } + + // MARK: Private + private func typedObjectInContext(context: NSManagedObjectContext) -> T? { let objectID = self.objectID diff --git a/HardcoreData/NSManagedObjectContext+HardcoreData.swift b/HardcoreData/NSManagedObjectContext+HardcoreData.swift index ae7f76c..456d8ba 100644 --- a/HardcoreData/NSManagedObjectContext+HardcoreData.swift +++ b/HardcoreData/NSManagedObjectContext+HardcoreData.swift @@ -51,7 +51,7 @@ public extension NSManagedObjectContext { // MARK: - Internal - internal var parentStack: DataStack? { + internal weak var parentStack: DataStack? { get { @@ -59,13 +59,13 @@ public extension NSManagedObjectContext { } set { - self.setAssociatedAssignedObject( + self.setAssociatedWeakObject( newValue, forKey: &PropertyKeys.parentStack) } } - internal var parentTransaction: DataTransaction? { + internal weak var parentTransaction: DataTransaction? { get { @@ -73,7 +73,7 @@ public extension NSManagedObjectContext { } set { - self.setAssociatedAssignedObject( + self.setAssociatedWeakObject( newValue, forKey: &PropertyKeys.parentTransaction) } @@ -91,15 +91,15 @@ public extension NSManagedObjectContext { internal func saveSynchronously() -> SaveResult { - var result: SaveResult = SaveResult(hasChanges: false) - if !self.hasChanges { - - self.reset() - return result - } - + var result = SaveResult(hasChanges: false) self.performBlockAndWait { - [unowned self] () -> () in + [unowned self] () -> Void in + + if !self.hasChanges { + + self.reset() + return + } var saveError: NSError? if self.save(&saveError) { @@ -137,22 +137,21 @@ public extension NSManagedObjectContext { return result } - internal func saveAsynchronouslyWithCompletion(completion: ((result: SaveResult) -> ())?) { + internal func saveAsynchronouslyWithCompletion(completion: ((result: SaveResult) -> Void)?) { - if !self.hasChanges { + self.performBlock { () -> Void in - self.reset() - if let completion = completion { + if !self.hasChanges { - GCDQueue.Main.async { + if let completion = completion { - completion(result: SaveResult(hasChanges: false)) + GCDQueue.Main.async { + + completion(result: SaveResult(hasChanges: false)) + } } + return } - return - } - - self.performBlock { () -> () in var saveError: NSError? if self.save(&saveError) { @@ -216,7 +215,7 @@ public extension NSManagedObjectContext { context.observerForDidSaveNotification = NotificationObserver( notificationName: NSManagedObjectContextDidSaveNotification, object: rootContext, - closure: { [weak context] (note) -> () in + closure: { [weak context] (note) -> Void in context?.mergeChangesFromContextDidSaveNotification(note) return @@ -290,7 +289,7 @@ public extension NSManagedObjectContext { self.observerForWillSaveNotification = NotificationObserver( notificationName: NSManagedObjectContextWillSaveNotification, object: self, - closure: { (note) -> () in + closure: { (note) -> Void in let context = note.object as! NSManagedObjectContext let insertedObjects = context.insertedObjects diff --git a/HardcoreData/NSManagedObjectContext+Querying.swift b/HardcoreData/NSManagedObjectContext+Querying.swift index 1771706..43e4243 100644 --- a/HardcoreData/NSManagedObjectContext+Querying.swift +++ b/HardcoreData/NSManagedObjectContext+Querying.swift @@ -27,6 +27,8 @@ import Foundation import CoreData +// MARK: - NSManagedObjectContext+Querying + extension NSManagedObjectContext { public func fetchOne(entity: T.Type, _ queryClauses: FetchClause...) -> T? { diff --git a/HardcoreData/NSObject+HardcoreData.swift b/HardcoreData/NSObject+HardcoreData.swift index 16a6e19..2a5798d 100644 --- a/HardcoreData/NSObject+HardcoreData.swift +++ b/HardcoreData/NSObject+HardcoreData.swift @@ -26,13 +26,36 @@ import Foundation +private class WeakObject { + + private(set) weak var object: AnyObject? + + init(_ object: AnyObject) { + + self.object = object + } +} + + // MARK: - NSObject+HardcoreData internal extension NSObject { + // MARK: Internal + internal func getAssociatedObjectForKey(key: UnsafePointer) -> T? { - return objc_getAssociatedObject(self, key) as? T + switch objc_getAssociatedObject(self, key) { + + case let object as T: + return object + + case let object as WeakObject: + return object.object as? T + + default: + return nil + } } internal func setAssociatedRetainedObject(object: T?, forKey key: UnsafePointer) { @@ -49,4 +72,16 @@ internal extension NSObject { objc_setAssociatedObject(self, key, object, UInt(OBJC_ASSOCIATION_ASSIGN)) } + + internal func setAssociatedWeakObject(object: T?, forKey key: UnsafePointer) { + + if let object = object { + + objc_setAssociatedObject(self, key, WeakObject(object), UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) + } + else { + + objc_setAssociatedObject(self, key, nil, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) + } + } } \ No newline at end of file diff --git a/HardcoreData/NSPersistentStoreCoordinator+HardcoreData.swift b/HardcoreData/NSPersistentStoreCoordinator+HardcoreData.swift index 67f16b1..a67b9ab 100644 --- a/HardcoreData/NSPersistentStoreCoordinator+HardcoreData.swift +++ b/HardcoreData/NSPersistentStoreCoordinator+HardcoreData.swift @@ -31,7 +31,7 @@ import CoreData public extension NSPersistentStoreCoordinator { - public func performSynchronously(closure: () -> ()) { + public func performSynchronously(closure: () -> Void) { if self.respondsToSelector("performBlockAndWait:") { diff --git a/HardcoreData/NotificationObserver.swift b/HardcoreData/NotificationObserver.swift index f3db0e5..b74435e 100644 --- a/HardcoreData/NotificationObserver.swift +++ b/HardcoreData/NotificationObserver.swift @@ -30,11 +30,13 @@ import Foundation internal final class NotificationObserver { + // MARK: Public + let notificationName: String let object: AnyObject? let observer: NSObjectProtocol - init(notificationName: String, object: AnyObject?, closure: (note: NSNotification!) -> ()) { + init(notificationName: String, object: AnyObject?, closure: (note: NSNotification!) -> Void) { self.notificationName = notificationName self.object = object diff --git a/HardcoreData/PersistentStoreResult.swift b/HardcoreData/PersistentStoreResult.swift index c5ba0fb..c4ecfe2 100644 --- a/HardcoreData/PersistentStoreResult.swift +++ b/HardcoreData/PersistentStoreResult.swift @@ -31,9 +31,14 @@ import CoreData public enum PersistentStoreResult { + // MARK: Public + case Success(NSPersistentStore) case Failure(NSError) + + // MARK: Internal + internal init(_ store: NSPersistentStore) { self = .Success(store) @@ -58,13 +63,16 @@ public enum PersistentStoreResult { } -// MARK: - PersistentStoreResult+BooleanType +// MARK: - PersistentStoreResult: BooleanType extension PersistentStoreResult: BooleanType { + // MARK: Public + public var boolValue: Bool { switch self { + case .Success: return true case .Failure: return false } diff --git a/HardcoreData/QueryClause.swift b/HardcoreData/QueryClause.swift deleted file mode 100644 index 013f740..0000000 --- a/HardcoreData/QueryClause.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// FetchClause.swift -// HardcoreData -// -// Copyright (c) 2014 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 -import CoreData - - -// MARK: - QueryClause - -public protocol QueryClause { - - func applyToFetchRequest(fetchRequest: NSFetchRequest) -} diff --git a/HardcoreData/SaveResult.swift b/HardcoreData/SaveResult.swift index 4281e22..8b26d22 100644 --- a/HardcoreData/SaveResult.swift +++ b/HardcoreData/SaveResult.swift @@ -57,7 +57,7 @@ public enum SaveResult { } -// MARK: - SaveResult+BooleanType +// MARK: - SaveResult: BooleanType extension SaveResult: BooleanType { diff --git a/HardcoreData/SortedBy.swift b/HardcoreData/SortedBy.swift index 2e2cf53..07199f5 100644 --- a/HardcoreData/SortedBy.swift +++ b/HardcoreData/SortedBy.swift @@ -31,10 +31,14 @@ public func +(left: SortedBy, right: SortedBy) -> SortedBy { return SortedBy(left.sortDescriptors + right.sortDescriptors) } + +// MARK: - AttributeName public typealias AttributeName = Selector +// MARK: - SortOrder + public enum SortOrder { case Ascending(AttributeName) diff --git a/HardcoreDataTests/HardcoreDataTests.swift b/HardcoreDataTests/HardcoreDataTests.swift index c9fc4e9..0411e22 100644 --- a/HardcoreDataTests/HardcoreDataTests.swift +++ b/HardcoreDataTests/HardcoreDataTests.swift @@ -45,30 +45,26 @@ class HardcoreDataTests: XCTestCase { HardcoreData.defaultStack = stack XCTAssertEqual(HardcoreData.defaultStack, stack, "HardcoreData.defaultStack == stack") - switch stack.addSQLiteStore("Config1Store", configuration: "Config1", resetStoreOnMigrationFailure: true){ + switch stack.addSQLiteStore("Config1Store.sqlite", configuration: "Config1", resetStoreOnMigrationFailure: true){ case .Failure(let error): - NSException( - name: "CoreDataMigrationException", - reason: error.localizedDescription, - userInfo: error.userInfo).raise() - - default: - break - } - switch stack.addSQLiteStore("Config2Store", configuration: "Config2", resetStoreOnMigrationFailure: true){ - - case .Failure(let error): - NSException( - name: "CoreDataMigrationException", - reason: error.localizedDescription, - userInfo: error.userInfo).raise() + XCTFail(error.description) default: break } - HardcoreData.performTransactionAndWait({ (transaction) -> () in + switch stack.addSQLiteStore("Config2Store.sqlite", configuration: "Config2", resetStoreOnMigrationFailure: true){ + + case .Failure(let error): + XCTFail(error.description) + + default: + break + } + + let createExpectation = self.expectationWithDescription("Entity creation") + HardcoreData.performTransaction { (transaction) -> Void in let obj1 = transaction.create(TestEntity1) obj1.testEntityID = 1 @@ -79,34 +75,61 @@ class HardcoreDataTests: XCTestCase { let obj2 = transaction.create(TestEntity2) obj2.testEntityID = 2 obj2.testString = "hahaha" - obj2.testNumber = 7 + obj2.testNumber = 100 obj2.testDate = NSDate() - transaction.commitAndWait() - }) - HardcoreData.performTransactionAndWait({ (transaction) -> () in + let obj3 = transaction.create(TestEntity2) + obj3.testEntityID = 3 + obj3.testString = "hohoho" + obj3.testNumber = 90 + obj3.testDate = NSDate() - let obj1 = transaction.fetchOne( - TestEntity1.self, - Where("testEntityID", isEqualTo: 1), + transaction.commit { (result) -> Void in + + XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()") + switch result { + + case .Success(let hasChanges): + createExpectation.fulfill() + + case .Failure(let error): + XCTFail(error.description) + } + } + } + + let queryExpectation = self.expectationWithDescription("Query creation") + HardcoreData.performTransaction{ (transaction) -> Void in + + let obj1 = transaction.fetchOne(TestEntity1) + XCTAssertNotNil(obj1, "obj1 != nil") + + let objs2 = transaction.fetchAll( + TestEntity2.self, + Where("testNumber", isEqualTo: 100) || Where("testNumber", isEqualTo: 90), SortedBy(.Ascending("testEntityID"), .Descending("testString")), CustomizeQuery { (fetchRequest) -> Void in fetchRequest.includesPendingChanges = true } ) - NSLog(">>>>> %@", obj1 ?? "nil") + XCTAssertNotNil(objs2, "objs2 != nil") + XCTAssertTrue(objs2?.count == 2, "objs2?.count == 2") - let objs2 = transaction.fetchAll( - TestEntity2.self, - Where("testEntityID", isEqualTo: 2) && Where("testNumber", isEqualTo: 7), - SortedBy(.Ascending("testEntityID"), .Descending("testString")), - CustomizeQuery { (fetchRequest) -> () in + transaction.commit { (result) -> Void in + + XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()") + switch result { - fetchRequest.includesPendingChanges = true + case .Success(let hasChanges): + queryExpectation.fulfill() + + case .Failure(let error): + XCTFail(error.description) } - ) - NSLog(">>>>> %@", objs2 ?? "nil") - }) + } + } + + self.waitForExpectationsWithTimeout(10, handler: nil) } } diff --git a/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents b/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents index d7109a4..d7f316b 100644 --- a/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -1,5 +1,5 @@ - +