From 8374c552f01158a3a316b75d36cf2022e4075bc7 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 22 May 2015 02:26:28 +0900 Subject: [PATCH] full inline sourcecode documentation --- HardcoreData.xcodeproj/project.pbxproj | 26 +- .../BaseDataTransaction+Querying.swift | 124 ++++++ .../Concrete Clauses/From.swift | 3 + .../Concrete Clauses/GroupBy.swift | 21 +- .../{SortedBy.swift => OrderBy.swift} | 61 ++- .../Concrete Clauses/Select.swift | 210 ++++++++++- .../{CustomizeFetch.swift => Tweak.swift} | 24 +- .../Concrete Clauses/Where.swift | 34 ++ .../DataStack+Querying.swift | 124 ++++++ .../HardcoreData+Querying.swift | 124 ++++++ HardcoreData/HardcoreData.swift | 6 +- HardcoreData/Logging/DefaultLogger.swift | 7 + .../Logging/HardcoreData+Logging.swift | 2 +- HardcoreData/Logging/HardcoreDataLogger.swift | 33 ++ HardcoreData/NSError+HardcoreData.swift | 8 +- .../Observing/DataStack+Observing.swift | 36 ++ .../Observing/HardcoreData+Observing.swift | 36 ++ .../Observing/ManagedObjectController.swift | 111 ++++-- .../ManagedObjectListController.swift | 352 +++++++++++++----- .../Observing/ManagedObjectListObserver.swift | 93 ++++- .../Observing/ManagedObjectObserver.swift | 28 ++ .../AsynchronousDataTransaction.swift | 34 +- .../BaseDataTransaction.swift | 25 +- .../DataStack+Transaction.swift | 14 +- .../DetachedDataTransaction.swift | 6 +- .../HardcoreData+Transaction.swift | 14 +- .../Saving and Processing/SaveResult.swift | 41 ++ .../SynchronousDataTransaction.swift | 30 +- HardcoreData/Setting Up/DataStack.swift | 18 +- .../Setting Up/HardcoreData+Setup.swift | 84 +++++ .../Setting Up/PersistentStoreResult.swift | 30 ++ .../xcshareddata/HardcoreDataDemo.xccheckout | 4 +- .../HardcoreDataDemo/AppDelegate.swift | 2 +- ...ObjectListObserverDemoViewController.swift | 2 +- .../ObjectObserverDemoViewController.swift | 4 +- HardcoreDataTests/HardcoreDataTests.swift | 10 +- README.md | 20 +- 37 files changed, 1514 insertions(+), 287 deletions(-) rename HardcoreData/Fetching and Querying/Concrete Clauses/{SortedBy.swift => OrderBy.swift} (62%) rename HardcoreData/Fetching and Querying/Concrete Clauses/{CustomizeFetch.swift => Tweak.swift} (72%) create mode 100644 HardcoreData/Setting Up/HardcoreData+Setup.swift diff --git a/HardcoreData.xcodeproj/project.pbxproj b/HardcoreData.xcodeproj/project.pbxproj index 867a983..15d8d4c 100644 --- a/HardcoreData.xcodeproj/project.pbxproj +++ b/HardcoreData.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 2F03A54019C5C6DA005002A5 /* HardcoreDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F03A53F19C5C6DA005002A5 /* HardcoreDataTests.swift */; }; 2F03A54D19C5C872005002A5 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F03A54C19C5C872005002A5 /* CoreData.framework */; }; 2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F291E2619C6D3CF007AF63F /* HardcoreData.swift */; }; + B504D0D61B02362500B2BBB1 /* HardcoreData+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B504D0D51B02362500B2BBB1 /* HardcoreData+Setup.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 */; }; B5D372861A39CDDB00F583D9 /* TestEntity1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D372851A39CDDB00F583D9 /* TestEntity1.swift */; }; @@ -31,11 +32,11 @@ B5E84EFB1AFF846E0064E85B /* SaveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EF21AFF846E0064E85B /* SaveResult.swift */; }; B5E84EFC1AFF846E0064E85B /* SynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EF31AFF846E0064E85B /* SynchronousDataTransaction.swift */; }; B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EFE1AFF847B0064E85B /* BaseDataTransaction+Querying.swift */; }; - B5E84F0E1AFF847B0064E85B /* CustomizeFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F001AFF847B0064E85B /* CustomizeFetch.swift */; }; + B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F001AFF847B0064E85B /* Tweak.swift */; }; B5E84F0F1AFF847B0064E85B /* From.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F011AFF847B0064E85B /* From.swift */; }; B5E84F101AFF847B0064E85B /* GroupBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F021AFF847B0064E85B /* GroupBy.swift */; }; B5E84F111AFF847B0064E85B /* Select.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F031AFF847B0064E85B /* Select.swift */; }; - B5E84F121AFF847B0064E85B /* SortedBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F041AFF847B0064E85B /* SortedBy.swift */; }; + B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F041AFF847B0064E85B /* OrderBy.swift */; }; B5E84F131AFF847B0064E85B /* Where.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F051AFF847B0064E85B /* Where.swift */; }; B5E84F141AFF847B0064E85B /* DataStack+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F061AFF847B0064E85B /* DataStack+Querying.swift */; }; B5E84F151AFF847B0064E85B /* HardcoreData+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F071AFF847B0064E85B /* HardcoreData+Querying.swift */; }; @@ -90,6 +91,7 @@ 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; lineEnding = 0; path = HardcoreData.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + B504D0D51B02362500B2BBB1 /* HardcoreData+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HardcoreData+Setup.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 = ""; }; B5D372851A39CDDB00F583D9 /* TestEntity1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestEntity1.swift; sourceTree = ""; }; @@ -112,11 +114,11 @@ B5E84EF21AFF846E0064E85B /* SaveResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveResult.swift; sourceTree = ""; }; B5E84EF31AFF846E0064E85B /* SynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronousDataTransaction.swift; sourceTree = ""; }; B5E84EFE1AFF847B0064E85B /* BaseDataTransaction+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BaseDataTransaction+Querying.swift"; sourceTree = ""; }; - B5E84F001AFF847B0064E85B /* CustomizeFetch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeFetch.swift; sourceTree = ""; }; + B5E84F001AFF847B0064E85B /* Tweak.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tweak.swift; sourceTree = ""; }; B5E84F011AFF847B0064E85B /* From.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = From.swift; sourceTree = ""; }; B5E84F021AFF847B0064E85B /* GroupBy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupBy.swift; sourceTree = ""; }; B5E84F031AFF847B0064E85B /* Select.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Select.swift; sourceTree = ""; }; - B5E84F041AFF847B0064E85B /* SortedBy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedBy.swift; sourceTree = ""; }; + B5E84F041AFF847B0064E85B /* OrderBy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderBy.swift; sourceTree = ""; }; B5E84F051AFF847B0064E85B /* Where.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Where.swift; sourceTree = ""; }; B5E84F061AFF847B0064E85B /* DataStack+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Querying.swift"; sourceTree = ""; }; B5E84F071AFF847B0064E85B /* HardcoreData+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HardcoreData+Querying.swift"; sourceTree = ""; }; @@ -268,6 +270,7 @@ children = ( B5E84EDB1AFF84500064E85B /* DataStack.swift */, B5E84EDE1AFF84500064E85B /* PersistentStoreResult.swift */, + B504D0D51B02362500B2BBB1 /* HardcoreData+Setup.swift */, ); path = "Setting Up"; sourceTree = ""; @@ -285,8 +288,8 @@ B5E84EE91AFF846E0064E85B /* Saving and Processing */ = { isa = PBXGroup; children = ( - B5E84EEA1AFF846E0064E85B /* AsynchronousDataTransaction.swift */, B5E84EEB1AFF846E0064E85B /* BaseDataTransaction.swift */, + B5E84EEA1AFF846E0064E85B /* AsynchronousDataTransaction.swift */, B5E84EEC1AFF846E0064E85B /* DataStack+Transaction.swift */, B5E84EED1AFF846E0064E85B /* DetachedDataTransaction.swift */, B5E84EEE1AFF846E0064E85B /* HardcoreData+Transaction.swift */, @@ -311,12 +314,12 @@ B5E84EFF1AFF847B0064E85B /* Concrete Clauses */ = { isa = PBXGroup; children = ( - B5E84F001AFF847B0064E85B /* CustomizeFetch.swift */, B5E84F011AFF847B0064E85B /* From.swift */, - B5E84F021AFF847B0064E85B /* GroupBy.swift */, B5E84F031AFF847B0064E85B /* Select.swift */, - B5E84F041AFF847B0064E85B /* SortedBy.swift */, B5E84F051AFF847B0064E85B /* Where.swift */, + B5E84F041AFF847B0064E85B /* OrderBy.swift */, + B5E84F021AFF847B0064E85B /* GroupBy.swift */, + B5E84F001AFF847B0064E85B /* Tweak.swift */, ); path = "Concrete Clauses"; sourceTree = ""; @@ -335,9 +338,9 @@ B5E84F1A1AFF84860064E85B /* DataStack+Observing.swift */, B5E84F1B1AFF84860064E85B /* HardcoreData+Observing.swift */, B5E84F1C1AFF84860064E85B /* ManagedObjectController.swift */, + B5E84F1F1AFF84860064E85B /* ManagedObjectObserver.swift */, B5E84F1D1AFF84860064E85B /* ManagedObjectListController.swift */, B5E84F1E1AFF84860064E85B /* ManagedObjectListObserver.swift */, - B5E84F1F1AFF84860064E85B /* ManagedObjectObserver.swift */, ); path = Observing; sourceTree = ""; @@ -497,12 +500,13 @@ buildActionMask = 2147483647; files = ( B5E84F221AFF84860064E85B /* ManagedObjectController.swift in Sources */, + B504D0D61B02362500B2BBB1 /* HardcoreData+Setup.swift in Sources */, B5D1E22C19FA9FBC003B2874 /* NSError+HardcoreData.swift in Sources */, B5E84F131AFF847B0064E85B /* Where.swift in Sources */, B5E84F141AFF847B0064E85B /* DataStack+Querying.swift in Sources */, B5E84F371AFF85470064E85B /* NSManagedObjectContext+Transaction.swift in Sources */, - B5E84F0E1AFF847B0064E85B /* CustomizeFetch.swift in Sources */, - B5E84F121AFF847B0064E85B /* SortedBy.swift in Sources */, + B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */, + B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */, B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */, B5E84EE71AFF84610064E85B /* HardcoreData+Logging.swift in Sources */, B5E84F111AFF847B0064E85B /* Select.swift in Sources */, diff --git a/HardcoreData/Fetching and Querying/BaseDataTransaction+Querying.swift b/HardcoreData/Fetching and Querying/BaseDataTransaction+Querying.swift index 36a7ff0..c3bf8fe 100644 --- a/HardcoreData/Fetching and Querying/BaseDataTransaction+Querying.swift +++ b/HardcoreData/Fetching and Querying/BaseDataTransaction+Querying.swift @@ -33,6 +33,13 @@ public extension BaseDataTransaction { // MARK: Public + /** + Fetches the first `NSManagedObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: 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(from: From, _ fetchClauses: FetchClause...) -> T? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -40,6 +47,13 @@ public extension BaseDataTransaction { return self.context.fetchOne(from, fetchClauses) } + /** + Fetches the first `NSManagedObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: 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(from: From, _ fetchClauses: [FetchClause]) -> T? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -47,6 +61,13 @@ public extension BaseDataTransaction { return self.context.fetchOne(from, fetchClauses) } + /** + Fetches all `NSManagedObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s + */ public func fetchAll(from: From, _ fetchClauses: FetchClause...) -> [T]? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -54,6 +75,13 @@ public extension BaseDataTransaction { return self.context.fetchAll(from, fetchClauses) } + /** + Fetches all `NSManagedObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s + */ public func fetchAll(from: From, _ fetchClauses: [FetchClause]) -> [T]? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -61,6 +89,13 @@ public extension BaseDataTransaction { return self.context.fetchAll(from, fetchClauses) } + /** + Fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public func fetchCount(from: From, _ fetchClauses: FetchClause...) -> Int? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -68,6 +103,13 @@ public extension BaseDataTransaction { return self.context.fetchCount(from, fetchClauses) } + /** + Fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public func fetchCount(from: From, _ fetchClauses: [FetchClause]) -> Int? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -75,6 +117,13 @@ public extension BaseDataTransaction { return self.context.fetchCount(from, fetchClauses) } + /** + Fetches the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s + */ public func fetchObjectID(from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -82,6 +131,13 @@ public extension BaseDataTransaction { return self.context.fetchObjectID(from, fetchClauses) } + /** + Fetches the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s + */ public func fetchObjectID(from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -89,6 +145,13 @@ public extension BaseDataTransaction { return self.context.fetchObjectID(from, fetchClauses) } + /** + Fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public func fetchObjectIDs(from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -96,6 +159,13 @@ public extension BaseDataTransaction { return self.context.fetchObjectIDs(from, fetchClauses) } + /** + Fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public func fetchObjectIDs(from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside its designated queue.") @@ -103,6 +173,13 @@ public extension BaseDataTransaction { return self.context.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(from: From, _ deleteClauses: DeleteClause...) -> Int? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete from a \(typeName(self)) outside its designated queue.") @@ -110,6 +187,13 @@ public extension BaseDataTransaction { return self.context.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(from: From, _ deleteClauses: [DeleteClause]) -> Int? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete from a \(typeName(self)) outside its designated queue.") @@ -117,6 +201,16 @@ public extension BaseDataTransaction { return self.context.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. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public func queryValue(from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside its designated queue.") @@ -124,6 +218,16 @@ public extension BaseDataTransaction { return self.context.queryValue(from, selectClause, queryClauses) } + /** + Queries aggregate values or aggregates as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public func queryValue(from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside its designated queue.") @@ -131,6 +235,16 @@ public extension BaseDataTransaction { return self.context.queryValue(from, selectClause, queryClauses) } + /** + Queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public func queryAttributes(from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside its designated queue.") @@ -138,6 +252,16 @@ public extension BaseDataTransaction { return self.context.queryAttributes(from, selectClause, queryClauses) } + /** + Queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public func queryAttributes(from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside its designated queue.") diff --git a/HardcoreData/Fetching and Querying/Concrete Clauses/From.swift b/HardcoreData/Fetching and Querying/Concrete Clauses/From.swift index 2fa12aa..bec634a 100644 --- a/HardcoreData/Fetching and Querying/Concrete Clauses/From.swift +++ b/HardcoreData/Fetching and Querying/Concrete Clauses/From.swift @@ -29,6 +29,9 @@ import CoreData // MARK: - From +/** +A `Form` clause binds the `NSManagedObject` entity type to the generics type system. +*/ public struct From { public init(){ } diff --git a/HardcoreData/Fetching and Querying/Concrete Clauses/GroupBy.swift b/HardcoreData/Fetching and Querying/Concrete Clauses/GroupBy.swift index e90a711..c9733de 100644 --- a/HardcoreData/Fetching and Querying/Concrete Clauses/GroupBy.swift +++ b/HardcoreData/Fetching and Querying/Concrete Clauses/GroupBy.swift @@ -29,23 +29,40 @@ import CoreData // MARK: - GroupBy +/** +The `GroupBy` clause specifies that the result of a query be grouped accoording to the specified key path. +*/ public struct GroupBy: QueryClause { // MARK: Public + /** + Initializes a `GroupBy` clause with a list of key path strings + + :param: keyPaths a list of key path strings to group results with + */ public init(_ keyPaths: [KeyPath]) { self.keyPaths = keyPaths } + /** + Initializes a `GroupBy` clause with an empty list of key path strings + */ public init() { self.init([]) } - public init(_ keyPath: KeyPath, _ subKeyPaths: KeyPath...) { + /** + Initializes a `GroupBy` clause with a list of key path strings + + :param: keyPath a key path string to group results with + :param: keyPaths a series of key path strings to group results with + */ + public init(_ keyPath: KeyPath, _ keyPaths: KeyPath...) { - self.init([keyPath] + subKeyPaths) + self.init([keyPath] + keyPaths) } public let keyPaths: [KeyPath] diff --git a/HardcoreData/Fetching and Querying/Concrete Clauses/SortedBy.swift b/HardcoreData/Fetching and Querying/Concrete Clauses/OrderBy.swift similarity index 62% rename from HardcoreData/Fetching and Querying/Concrete Clauses/SortedBy.swift rename to HardcoreData/Fetching and Querying/Concrete Clauses/OrderBy.swift index e31062f..8c36cde 100644 --- a/HardcoreData/Fetching and Querying/Concrete Clauses/SortedBy.swift +++ b/HardcoreData/Fetching and Querying/Concrete Clauses/OrderBy.swift @@ -1,5 +1,5 @@ // -// SortedBy.swift +// OrderBy.swift // HardcoreData // // Copyright (c) 2015 John Rommel Estropia @@ -26,9 +26,9 @@ import Foundation -public func +(left: SortedBy, right: SortedBy) -> SortedBy { +public func +(left: OrderBy, right: OrderBy) -> OrderBy { - return SortedBy(left.sortDescriptors + right.sortDescriptors) + return OrderBy(left.sortDescriptors + right.sortDescriptors) } @@ -37,42 +37,73 @@ public func +(left: SortedBy, right: SortedBy) -> SortedBy { public typealias KeyPath = String -// MARK: - SortOrder +// MARK: - SortKey -public enum SortOrder { +/** +The `SortKey` is passed to the `OrderBy` clause to indicate the sort keys and their sort direction. +*/ +public enum SortKey { + /** + Indicates that the `KeyPath` should be sorted in ascending order + */ case Ascending(KeyPath) + + /** + Indicates that the `KeyPath` should be sorted in descending order + */ case Descending(KeyPath) } -// MARK: - SortedBy +// MARK: - OrderBy -public struct SortedBy: FetchClause, QueryClause, DeleteClause { +/** +The `OrderBy` clause specifies the sort order for results for a fetch or a query. +*/ +public struct OrderBy: FetchClause, QueryClause, DeleteClause { // MARK: Public + /** + Initializes a `OrderBy` clause with a list of sort descriptors + + :param: sortDescriptors a series of `NSSortDescriptor`'s + */ public init(_ sortDescriptors: [NSSortDescriptor]) { self.sortDescriptors = sortDescriptors } + /** + Initializes a `OrderBy` clause with an empty list of sort descriptors + */ public init() { self.init([NSSortDescriptor]()) } + /** + Initializes a `OrderBy` clause with a single sort descriptor + + :param: sortDescriptor a `NSSortDescriptor` + */ public init(_ sortDescriptor: NSSortDescriptor) { self.init([sortDescriptor]) } - public init(_ order: [SortOrder]) { + /** + Initializes a `OrderBy` clause with a series of `SortKey`'s + + :param: sortKey a series of `SortKey`'s + */ + public init(_ sortKey: [SortKey]) { self.init( - order.map { sortOrder -> NSSortDescriptor in + sortKey.map { SortKey -> NSSortDescriptor in - switch sortOrder { + switch SortKey { case .Ascending(let keyPath): return NSSortDescriptor(key: keyPath, ascending: true) @@ -84,9 +115,15 @@ public struct SortedBy: FetchClause, QueryClause, DeleteClause { ) } - public init(_ order: SortOrder, _ subOrder: SortOrder...) { + /** + Initializes a `OrderBy` clause with a series of `SortKey`'s + + :param: sortKey a single `SortKey` + :param: sortKeys a series of `SortKey`'s + */ + public init(_ sortKey: SortKey, _ sortKeys: SortKey...) { - self.init([order] + subOrder) + self.init([sortKey] + sortKeys) } public let sortDescriptors: [NSSortDescriptor] diff --git a/HardcoreData/Fetching and Querying/Concrete Clauses/Select.swift b/HardcoreData/Fetching and Querying/Concrete Clauses/Select.swift index 02cfdbb..28ad208 100644 --- a/HardcoreData/Fetching and Querying/Concrete Clauses/Select.swift +++ b/HardcoreData/Fetching and Querying/Concrete Clauses/Select.swift @@ -29,11 +29,17 @@ import CoreData // MARK: - SelectResultType +/** +The `SelectResultType` protocol is implemented by return types supported by the `Select` clause. +*/ public protocol SelectResultType { } // MARK: - SelectValueResultType +/** +The `SelectValueResultType` protocol is implemented by return types supported by the `queryValue(...)` methods. +*/ public protocol SelectValueResultType: SelectResultType { static func fromResultObject(result: AnyObject) -> Self? @@ -42,6 +48,9 @@ public protocol SelectValueResultType: SelectResultType { // MARK: - SelectAttributesResultType +/** +The `SelectValueResultType` protocol is implemented by return types supported by the `queryAttributes(...)` methods. +*/ public protocol SelectAttributesResultType: SelectResultType { static func fromResultObjects(result: [AnyObject]) -> [[NSString: AnyObject]] @@ -50,99 +59,268 @@ public protocol SelectAttributesResultType: SelectResultType { // MARK: - SelectTerm +/** +The `SelectTerm` is passed to the `Select` clause to indicate the attributes/aggregate keys to be queried. +*/ public enum SelectTerm: StringLiteralConvertible { - case Attribute(KeyPath) - case Aggregate(function: String, KeyPath, As: String) + // MARK: Public + /** + Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. A shorter way to do the same is to assign from the string keypath directly: + + let fullName = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Attribute("fullName")), + Where("employeeID", isEqualTo: 1111) + ) + + is equivalent to: + + let fullName = HardcoreData.queryValue( + From(MyPersonEntity), + Select("fullName"), + Where("employeeID", isEqualTo: 1111) + ) + + :param: keyPath the attribute name + :returns: a `SelectTerm` to a `Select` clause for querying an entity attribute + */ + public static func Attribute(keyPath: KeyPath) -> SelectTerm { + + return ._Attribute(keyPath) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute. + + let averageAge = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Average("age")) + ) + + :param: keyPath the attribute name + :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average()" is used + :returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute + */ public static func Average(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - return .Aggregate( + return ._Aggregate( function: "average:", keyPath, As: alias ?? "average(\(keyPath))" ) } + /** + Provides a `SelectTerm` to a `Select` clause for a count query. + + let numberOfEmployees = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Count("employeeID")) + ) + + :param: keyPath the attribute name + :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count()" is used + :returns: a `SelectTerm` to a `Select` clause for a count query + */ public static func Count(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - return .Aggregate( + return ._Aggregate( function: "count:", keyPath, As: alias ?? "count(\(keyPath))" ) } + /** + Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute. + + let maximumAge = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Maximum("age")) + ) + + :param: keyPath the attribute name + :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max()" is used + :returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute + */ public static func Maximum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - return .Aggregate( + return ._Aggregate( function: "max:", keyPath, As: alias ?? "max(\(keyPath))" ) } + /** + Provides a `SelectTerm` to a `Select` clause for querying the median value for an attribute. + + let medianAge = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Median("age")) + ) + + :param: keyPath the attribute name + :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max()" is used + :returns: a `SelectTerm` to a `Select` clause for querying the median value for an attribute + */ public static func Median(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - return .Aggregate( + return ._Aggregate( function: "median:", keyPath, As: alias ?? "median(\(keyPath))" ) } + /** + Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute. + + let minimumAge = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Median("age")) + ) + + :param: keyPath the attribute name + :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min()" is used + :returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute + */ public static func Minimum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - return .Aggregate( + return ._Aggregate( function: "min:", keyPath, As: alias ?? "min(\(keyPath))" ) } + /** + Provides a `SelectTerm` to a `Select` clause for querying the standard deviation value for an attribute. + + let stddevAge = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.StandardDeviation("age")) + ) + + :param: keyPath the attribute name + :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "stddev()" is used + :returns: a `SelectTerm` to a `Select` clause for querying the standard deviation value for an attribute + */ public static func StandardDeviation(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - return .Aggregate( + return ._Aggregate( function: "stddev:", keyPath, As: alias ?? "stddev(\(keyPath))" ) } + /** + Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute. + + let totalAge = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Sum("age")) + ) + + :param: keyPath the attribute name + :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum()" is used + :returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute + */ public static func Sum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - return .Aggregate( + return ._Aggregate( function: "sum:", keyPath, As: alias ?? "sum(\(keyPath))" ) } + + // MARK: StringLiteralConvertible + public init(stringLiteral value: KeyPath) { - self = .Attribute(value) + self = ._Attribute(value) } public init(unicodeScalarLiteral value: KeyPath) { - self = .Attribute(value) + self = ._Attribute(value) } public init(extendedGraphemeClusterLiteral value: KeyPath) { - self = .Attribute(value) + self = ._Attribute(value) } + + + // MARK: Internal + + case _Attribute(KeyPath) + case _Aggregate(function: String, KeyPath, As: String) } // MARK: - Select +/** +The `Select` clause indicates the attribute / aggregate value to be queried. The generic type is a `SelectResultType`, and will be used as the return type for the query. + +You can bind the return type by specializing the initializer: + + let maximumAge = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Maximum("age")) + ) + +or by casting the type of the return value: + + let maximumAge: Int = HardcoreData.queryValue( + From(MyPersonEntity), + Select(.Maximum("age")) + ) + +Valid return types depend on the query: + +- for `queryValue(...)` methods: + - `Bool` + - `Int8` + - `Int16` + - `Int32` + - `Int64` + - `Double` + - `Float` + - `String` + - `NSNumber` + - `NSString` + - `NSDecimalNumber` + - `NSDate` + - `NSData` + - `NSManagedObjectID` + - `NSString` +- for `queryAttributes(...)` methods: + - `NSDictionary` + +:param: sortDescriptors a series of `NSSortDescriptor`'s +*/ public struct Select { // MARK: Public + /** + The `SelectResultType` type for the query's return value + */ public typealias ReturnType = T + /** + Initializes a `Select` clause with a list of `SelectTerm`'s + + :param: selectTerm a `SelectTerm` + :param: selectTerms a series of `SelectTerm`'s + */ public init(_ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) { self.selectTerms = [selectTerm] + selectTerms @@ -170,7 +348,7 @@ public struct Select { switch term { - case .Attribute(let keyPath): + case ._Attribute(let keyPath): if let propertyDescription = propertiesByName[keyPath] as? NSPropertyDescription { propertiesToFetch.append(propertyDescription) @@ -180,7 +358,7 @@ public struct Select { HardcoreData.log(.Warning, message: "The property \"\(keyPath)\" does not exist in entity <\(entityDescription.managedObjectClassName)> and will be ignored by \(typeName(self)) query clause.") } - case .Aggregate(let function, let keyPath, let alias): + case ._Aggregate(let function, let keyPath, let alias): if let attributeDescription = attributesByName[keyPath] as? NSAttributeDescription { let expressionDescription = NSExpressionDescription() @@ -207,10 +385,10 @@ public struct Select { switch self.selectTerms.first! { - case .Attribute(let keyPath): + case ._Attribute(let keyPath): return keyPath - case .Aggregate(_, _, let alias): + case ._Aggregate(_, _, let alias): return alias } } diff --git a/HardcoreData/Fetching and Querying/Concrete Clauses/CustomizeFetch.swift b/HardcoreData/Fetching and Querying/Concrete Clauses/Tweak.swift similarity index 72% rename from HardcoreData/Fetching and Querying/Concrete Clauses/CustomizeFetch.swift rename to HardcoreData/Fetching and Querying/Concrete Clauses/Tweak.swift index b1e2d86..b9405e4 100644 --- a/HardcoreData/Fetching and Querying/Concrete Clauses/CustomizeFetch.swift +++ b/HardcoreData/Fetching and Querying/Concrete Clauses/Tweak.swift @@ -1,5 +1,5 @@ // -// CustomizeFetch.swift +// Tweak.swift // HardcoreData // // Copyright (c) 2015 John Rommel Estropia @@ -27,12 +27,30 @@ import Foundation import CoreData -// MARK: - CustomizeFetch +// MARK: - Tweak -public struct CustomizeFetch: FetchClause, QueryClause, DeleteClause { +/** +The `Tweak` clause allows fine-tuning the `NSFetchRequest` for a fetch or query. + +Sample usage: + + let employees = transaction.fetchAll( + From(MyPersonEntity), + Tweak { (fetchRequest) -> Void in + fetchRequest.includesPendingChanges = false + fetchRequest.fetchLimit = 5 + } + ) +*/ +public struct Tweak: FetchClause, QueryClause, DeleteClause { // MARK: Public + /** + Initializes a `Tweak` clause with a closure where the `NSFetchRequest` may be configured. + + :param: customization a list of key path strings to group results with + */ public init(_ customization: (fetchRequest: NSFetchRequest) -> Void) { self.customization = customization diff --git a/HardcoreData/Fetching and Querying/Concrete Clauses/Where.swift b/HardcoreData/Fetching and Querying/Concrete Clauses/Where.swift index 5614769..dc6002b 100644 --- a/HardcoreData/Fetching and Querying/Concrete Clauses/Where.swift +++ b/HardcoreData/Fetching and Querying/Concrete Clauses/Where.swift @@ -44,35 +44,69 @@ public prefix func !(clause: Where) -> Where { // MARK: - Where +/** +The `Where` clause specifies the conditions for a fetch or a query. +*/ public struct Where: FetchClause, QueryClause, DeleteClause { // MARK: Public + /** + Initializes a `Where` clause with an `NSPredicate` + + :param: predicate the `NSPredicate` for the fetch or query + */ public init(_ predicate: NSPredicate) { self.predicate = predicate } + /** + Initializes a `Where` clause with a predicate that always evaluates to `true` + */ public init() { self.init(true) } + /** + Initializes a `Where` clause with a predicate that always evaluates to the specified boolean value + + :param: value the boolean value for the predicate + */ public init(_ value: Bool) { self.init(NSPredicate(value: value)) } + /** + Initializes a `Where` clause with a predicate using the specified string format and arguments + + :param: format the format string for the predicate + :param: args the arguments for `format` + */ public init(_ format: String, _ args: NSObject...) { self.init(NSPredicate(format: format, argumentArray: args)) } + /** + Initializes a `Where` clause with a predicate using the specified string format and arguments + + :param: format the format string for the predicate + :param: argumentArray the arguments for `format` + */ public init(_ format: String, argumentArray: [NSObject]?) { self.init(NSPredicate(format: format, argumentArray: argumentArray)) } + /** + Initializes a `Where` clause with a predicate using the specified string format and arguments + + :param: format the format string for the predicate + :param: argumentArray the arguments for `format` + */ public init(_ keyPath: KeyPath, isEqualTo value: NSObject?) { self.init(value == nil diff --git a/HardcoreData/Fetching and Querying/DataStack+Querying.swift b/HardcoreData/Fetching and Querying/DataStack+Querying.swift index f40f4ba..224401c 100644 --- a/HardcoreData/Fetching and Querying/DataStack+Querying.swift +++ b/HardcoreData/Fetching and Querying/DataStack+Querying.swift @@ -34,6 +34,13 @@ public extension DataStack { // MARK: Public + /** + Fetches the first `NSManagedObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: 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(from: From, _ fetchClauses: FetchClause...) -> T? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -41,6 +48,13 @@ public extension DataStack { return self.mainContext.fetchOne(from, fetchClauses) } + /** + Fetches the first `NSManagedObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: 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(from: From, _ fetchClauses: [FetchClause]) -> T? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -48,6 +62,13 @@ public extension DataStack { return self.mainContext.fetchOne(from, fetchClauses) } + /** + Fetches all `NSManagedObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s + */ public func fetchAll(from: From, _ fetchClauses: FetchClause...) -> [T]? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -55,6 +76,13 @@ public extension DataStack { return self.mainContext.fetchAll(from, fetchClauses) } + /** + Fetches all `NSManagedObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s + */ public func fetchAll(from: From, _ fetchClauses: [FetchClause]) -> [T]? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -62,6 +90,13 @@ public extension DataStack { return self.mainContext.fetchAll(from, fetchClauses) } + /** + Fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public func fetchCount(from: From, _ fetchClauses: FetchClause...) -> Int? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -69,6 +104,13 @@ public extension DataStack { return self.mainContext.fetchCount(from, fetchClauses) } + /** + Fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public func fetchCount(from: From, _ fetchClauses: [FetchClause]) -> Int? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -76,6 +118,13 @@ public extension DataStack { return self.mainContext.fetchCount(from, fetchClauses) } + /** + Fetches the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s + */ public func fetchObjectID(from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -83,6 +132,13 @@ public extension DataStack { return self.mainContext.fetchObjectID(from, fetchClauses) } + /** + Fetches the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s + */ public func fetchObjectID(from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -90,6 +146,13 @@ public extension DataStack { return self.mainContext.fetchObjectID(from, fetchClauses) } + /** + Fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public func fetchObjectIDs(from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -97,6 +160,13 @@ public extension DataStack { return self.mainContext.fetchObjectIDs(from, fetchClauses) } + /** + Fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public func fetchObjectIDs(from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.") @@ -104,6 +174,13 @@ public extension DataStack { 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(from: From, _ deleteClauses: DeleteClause...) -> Int? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a \(typeName(self)) outside the main queue.") @@ -111,6 +188,13 @@ public extension DataStack { 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(from: From, _ deleteClauses: [DeleteClause]) -> Int? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a \(typeName(self)) outside the main queue.") @@ -118,6 +202,16 @@ public extension DataStack { 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. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public func queryValue(from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.") @@ -125,6 +219,16 @@ public extension DataStack { return self.mainContext.queryValue(from, selectClause, queryClauses) } + /** + Queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public func queryValue(from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.") @@ -132,6 +236,16 @@ public extension DataStack { return self.mainContext.queryValue(from, selectClause, queryClauses) } + /** + Queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public func queryAttributes(from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.") @@ -139,6 +253,16 @@ public extension DataStack { return self.mainContext.queryAttributes(from, selectClause, queryClauses) } + /** + Queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public func queryAttributes(from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.") diff --git a/HardcoreData/Fetching and Querying/HardcoreData+Querying.swift b/HardcoreData/Fetching and Querying/HardcoreData+Querying.swift index 753a86b..a625924 100644 --- a/HardcoreData/Fetching and Querying/HardcoreData+Querying.swift +++ b/HardcoreData/Fetching and Querying/HardcoreData+Querying.swift @@ -31,81 +31,205 @@ public extension HardcoreData { // MARK: Public + /** + Using the `defaultStack`, fetches the first `NSManagedObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: 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 static func fetchOne(from: From, _ fetchClauses: FetchClause...) -> T? { return self.defaultStack.fetchOne(from, fetchClauses) } + /** + Using the `defaultStack`, fetches the first `NSManagedObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: 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 static func fetchOne(from: From, _ fetchClauses: [FetchClause]) -> T? { return self.defaultStack.fetchOne(from, fetchClauses) } + /** + Using the `defaultStack`, fetches all `NSManagedObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s + */ public static func fetchAll(from: From, _ fetchClauses: FetchClause...) -> [T]? { return self.defaultStack.fetchAll(from, fetchClauses) } + /** + Using the `defaultStack`, fetches all `NSManagedObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s + */ public static func fetchAll(from: From, _ fetchClauses: [FetchClause]) -> [T]? { return self.defaultStack.fetchAll(from, fetchClauses) } + /** + Using the `defaultStack`, fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public static func fetchCount(from: From, _ fetchClauses: FetchClause...) -> Int? { return self.defaultStack.fetchCount(from, fetchClauses) } + /** + Using the `defaultStack`, fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public static func fetchCount(from: From, _ fetchClauses: [FetchClause]) -> Int? { return self.defaultStack.fetchCount(from, fetchClauses) } + /** + Using the `defaultStack`, fetches the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s + */ public static func fetchObjectID(from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { return self.defaultStack.fetchObjectID(from, fetchClauses) } + /** + Using the `defaultStack`, fetches the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s + */ public static func fetchObjectID(from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { return self.defaultStack.fetchObjectID(from, fetchClauses) } + /** + Using the `defaultStack`, fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public static func fetchObjectIDs(from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { return self.defaultStack.fetchObjectIDs(from, fetchClauses) } + /** + Using the `defaultStack`, fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s + */ public static func fetchObjectIDs(from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { 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(from: From, _ 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(from: From, _ 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. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public static func queryValue(from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { return self.defaultStack.queryValue(from, selectClause, queryClauses) } + /** + 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. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public static func queryValue(from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { return self.defaultStack.queryValue(from, selectClause, queryClauses) } + /** + Using the `defaultStack`, queries a dictionary of attribtue values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public static func queryAttributes(from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? { return self.defaultStack.queryAttributes(from, selectClause, queryClauses) } + /** + Using the `defaultStack`, queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + + A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result. + + :param: from a `From` clause indicating the entity type + :param: selectClause a `Select` clause indicating the properties to fetch, and with the generic type indicating the return type. + :param: queryClauses a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. + :returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. + */ public static func queryAttributes(from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? { return self.defaultStack.queryAttributes(from, selectClause, queryClauses) diff --git a/HardcoreData/HardcoreData.swift b/HardcoreData/HardcoreData.swift index 14f25b9..e2fcbea 100644 --- a/HardcoreData/HardcoreData.swift +++ b/HardcoreData/HardcoreData.swift @@ -36,16 +36,16 @@ public typealias HCD = HardcoreData // MARK: - HardcoreData /** -HardcoreData is the main entry point for all other APIs. +`HardcoreData` is the main entry point for all other APIs. */ 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. + The default `DataStack` instance to be used. If `defaultStack` is not set before the first time accessed, a default-configured `DataStack` will be created. - Changing the defaultStack is thread safe, but it is recommended to setup DataStacks on a common queue (e.g. the main queue). + Changing the `defaultStack` is thread safe, but it is recommended to setup `DataStacks` on a common queue (e.g. the main queue). */ public static var defaultStack: DataStack { diff --git a/HardcoreData/Logging/DefaultLogger.swift b/HardcoreData/Logging/DefaultLogger.swift index 4d6133b..f86cacf 100644 --- a/HardcoreData/Logging/DefaultLogger.swift +++ b/HardcoreData/Logging/DefaultLogger.swift @@ -28,6 +28,13 @@ import Foundation // MARK: - DefaultLogger +/** +The `DefaultLogger` is a basic implementation of the `HardcoreDataLogger` protocol. + +- The `log(...)` method calls `println(...)` to print the level, source file name, line number, function name, and the log message. +- The `handleError(...)` method calls `println(...)` to print the source file name, line number, function name, and the error message. +- The `assert(...)` method calls `assert(...)` on the arguments. +*/ public final class DefaultLogger: HardcoreDataLogger { public func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) { diff --git a/HardcoreData/Logging/HardcoreData+Logging.swift b/HardcoreData/Logging/HardcoreData+Logging.swift index 3dd8ffa..f0ff785 100644 --- a/HardcoreData/Logging/HardcoreData+Logging.swift +++ b/HardcoreData/Logging/HardcoreData+Logging.swift @@ -33,7 +33,7 @@ public extension HardcoreData { // MARK: Public /** - The HardcoreDataLogger instance to be used. The default logger is an instance of a DefaultLogger. + The `HardcoreDataLogger` instance to be used. The default logger is an instance of a `DefaultLogger`. */ public static var logger: HardcoreDataLogger = DefaultLogger() diff --git a/HardcoreData/Logging/HardcoreDataLogger.swift b/HardcoreData/Logging/HardcoreDataLogger.swift index 37173a5..f5cafba 100644 --- a/HardcoreData/Logging/HardcoreDataLogger.swift +++ b/HardcoreData/Logging/HardcoreDataLogger.swift @@ -28,6 +28,9 @@ import Foundation // MARK: - LogLevel +/** +The `LogLevel` indicates the severity of a log message. +*/ public enum LogLevel { case Trace @@ -39,12 +42,42 @@ public enum LogLevel { // MARK: - HardcoreDataLogger +/** +Custom loggers should implement the `HardcoreDataLogger` protocol and pass its instance to `HardcoreData.logger`. Calls to `log(...)`, `handleError(...)`, and `assert(...)` are not tied to a specific queue/thread, so it is the implementer's job to handle thread-safety. +*/ public protocol HardcoreDataLogger { + /** + Handles log messages sent by the `HardcoreData` framework. + + :level: the severity of the log message + :message: the log message + :fileName: the source file name + :lineNumber: the source line number + :functionName: the source function name + */ func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) + /** + Handles errors sent by the `HardcoreData` framework. + + :error: the error + :message: the error message + :fileName: the source file name + :lineNumber: the source line number + :functionName: the source function name + */ func handleError(#error: NSError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) + /** + Handles assertions made throughout the `HardcoreData` framework. + + :condition: the assertion condition + :message: the assertion message + :fileName: the source file name + :lineNumber: the source line number + :functionName: the source function name + */ func assert(@autoclosure condition: () -> Bool, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) } diff --git a/HardcoreData/NSError+HardcoreData.swift b/HardcoreData/NSError+HardcoreData.swift index 35a665b..203fec7 100644 --- a/HardcoreData/NSError+HardcoreData.swift +++ b/HardcoreData/NSError+HardcoreData.swift @@ -26,12 +26,12 @@ import Foundation /** -The NSError error domain for HardcoreData. +The `NSError` error domain for `HardcoreData`. */ public let HardcoreDataErrorDomain = "com.hardcoredata.error" /** -The NSError error codes for HardcoreDataErrorDomain. +The `NSError` error codes for `HardcoreDataErrorDomain`. */ public enum HardcoreDataErrorCode: Int { @@ -41,7 +41,7 @@ public enum HardcoreDataErrorCode: Int { case UnknownError /** - The NSPersistentStore could note be initialized because another store existed at the specified NSURL. + The `NSPersistentStore` could note be initialized because another store existed at the specified `NSURL`. */ case DifferentPersistentStoreExistsAtURL } @@ -52,7 +52,7 @@ public enum HardcoreDataErrorCode: Int { public extension NSError { /** - If the error's domain is equal to HardcoreDataErrorDomain, returns the associated HardcoreDataErrorCode. For other domains, returns nil. + If the error's domain is equal to `HardcoreDataErrorDomain`, returns the associated `HardcoreDataErrorCode`. For other domains, returns `nil`. */ public var hardcoreDataErrorCode: HardcoreDataErrorCode? { diff --git a/HardcoreData/Observing/DataStack+Observing.swift b/HardcoreData/Observing/DataStack+Observing.swift index 4e95da2..a7022a6 100644 --- a/HardcoreData/Observing/DataStack+Observing.swift +++ b/HardcoreData/Observing/DataStack+Observing.swift @@ -34,6 +34,12 @@ public extension DataStack { // MARK: Public + /** + Creates a `ManagedObjectController` for the specified `NSManagedObject`. Multiple `ManagedObjectObserver`'s may then register themselves to be notified when changes are made to the `NSManagedObject`. + + :param: object the `NSManagedObject` to observe changes from + :returns: a `ManagedObjectController` that monitors changes to `object` + */ public func observeObject(object: T) -> ManagedObjectController { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.") @@ -44,11 +50,25 @@ public extension DataStack { ) } + /** + Creates a `ManagedObjectListController` for a list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: a `ManagedObjectListController` instance that monitors changes to the list + */ public func observeObjectList(from: From, _ fetchClauses: FetchClause...) -> ManagedObjectListController { return self.observeObjectList(from, fetchClauses) } + /** + Creates a `ManagedObjectListController` for a list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: a `ManagedObjectListController` instance that monitors changes to the list + */ public func observeObjectList(from: From, _ fetchClauses: [FetchClause]) -> ManagedObjectListController { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.") @@ -61,11 +81,27 @@ public extension DataStack { ) } + /** + Creates a `ManagedObjectListController` for a sectioned list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list. + + :param: from a `From` clause indicating the entity type + :param: sectionedBy a `SectionedBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. + :param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: a `ManagedObjectListController` instance that monitors changes to the list + */ public func observeSectionedList(from: From, _ sectionedBy: SectionedBy, _ fetchClauses: FetchClause...) -> ManagedObjectListController { return self.observeSectionedList(from, sectionedBy, fetchClauses) } + /** + Creates a `ManagedObjectListController` for a sectioned list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list. + + :param: from a `From` clause indicating the entity type + :param: sectionedBy a `SectionedBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. + :param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: a `ManagedObjectListController` instance that monitors changes to the list + */ public func observeSectionedList(from: From, _ sectionedBy: SectionedBy, _ fetchClauses: [FetchClause]) -> ManagedObjectListController { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.") diff --git a/HardcoreData/Observing/HardcoreData+Observing.swift b/HardcoreData/Observing/HardcoreData+Observing.swift index ea55bad..1c788dc 100644 --- a/HardcoreData/Observing/HardcoreData+Observing.swift +++ b/HardcoreData/Observing/HardcoreData+Observing.swift @@ -33,26 +33,62 @@ public extension HardcoreData { // MARK: Public + /** + Using the `defaultStack`, creates a `ManagedObjectController` for the specified `NSManagedObject`. Multiple `ManagedObjectObserver`'s may then register themselves to be notified when changes are made to the `NSManagedObject`. + + :param: object the `NSManagedObject` to observe changes from + :returns: a `ManagedObjectController` that monitors changes to `object` + */ public static func observeObject(object: T) -> ManagedObjectController { return self.defaultStack.observeObject(object) } + /** + Using the `defaultStack`, creates a `ManagedObjectListController` for a list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: a `ManagedObjectListController` instance that monitors changes to the list + */ public static func observeObjectList(from: From, _ groupBy: GroupBy? = nil, _ queryClauses: FetchClause...) -> ManagedObjectListController { return self.defaultStack.observeObjectList(from, queryClauses) } + /** + Using the `defaultStack`, creates a `ManagedObjectListController` for a list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list. + + :param: from a `From` clause indicating the entity type + :param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: a `ManagedObjectListController` instance that monitors changes to the list + */ public static func observeObjectList(from: From, _ groupBy: GroupBy? = nil, _ queryClauses: [FetchClause]) -> ManagedObjectListController { return self.defaultStack.observeObjectList(from, queryClauses) } + /** + Using the `defaultStack`, creates a `ManagedObjectListController` for a sectioned list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list. + + :param: from a `From` clause indicating the entity type + :param: sectionedBy a `SectionedBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. + :param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: a `ManagedObjectListController` instance that monitors changes to the list + */ public static func observeSectionedList(from: From, _ sectionedBy: SectionedBy, _ fetchClauses: FetchClause...) -> ManagedObjectListController { return self.defaultStack.observeSectionedList(from, sectionedBy, fetchClauses) } + /** + Using the `defaultStack`, creates a `ManagedObjectListController` for a sectioned list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list. + + :param: from a `From` clause indicating the entity type + :param: sectionedBy a `SectionedBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. + :param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + :returns: a `ManagedObjectListController` instance that monitors changes to the list + */ public static func observeSectionedList(from: From, _ sectionedBy: SectionedBy, _ fetchClauses: [FetchClause]) -> ManagedObjectListController { return self.defaultStack.observeSectionedList(from, sectionedBy, fetchClauses) diff --git a/HardcoreData/Observing/ManagedObjectController.swift b/HardcoreData/Observing/ManagedObjectController.swift index 9a3877c..8d9bde2 100644 --- a/HardcoreData/Observing/ManagedObjectController.swift +++ b/HardcoreData/Observing/ManagedObjectController.swift @@ -44,24 +44,53 @@ private struct NotificationKey { // MARK: - ManagedObjectController -public final class ManagedObjectController: FetchedResultsControllerHandler { +/** +The `ManagedObjectController` monitors changes to a single `NSManagedObject` instance. Observers that implement the `ManagedObjectObserver` protocol may then register themselves to the `ManagedObjectController`'s `addObserver(_:)` method: + + let objectController = HardcoreData.observeObject(object) + objectController.addObserver(self) + +The created `ManagedObjectController` instance needs to be held on (retained) for as long as the object needs to be observed. + +Observers registered via `addObserver(_:)` are not retained. `ManagedObjectController` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles. +*/ +public final class ManagedObjectController { // MARK: Public + /** + Returns the `NSManagedObject` instance being observed, or `nil` if the object was already deleted. + */ public var object: T? { return self.fetchedResultsController.fetchedObjects?.first as? T } + /** + Returns `true` if the `NSManagedObject` instance being observed still exists, or `false` if the object was already deleted. + */ public var isObjectDeleted: Bool { return self.object?.managedObjectContext == nil } + /** + Registers a `ManagedObjectObserver` to be notified when changes to the receiver's `object` are made. + + To prevent retain-cycles, `ManagedObjectController` only keeps `weak` references to its observers. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + Calling `addObserver(_:)` multiple times on the same observer is safe, as `ManagedObjectController` unregisters previous notifications to the observer before re-registering them. + + :param: observer a `ManagedObjectObserver` to send change notifications to + */ public func addObserver(observer: U) { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.") + self.removeObserver(observer) + self.registerChangeNotification( &NotificationKey.willChangeObject, name: ManagedObjectListControllerWillChangeObjectNotification, @@ -117,6 +146,13 @@ public final class ManagedObjectController: FetchedResultsCo ) } + /** + Unregisters a `ManagedObjectObserver` from receiving notifications for changes to the receiver's `object`. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + :param: observer a `ManagedObjectObserver` to unregister notifications to + */ public func removeObserver(observer: U) { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to remove a \(typeName(observer)) outside the main queue.") @@ -128,40 +164,6 @@ public final class ManagedObjectController: FetchedResultsCo } - // MARK: FetchedResultsControllerHandler - - private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { - - switch type { - - case .Delete: - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidDeleteObjectNotification, - object: self, - userInfo: [UserInfoKeyObject: anObject] - ) - - case .Update: - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidUpdateObjectNotification, - object: self, - userInfo: [UserInfoKeyObject: anObject] - ) - - default: - break - } - } - - private func controllerWillChangeContent(controller: NSFetchedResultsController) { - - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerWillChangeObjectNotification, - object: self - ) - } - - // MARK: Internal internal init(dataStack: DataStack, object: T) { @@ -259,6 +261,45 @@ public final class ManagedObjectController: FetchedResultsCo } +// MARK: - ManagedObjectController: FetchedResultsControllerHandler + +extension ManagedObjectController: FetchedResultsControllerHandler { + + // MARK: FetchedResultsControllerHandler + + private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { + + switch type { + + case .Delete: + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidDeleteObjectNotification, + object: self, + userInfo: [UserInfoKeyObject: anObject] + ) + + case .Update: + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidUpdateObjectNotification, + object: self, + userInfo: [UserInfoKeyObject: anObject] + ) + + default: + break + } + } + + private func controllerWillChangeContent(controller: NSFetchedResultsController) { + + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerWillChangeObjectNotification, + object: self + ) + } +} + + // MARK: - FetchedResultsControllerHandler private protocol FetchedResultsControllerHandler: class { diff --git a/HardcoreData/Observing/ManagedObjectListController.swift b/HardcoreData/Observing/ManagedObjectListController.swift index 28d713f..bfc9824 100644 --- a/HardcoreData/Observing/ManagedObjectListController.swift +++ b/HardcoreData/Observing/ManagedObjectListController.swift @@ -30,21 +30,44 @@ import GCDKit // MARK: - SectionedBy +/** +The `SectionedBy` clause indicates the key path to use to group the `ManagedObjectListController` objects into sections. An optional closure can also be provided to transform the value into an appropriate section name: + + let listController = HardcoreData.observeSectionedList( + From(MyPersonEntity), + SectionedBy("age") { "Age \($0)" }, + OrderBy(.Ascending("lastName")) + ) +*/ public struct SectionedBy { // MARK: Public + /** + Initializes a `SectionedBy` clause with the key path to use to group `ManagedObjectListController` objects into sections + + :param: sectionKeyPath the key path to use to group the objects into sections + */ public init(_ sectionKeyPath: KeyPath) { self.init(sectionKeyPath, { $0 }) } + /** + Initializes a `SectionedBy` clause with the key path to use to group `ManagedObjectListController` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + :param: sectionKeyPath the key path to use to group the objects into sections + :param: sectionIndexTransformer a closure to transform the value for the key path to an appropriate section name + */ public init(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: (sectionName: String?) -> String?) { self.sectionKeyPath = sectionKeyPath self.sectionIndexTransformer = sectionIndexTransformer } + + // MARK: Internal + internal let sectionKeyPath: KeyPath internal let sectionIndexTransformer: (sectionName: KeyPath?) -> String? } @@ -52,34 +75,123 @@ public struct SectionedBy { // MARK: - ManagedObjectListController -public final class ManagedObjectListController: FetchedResultsControllerHandler { +/** +The `ManagedObjectListController` monitors changes to a list of `NSManagedObject` instances. Observers that implement the `ManagedObjectListChangeObserver` protocol may then register themselves to the `ManagedObjectListController`'s `addObserver(_:)` method: + + let listController = HardcoreData.observeObjectList( + From(MyPersonEntity), + Where("title", isEqualTo: "Engineer"), + OrderBy(.Ascending("lastName")) + ) + listController.addObserver(self) + +The `ManagedObjectListController` instance needs to be held on (retained) for as long as the list needs to be observed. +Observers registered via `addObserver(_:)` are not retained. `ManagedObjectListController` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles. + +Lists created with `observeObjectList(...)` keep a single-section list of objects, where each object can be accessed by index: + + let firstPerson: MyPersonEntity = listController[0] + +Accessing the list with an index above the valid range will throw an exception. + +Creating a sectioned-list is also possible with the `observeSectionedList(...)` method: + + let listController = HardcoreData.observeSectionedList( + From(MyPersonEntity), + SectionedBy("age") { "Age \($0)" }, + Where("title", isEqualTo: "Engineer"), + OrderBy(.Ascending("lastName")) + ) + listController.addObserver(self) + +Objects from `ManagedObjectListController`'s created this way can be accessed either by an `NSIndexPath` or a tuple: + + let indexPath = NSIndexPath(forItem: 3, inSection: 2) + let person1 = listController[indexPath] + let person2 = listController[2, 3] + +In the example above, both `person1` and `person2` will contain the object at section=2, index=3. +*/ +public final class ManagedObjectListController { // MARK: Public + /** + Accesses the object at the given index within the first section. This subscript indexer is typically used for `ManagedObjectListController`'s created with `addObserver(_:)`. + + :param: index the index of the object. Using an index above the valid range will throw an exception. + */ + public subscript(index: Int) -> T { + + return self.fetchedResultsController.objectAtIndexPath(NSIndexPath(forItem: index, inSection: 0)) as! T + } + + /** + Accesses the object at the given `NSIndexPath`. This subscript indexer is typically used for `ManagedObjectListController`'s created with `observeSectionedList(_:)`. + + :param: indexPath the `NSIndexPath` for the object. Using an `indexPath` with an invalid range will throw an exception. + */ public subscript(indexPath: NSIndexPath) -> T { return self.fetchedResultsController.objectAtIndexPath(indexPath) as! T } + /** + Accesses the object at the given `sectionIndex` and `itemIndex`. This subscript indexer is typically used for `ManagedObjectListController`'s created with `observeSectionedList(_:)`. + + :param: sectionIndex the section index for the object. Using a `sectionIndex` with an invalid range will throw an exception. + :param: itemIndex the index for the object within the section. Using an `itemIndex` with an invalid range will throw an exception. + */ + public subscript(sectionIndex: Int, itemIndex: Int) -> T { + + return self.fetchedResultsController.objectAtIndexPath(NSIndexPath(forItem: itemIndex, inSection: sectionIndex)) as! T + } + + /** + Returns the number of sections + */ public func numberOfSections() -> Int { return self.fetchedResultsController.sections?.count ?? 0 } + /** + Returns the number of objects in the specified section + + :param: section the section index + */ public func numberOfObjectsInSection(section: Int) -> Int { return (self.fetchedResultsController.sections?[section] as? NSFetchedResultsSectionInfo)?.numberOfObjects ?? 0 } + /** + Returns the `NSFetchedResultsSectionInfo` for the specified section + + :param: section the section index + */ public func sectionInfoAtIndex(section: Int) -> NSFetchedResultsSectionInfo { return self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo } + /** + Registers a `ManagedObjectListChangeObserver` to be notified when changes to the receiver's list occur. + + To prevent retain-cycles, `ManagedObjectListController` only keeps `weak` references to its observers. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + Calling `addObserver(_:)` multiple times on the same observer is safe, as `ManagedObjectListController` unregisters previous notifications to the observer before re-registering them. + + :param: observer a `ManagedObjectListChangeObserver` to send change notifications to + */ public func addObserver(observer: U) { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.") + self.removeObserver(observer) + self.registerChangeNotification( &NotificationKey.willChangeList, name: ManagedObjectListControllerWillChangeListNotification, @@ -106,10 +218,23 @@ public final class ManagedObjectListController: FetchedResul ) } + /** + Registers a `ManagedObjectListObjectObserver` to be notified when changes to the receiver's list occur. + + To prevent retain-cycles, `ManagedObjectListController` only keeps `weak` references to its observers. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + Calling `addObserver(_:)` multiple times on the same observer is safe, as `ManagedObjectListController` unregisters previous notifications to the observer before re-registering them. + + :param: observer a `ManagedObjectListObjectObserver` to send change notifications to + */ public func addObserver(observer: U) { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.") + self.removeObserver(observer) + self.registerChangeNotification( &NotificationKey.willChangeList, name: ManagedObjectListControllerWillChangeListNotification, @@ -202,10 +327,23 @@ public final class ManagedObjectListController: FetchedResul ) } + /** + Registers a `ManagedObjectListSectionObserver` to be notified when changes to the receiver's list occur. + + To prevent retain-cycles, `ManagedObjectListController` only keeps `weak` references to its observers. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + Calling `addObserver(_:)` multiple times on the same observer is safe, as `ManagedObjectListController` unregisters previous notifications to the observer before re-registering them. + + :param: observer a `ManagedObjectListSectionObserver` to send change notifications to + */ public func addObserver(observer: U) { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.") + self.removeObserver(observer) + self.registerChangeNotification( &NotificationKey.willChangeList, name: ManagedObjectListControllerWillChangeListNotification, @@ -331,6 +469,13 @@ public final class ManagedObjectListController: FetchedResul ) } + /** + Unregisters a `ManagedObjectListChangeObserver` from receiving notifications for changes to the receiver's list. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + :param: observer a `ManagedObjectListChangeObserver` to unregister notifications to + */ public func removeObserver(observer: U) { HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to remove a \(typeName(observer)) outside the main queue.") @@ -349,106 +494,6 @@ public final class ManagedObjectListController: FetchedResul } - // MARK: FetchedResultsControllerHandler - - private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { - - switch type { - - case .Insert: - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidInsertObjectNotification, - object: self, - userInfo: [ - UserInfoKeyObject: anObject, - UserInfoKeyNewIndexPath: newIndexPath! - ] - ) - - case .Delete: - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidDeleteObjectNotification, - object: self, - userInfo: [ - UserInfoKeyObject: anObject, - UserInfoKeyIndexPath: indexPath! - ] - ) - - case .Update: - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidUpdateObjectNotification, - object: self, - userInfo: [ - UserInfoKeyObject: anObject, - UserInfoKeyIndexPath: indexPath! - ] - ) - - case .Move: - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidMoveObjectNotification, - object: self, - userInfo: [ - UserInfoKeyObject: anObject, - UserInfoKeyIndexPath: indexPath!, - UserInfoKeyNewIndexPath: newIndexPath! - ] - ) - } - } - - private func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { - - switch type { - - case .Insert: - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidInsertSectionNotification, - object: self, - userInfo: [ - UserInfoKeySectionInfo: sectionInfo, - UserInfoKeySectionIndex: NSNumber(integer: sectionIndex) - ] - ) - - case .Delete: - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidDeleteSectionNotification, - object: self, - userInfo: [ - UserInfoKeySectionInfo: sectionInfo, - UserInfoKeySectionIndex: NSNumber(integer: sectionIndex) - ] - ) - - default: - break - } - } - - private func controllerWillChangeContent(controller: NSFetchedResultsController) { - - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerWillChangeListNotification, - object: self - ) - } - - private func controllerDidChangeContent(controller: NSFetchedResultsController) { - - NSNotificationCenter.defaultCenter().postNotificationName( - ManagedObjectListControllerDidChangeListNotification, - object: self - ) - } - - private func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? { - - return self.sectionIndexTransformer(sectionName: sectionName) - } - - // MARK: Internal internal init(dataStack: DataStack, entity: T.Type, sectionedBy: SectionedBy?, fetchClauses: [FetchClause]) { @@ -581,6 +626,111 @@ public final class ManagedObjectListController: FetchedResul } +// MARK: - ManagedObjectListController: FetchedResultsControllerHandler + +extension ManagedObjectListController: FetchedResultsControllerHandler { + + // MARK: FetchedResultsControllerHandler + + private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { + + switch type { + + case .Insert: + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidInsertObjectNotification, + object: self, + userInfo: [ + UserInfoKeyObject: anObject, + UserInfoKeyNewIndexPath: newIndexPath! + ] + ) + + case .Delete: + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidDeleteObjectNotification, + object: self, + userInfo: [ + UserInfoKeyObject: anObject, + UserInfoKeyIndexPath: indexPath! + ] + ) + + case .Update: + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidUpdateObjectNotification, + object: self, + userInfo: [ + UserInfoKeyObject: anObject, + UserInfoKeyIndexPath: indexPath! + ] + ) + + case .Move: + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidMoveObjectNotification, + object: self, + userInfo: [ + UserInfoKeyObject: anObject, + UserInfoKeyIndexPath: indexPath!, + UserInfoKeyNewIndexPath: newIndexPath! + ] + ) + } + } + + private func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { + + switch type { + + case .Insert: + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidInsertSectionNotification, + object: self, + userInfo: [ + UserInfoKeySectionInfo: sectionInfo, + UserInfoKeySectionIndex: NSNumber(integer: sectionIndex) + ] + ) + + case .Delete: + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidDeleteSectionNotification, + object: self, + userInfo: [ + UserInfoKeySectionInfo: sectionInfo, + UserInfoKeySectionIndex: NSNumber(integer: sectionIndex) + ] + ) + + default: + break + } + } + + private func controllerWillChangeContent(controller: NSFetchedResultsController) { + + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerWillChangeListNotification, + object: self + ) + } + + private func controllerDidChangeContent(controller: NSFetchedResultsController) { + + NSNotificationCenter.defaultCenter().postNotificationName( + ManagedObjectListControllerDidChangeListNotification, + object: self + ) + } + + private func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? { + + return self.sectionIndexTransformer(sectionName: sectionName) + } +} + + // MARK: - FetchedResultsControllerHandler private protocol FetchedResultsControllerHandler: class { diff --git a/HardcoreData/Observing/ManagedObjectListObserver.swift b/HardcoreData/Observing/ManagedObjectListObserver.swift index 02f704b..06064b5 100644 --- a/HardcoreData/Observing/ManagedObjectListObserver.swift +++ b/HardcoreData/Observing/ManagedObjectListObserver.swift @@ -29,42 +29,119 @@ import CoreData // MARK: - ManagedObjectListChangeObserver +/** +Implement the `ManagedObjectListChangeObserver` protocol to observe changes to a list of `NSManagedObject`'s. `ManagedObjectListChangeObserver`'s may register themselves to a `ManagedObjectListController`'s `addObserver(_:)` method: + + let listController = HardcoreData.observeObjectList( + From(MyPersonEntity), + OrderBy(.Ascending("lastName")) + ) + listController.addObserver(self) +*/ public protocol ManagedObjectListChangeObserver: class { + /** + The `NSManagedObject` type for the observed list + */ typealias EntityType: NSManagedObject + /** + Handles processing just before a change to the observed list occurs + + :param: listController the `ManagedObjectListController` monitoring the list being observed + */ func managedObjectListWillChange(listController: ManagedObjectListController) + /** + Handles processing right after a change to the observed list occurs + + :param: listController the `ManagedObjectListController` monitoring the object being observed + */ func managedObjectListDidChange(listController: ManagedObjectListController) } // MARK: - ManagedObjectListObjectObserver +/** +Implement the `ManagedObjectListObjectObserver` protocol to observe detailed changes to a list's object. `ManagedObjectListObjectObserver`'s may register themselves to a `ManagedObjectListController`'s `addObserver(_:)` method: + + let listController = HardcoreData.observeObjectList( + From(MyPersonEntity), + OrderBy(.Ascending("lastName")) + ) + listController.addObserver(self) +*/ public protocol ManagedObjectListObjectObserver: ManagedObjectListChangeObserver { + /** + Notifies that an object was inserted to the specified `NSIndexPath` in the list + + :param: listController the `ManagedObjectListController` monitoring the list being observed + :param: object the entity type for the inserted object + :param: indexPath the new `NSIndexPath` for the inserted object + */ func managedObjectList(listController: ManagedObjectListController, didInsertObject object: EntityType, toIndexPath indexPath: NSIndexPath) + /** + Notifies that an object was deleted from the specified `NSIndexPath` in the list + + :param: listController the `ManagedObjectListController` monitoring the list being observed + :param: object the entity type for the deleted object + :param: indexPath the `NSIndexPath` for the deleted object + */ func managedObjectList(listController: ManagedObjectListController, didDeleteObject object: EntityType, fromIndexPath indexPath: NSIndexPath) + /** + Notifies that an object at the specified `NSIndexPath` was updated + + :param: listController the `ManagedObjectListController` monitoring the list being observed + :param: object the entity type for the updated object + :param: indexPath the `NSIndexPath` for the updated object + */ func managedObjectList(listController: ManagedObjectListController, didUpdateObject object: EntityType, atIndexPath indexPath: NSIndexPath) + /** + Notifies that an object's index changed + + :param: listController the `ManagedObjectListController` monitoring the list being observed + :param: object the entity type for the moved object + :param: fromIndexPath the previous `NSIndexPath` for the moved object + :param: toIndexPath the new `NSIndexPath` for the moved object + */ func managedObjectList(listController: ManagedObjectListController, didMoveObject object: EntityType, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) - - -// -// private func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? { -// -// return nil -// } } // MARK: - ManagedObjectListSectionObserver +/** +Implement the `ManagedObjectListSectionObserver` protocol to observe changes to a list's section info. `ManagedObjectListSectionObserver`'s may register themselves to a `ManagedObjectListController`'s `addObserver(_:)` method: + + let listController = HardcoreData.observeSectionedList( + From(MyPersonEntity), + SectionedBy("age") { "Age \($0)" }, + OrderBy(.Ascending("lastName")) + ) + listController.addObserver(self) +*/ public protocol ManagedObjectListSectionObserver: ManagedObjectListObjectObserver { + /** + Notifies that a section was inserted at the specified index + + :param: listController the `ManagedObjectListController` monitoring the list being observed + :param: sectionInfo the `NSFetchedResultsSectionInfo` for the inserted section + :param: sectionIndex the new section index for the new section + */ func managedObjectList(listController: ManagedObjectListController, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) - + + /** + Notifies that a section was inserted at the specified index + + :param: listController the `ManagedObjectListController` monitoring the list being observed + :param: sectionInfo the `NSFetchedResultsSectionInfo` for the deleted section + :param: sectionIndex the previous section index for the deleted section + */ func managedObjectList(listController: ManagedObjectListController, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) } diff --git a/HardcoreData/Observing/ManagedObjectObserver.swift b/HardcoreData/Observing/ManagedObjectObserver.swift index 479bbdd..6f235d2 100644 --- a/HardcoreData/Observing/ManagedObjectObserver.swift +++ b/HardcoreData/Observing/ManagedObjectObserver.swift @@ -29,13 +29,41 @@ import CoreData // MARK: - ManagedObjectObserver +/** +Implement the `ManagedObjectObserver` protocol to observe changes to a single `NSManagedObject` instance. `ManagedObjectObserver`'s may register themselves to a `ManagedObjectController`'s `addObserver(_:)` method: + + let objectController = HardcoreData.observeObject(object) + objectController.addObserver(self) +*/ public protocol ManagedObjectObserver: class { + /** + The `NSManagedObject` type for the observed object + */ typealias EntityType: NSManagedObject + /** + Handles processing just before a change to the observed `object` occurs + + :param: objectController the `ManagedObjectController` monitoring the object being observed + :param: object the `NSManagedObject` instance being observed + */ func managedObjectWillUpdate(objectController: ManagedObjectController, object: EntityType) + /** + Handles processing right after a change to the observed `object` occurs + + :param: objectController the `ManagedObjectController` monitoring the object being observed + :param: object the `NSManagedObject` instance being observed + :param: changedPersistentKeys a `Set` of key paths for the attributes that were changed. Note that `changedPersistentKeys` only contains keys for attributes/relationships present in the persistent store, thus transient properties will not be reported. + */ func managedObjectWasUpdated(objectController: ManagedObjectController, object: EntityType, changedPersistentKeys: Set) + /** + Handles processing right after `object` is deleted + + :param: objectController the `ManagedObjectController` monitoring the object being observed + :param: object the `NSManagedObject` instance being observed + */ func managedObjectWasDeleted(objectController: ManagedObjectController, object: EntityType) } diff --git a/HardcoreData/Saving and Processing/AsynchronousDataTransaction.swift b/HardcoreData/Saving and Processing/AsynchronousDataTransaction.swift index 2d1a86e..5ffa565 100644 --- a/HardcoreData/Saving and Processing/AsynchronousDataTransaction.swift +++ b/HardcoreData/Saving and Processing/AsynchronousDataTransaction.swift @@ -31,16 +31,16 @@ import GCDKit // MARK: - AsynchronousDataTransaction /** -The AsynchronousDataTransaction provides an interface for NSManagedObject creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from DataStack.beginAsynchronous(_:), or from HardcoreData.beginAsynchronous(_:). +The `AsynchronousDataTransaction` provides an interface for `NSManagedObject` creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from `DataStack.beginAsynchronous(_:)`, or from `HardcoreData.beginAsynchronous(_:)`. */ public final class AsynchronousDataTransaction: BaseDataTransaction { // MARK: Public /** - Saves the transaction changes asynchronously. This method should not be used after the commit() method was already called once. + Saves the transaction changes asynchronously. This method should not be used after the `commit()` method was already called once. - :param: completion the block executed after the save completes. Success or failure is reported by the SaveResult argument of the block. + :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) -> Void) { @@ -59,9 +59,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { } /** - Saves the transaction changes and waits for completion synchronously. This method should not be used after the commit() method was already called once. - - :returns: a SaveResult value indicating success or failure. + Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` method was already called once. */ public func commit() { @@ -73,10 +71,10 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { } /** - Begins a child transaction synchronously where NSManagedObject creates, updates, and deletes can be made. This method should not be used after he commit() method was already called once. + Begins a child transaction synchronously where NSManagedObject creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once. - :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 + :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 beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? { @@ -93,10 +91,10 @@ 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. This method should not be used after the `commit()` method was already called once. - :param: entity the NSManagedObject type to be created - :returns: a new NSManagedObject instance of the specified entity type. + :param: entity the `NSManagedObject` type to be created + :returns: a new `NSManagedObject` instance of the specified entity type. */ public override func create(entity: T.Type) -> T { @@ -106,10 +104,10 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { } /** - Returns an editable proxy of a specified NSManagedObject. This method should not be used after the commit() method was already called once. + Returns an editable proxy of a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once. - :param: object the NSManagedObject type to be edited - :returns: an editable proxy for the specified NSManagedObject. + :param: object the `NSManagedObject` type to be edited + :returns: an editable proxy for the specified `NSManagedObject`. */ public override func fetch(object: T?) -> T? { @@ -119,9 +117,9 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { } /** - Deletes a specified NSManagedObject. This method should not be used after the commit() method was already called once. + Deletes a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once. - :param: object the NSManagedObject type to be deleted + :param: object the `NSManagedObject` type to be deleted */ public override func delete(object: NSManagedObject?) { @@ -131,7 +129,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { } /** - Rolls back the transaction by resetting the NSManagedObjectContext. After calling this method, all NSManagedObjects fetched within the transaction will become invalid. This method should not be used after the commit() method was already called once. + Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. This method should not be used after the `commit()` method was already called once. */ public override func rollback() { diff --git a/HardcoreData/Saving and Processing/BaseDataTransaction.swift b/HardcoreData/Saving and Processing/BaseDataTransaction.swift index c06ca45..1557973 100644 --- a/HardcoreData/Saving and Processing/BaseDataTransaction.swift +++ b/HardcoreData/Saving and Processing/BaseDataTransaction.swift @@ -31,22 +31,25 @@ import GCDKit // MARK: - BaseDataTransaction /** -The BaseDataTransaction is an abstract interface for NSManagedObject creates, updates, and deletes. All BaseDataTransaction subclasses manage a private NSManagedObjectContext which are direct children of the NSPersistentStoreCoordinator's root NSManagedObjectContext. This means that all updates are saved first to the persistent store, and then propagated up to the read-only NSManagedObjectContext. +The `BaseDataTransaction` is an abstract interface for `NSManagedObject` creates, updates, and deletes. All `BaseDataTransaction` subclasses manage a private `NSManagedObjectContext` which are direct children of the `NSPersistentStoreCoordinator`'s root `NSManagedObjectContext`. This means that all updates are saved first to the persistent store, and then propagated up to the read-only `NSManagedObjectContext`. */ public /*abstract*/ class BaseDataTransaction { // MARK: Object management - var hasChanges: Bool { + /** + Indicates if the transaction has pending changes + */ + public var hasChanges: Bool { return self.context.hasChanges } /** - Creates a new NSManagedObject with the specified entity type. + Creates a new `NSManagedObject` with the specified entity type. - :param: entity the NSManagedObject type to be created - :returns: a new NSManagedObject instance of the specified entity type. + :param: entity the `NSManagedObject` type to be created + :returns: a new `NSManagedObject` instance of the specified entity type. */ public func create(entity: T.Type) -> T { @@ -56,10 +59,10 @@ public /*abstract*/ class BaseDataTransaction { } /** - Returns an editable proxy of a specified NSManagedObject. + Returns an editable proxy of a specified `NSManagedObject`. - :param: object the NSManagedObject type to be edited - :returns: an editable proxy for the specified NSManagedObject. + :param: object the `NSManagedObject` type to be edited + :returns: an editable proxy for the specified `NSManagedObject`. */ public func fetch(object: T?) -> T? { @@ -69,9 +72,9 @@ public /*abstract*/ class BaseDataTransaction { } /** - Deletes a specified NSManagedObject. + Deletes a specified `NSManagedObject`. - :param: object the NSManagedObject type to be deleted + :param: object the `NSManagedObject` type to be deleted */ public func delete(object: NSManagedObject?) { @@ -83,7 +86,7 @@ public /*abstract*/ class BaseDataTransaction { // MARK: Saving changes /** - Rolls back the transaction by resetting the NSManagedObjectContext. After calling this method, all NSManagedObjects fetched within the transaction will become invalid. + Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. */ public func rollback() { diff --git a/HardcoreData/Saving and Processing/DataStack+Transaction.swift b/HardcoreData/Saving and Processing/DataStack+Transaction.swift index c038d31..7d3638a 100644 --- a/HardcoreData/Saving and Processing/DataStack+Transaction.swift +++ b/HardcoreData/Saving and Processing/DataStack+Transaction.swift @@ -35,9 +35,9 @@ public extension DataStack { // MARK: Public /** - Begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made. + 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. + :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 beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) { @@ -50,10 +50,10 @@ public extension DataStack { } /** - Begins a transaction synchronously where NSManagedObject creates, updates, and deletes can be made. + 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 + :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 beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? { @@ -66,9 +66,9 @@ public extension DataStack { } /** - Begins a non-contiguous transaction where NSManagedObject creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. + Begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. - :returns: a DetachedDataTransaction instance where creates, updates, and deletes can be made. + :returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made. */ public func beginDetached() -> DetachedDataTransaction { diff --git a/HardcoreData/Saving and Processing/DetachedDataTransaction.swift b/HardcoreData/Saving and Processing/DetachedDataTransaction.swift index bcbe91a..d490881 100644 --- a/HardcoreData/Saving and Processing/DetachedDataTransaction.swift +++ b/HardcoreData/Saving and Processing/DetachedDataTransaction.swift @@ -30,16 +30,16 @@ import GCDKit // MARK: - DetachedDataTransaction /** -The DetachedDataTransaction provides an interface for non-contiguous NSManagedObject creates, updates, and deletes. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. +The `DetachedDataTransaction` provides an interface for non-contiguous `NSManagedObject` creates, updates, and deletes. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. */ public final class DetachedDataTransaction: BaseDataTransaction { // MARK: Public /** - Saves the transaction changes asynchronously. For a DetachedDataTransaction, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread. + Saves the transaction changes asynchronously. For a `DetachedDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread. - :param: completion the block executed after the save completes. Success or failure is reported by the SaveResult argument of the block. + :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) -> Void) { diff --git a/HardcoreData/Saving and Processing/HardcoreData+Transaction.swift b/HardcoreData/Saving and Processing/HardcoreData+Transaction.swift index 754bd53..79757ea 100644 --- a/HardcoreData/Saving and Processing/HardcoreData+Transaction.swift +++ b/HardcoreData/Saving and Processing/HardcoreData+Transaction.swift @@ -33,9 +33,9 @@ public extension HardcoreData { // MARK: Public /** - Using the defaultStack, begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made. + Using the `defaultStack`, 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. + :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 beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) { @@ -43,10 +43,10 @@ public extension HardcoreData { } /** - Using the defaultStack, begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made. + Using the `defaultStack`, 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. - :returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously + :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 beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? { @@ -54,9 +54,9 @@ public extension HardcoreData { } /** - Using the defaultStack, begins a non-contiguous transaction where NSManagedObject creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. + Using the `defaultStack`, begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. - :returns: a DetachedDataTransaction instance where creates, updates, and deletes can be made. + :returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made. */ public static func beginDetached() -> DetachedDataTransaction { diff --git a/HardcoreData/Saving and Processing/SaveResult.swift b/HardcoreData/Saving and Processing/SaveResult.swift index 8b26d22..20cd032 100644 --- a/HardcoreData/Saving and Processing/SaveResult.swift +++ b/HardcoreData/Saving and Processing/SaveResult.swift @@ -28,11 +28,52 @@ import Foundation // MARK: - SaveResult +/** +The `SaveResult` indicates the result of a `commit(...)` for a transaction. +The `SaveResult` can be treated as a boolean: + + HardcoreData.beginAsynchronous { transaction in + // ... + let result = transaction.commit() + if result { + // succeeded + } + else { + // failed + } + } + +or as an `enum`, where the resulting associated object can also be inspected: + + HardcoreData.beginAsynchronous { transaction in + // ... + let result = transaction.commit() + switch result { + case .Success(let hasChanges): + // hasChanges indicates if there were changes or not + case .Failure(let error): + // error is the NSError instance for the failure + } + } +``` +*/ public enum SaveResult { + // MARK: Public + + /** + `SaveResult.Success` indicates that the `commit()` for the transaction succeeded, either because the save succeeded or because there were no changes to save. The associated value `hasChanges` indicates if there were saved changes or not. + */ case Success(hasChanges: Bool) + + /** + `SaveResult.Failure` indicates that the `commit()` for the transaction failed. The associated object for this value is the related `NSError` instance. + */ case Failure(NSError) + + // MARK: Internal + internal init(hasChanges: Bool) { self = .Success(hasChanges: hasChanges) diff --git a/HardcoreData/Saving and Processing/SynchronousDataTransaction.swift b/HardcoreData/Saving and Processing/SynchronousDataTransaction.swift index fb8c170..e04cf57 100644 --- a/HardcoreData/Saving and Processing/SynchronousDataTransaction.swift +++ b/HardcoreData/Saving and Processing/SynchronousDataTransaction.swift @@ -31,16 +31,14 @@ import GCDKit // MARK: - SynchronousDataTransaction /** -The SynchronousDataTransaction provides an interface for NSManagedObject creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from DataStack.beginSynchronous(_:), or from HardcoreData.beginSynchronous(_:). +The `SynchronousDataTransaction` provides an interface for `NSManagedObject` creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from `DataStack.beginSynchronous(_:)`, or from `HardcoreData.beginSynchronous(_:)`. */ public final class SynchronousDataTransaction: BaseDataTransaction { // MARK: Public /** - Saves the transaction changes and waits for completion synchronously. This method should not be used after the commit() method was already called once. - - :returns: a SaveResult value indicating success or failure. + Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` method was already called once. */ public func commit() { @@ -52,10 +50,10 @@ public final class SynchronousDataTransaction: BaseDataTransaction { } /** - Begins a child transaction synchronously where NSManagedObject creates, updates, and deletes can be made. This method should not be used after the commit() method was already called once. + Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once. - :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 + :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 beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? { @@ -72,10 +70,10 @@ 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. This method should not be used after the `commit()` method was already called once. - :param: entity the NSManagedObject type to be created - :returns: a new NSManagedObject instance of the specified entity type. + :param: entity the `NSManagedObject` type to be created + :returns: a new `NSManagedObject` instance of the specified entity type. */ public override func create(entity: T.Type) -> T { @@ -85,10 +83,10 @@ public final class SynchronousDataTransaction: BaseDataTransaction { } /** - Returns an editable proxy of a specified NSManagedObject. This method should not be used after the commit() method was already called once. + Returns an editable proxy of a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once. - :param: object the NSManagedObject type to be edited - :returns: an editable proxy for the specified NSManagedObject. + :param: object the `NSManagedObject` type to be edited + :returns: an editable proxy for the specified `NSManagedObject`. */ public override func fetch(object: T?) -> T? { @@ -98,9 +96,9 @@ public final class SynchronousDataTransaction: BaseDataTransaction { } /** - Deletes a specified NSManagedObject. This method should not be used after the commit() method was already called once. + Deletes a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once. - :param: object the NSManagedObject type to be deleted + :param: object the `NSManagedObject` type to be deleted */ public override func delete(object: NSManagedObject?) { @@ -110,7 +108,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { } /** - Rolls back the transaction by resetting the NSManagedObjectContext. After calling this method, all NSManagedObjects fetched within the transaction will become invalid. This method should not be used after the commit() method was already called once. + Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. This method should not be used after the `commit()` method was already called once. */ public override func rollback() { diff --git a/HardcoreData/Setting Up/DataStack.swift b/HardcoreData/Setting Up/DataStack.swift index b054766..3841853 100644 --- a/HardcoreData/Setting Up/DataStack.swift +++ b/HardcoreData/Setting Up/DataStack.swift @@ -32,20 +32,20 @@ private let applicationSupportDirectory = NSFileManager.defaultManager().URLsFor private let applicationName = ((NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData") -private let defaultSQLiteStoreURL = applicationSupportDirectory.URLByAppendingPathComponent(applicationName, isDirectory: false).URLByAppendingPathExtension("sqlite") +internal let defaultSQLiteStoreURL = applicationSupportDirectory.URLByAppendingPathComponent(applicationName, isDirectory: false).URLByAppendingPathExtension("sqlite") // 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 designed as a read-only model interface for NSManagedObjects. +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` designed as a read-only model interface for `NSManagedObjects`. */ public final class DataStack { // MARK: Public /** - Initializes a DataStack from a model created by merging all the models found in all bundles. + Initializes a `DataStack` from a model created by merging all the models found in all bundles. */ public convenience init() { @@ -56,7 +56,7 @@ public final class DataStack { } /** - Initializes a DataStack from the specified model name. + Initializes a `DataStack` from the specified model name. :param: modelName the name of the (.xcdatamodeld) model file. */ @@ -72,9 +72,9 @@ public final class DataStack { } /** - Initializes a DataStack from an NSManagedObjectModel. + Initializes a `DataStack` from an `NSManagedObjectModel`. - :param: managedObjectModel the NSManagedObjectModel of the (.xcdatamodeld) model file. + :param: managedObjectModel the `NSManagedObjectModel` of the (.xcdatamodeld) model file. */ public required init(managedObjectModel: NSManagedObjectModel) { @@ -97,7 +97,7 @@ public final class DataStack { Adds an in-memory store to the stack. :param: configuration an optional configuration name from the model file. If not specified, defaults to nil. - :returns: a PersistentStoreResult indicating success or failure. + :returns: a `PersistentStoreResult` indicating success or failure. */ public func addInMemoryStore(configuration: String? = nil) -> PersistentStoreResult { @@ -143,7 +143,7 @@ public final class DataStack { :param: configuration an optional configuration name from the model file. If not specified, defaults to nil. :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. + :returns: a `PersistentStoreResult` indicating success or failure. */ public func addSQLiteStore(fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult { @@ -165,7 +165,7 @@ public final class DataStack { :param: configuration an optional configuration name from the model file. If not specified, defaults to nil. :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. + :returns: a `PersistentStoreResult` indicating success or failure. */ public func addSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult { diff --git a/HardcoreData/Setting Up/HardcoreData+Setup.swift b/HardcoreData/Setting Up/HardcoreData+Setup.swift new file mode 100644 index 0000000..817f31f --- /dev/null +++ b/HardcoreData/Setting Up/HardcoreData+Setup.swift @@ -0,0 +1,84 @@ +// +// HardcoreData+Setup.swift +// HardcoreData +// +// Copyright (c) 2015 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 +import GCDKit + + +// MARK: - HardcoreData + +public extension HardcoreData { + + /** + Adds an in-memory store to the `defaultStack`. + + :param: configuration an optional configuration name from the model file. If not specified, defaults to nil. + :returns: a `PersistentStoreResult` indicating success or failure. + */ + public static func addInMemoryStore(configuration: String? = nil) -> PersistentStoreResult { + + return self.defaultStack.addInMemoryStore(configuration: configuration) + } + + /** + Adds to the `defaultStack` 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. + :param: configuration an optional configuration name from the model file. If not specified, defaults to nil. + :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. + */ + public static func addSQLiteStore(fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult { + + return self.defaultStack.addSQLiteStore( + fileName, + configuration: configuration, + automigrating: automigrating, + resetStoreOnMigrationFailure: resetStoreOnMigrationFailure + ) + } + + /** + Adds to the `defaultStack` 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 ".sqlite" file in the "Application Support" directory. + :param: configuration an optional configuration name from the model file. If not specified, defaults to nil. + :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. + */ + public static func addSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult { + + return self.defaultStack.addSQLiteStore( + fileURL: fileURL, + configuration: configuration, + automigrating: automigrating, + resetStoreOnMigrationFailure: resetStoreOnMigrationFailure + ) + } + +} \ No newline at end of file diff --git a/HardcoreData/Setting Up/PersistentStoreResult.swift b/HardcoreData/Setting Up/PersistentStoreResult.swift index c4ecfe2..4f08531 100644 --- a/HardcoreData/Setting Up/PersistentStoreResult.swift +++ b/HardcoreData/Setting Up/PersistentStoreResult.swift @@ -29,11 +29,41 @@ import CoreData // MARK: - PersistentStoreResult +/** +The `PersistentStoreResult` indicates the result of initializing the persistent store. +The `PersistentStoreResult` can be treated as a boolean: + + let result = HardcoreData.addSQLiteStore() + if result { + // succeeded + } + else { + // failed + } + +or as an `enum`, where the resulting associated object can also be inspected: + + let result = HardcoreData.addSQLiteStore() + switch result { + case .Success(let persistentStore): + // persistentStore is the related NSPersistentStore instance + case .Failure(let error): + // error is the NSError instance for the failure + } +``` +*/ public enum PersistentStoreResult { // MARK: Public + /** + `PersistentStoreResult.Success` indicates that the persistent store process succeeded. The associated object for this `enum` value is the related `NSPersistentStore` instance. + */ case Success(NSPersistentStore) + + /** + `PersistentStoreResult.Failure` indicates that the persistent store process failed. The associated object for this value is the related `NSError` instance. + */ case Failure(NSError) diff --git a/HardcoreDataDemo/HardcoreDataDemo.xcodeproj/project.xcworkspace/xcshareddata/HardcoreDataDemo.xccheckout b/HardcoreDataDemo/HardcoreDataDemo.xcodeproj/project.xcworkspace/xcshareddata/HardcoreDataDemo.xccheckout index d6128a7..202d445 100644 --- a/HardcoreDataDemo/HardcoreDataDemo.xcodeproj/project.xcworkspace/xcshareddata/HardcoreDataDemo.xccheckout +++ b/HardcoreDataDemo/HardcoreDataDemo.xcodeproj/project.xcworkspace/xcshareddata/HardcoreDataDemo.xccheckout @@ -7,7 +7,7 @@ IDESourceControlProjectIdentifier 7C5E31AC-5DD0-43DA-A5C6-AF73B4532D86 IDESourceControlProjectName - HardcoreDataDemo + project IDESourceControlProjectOriginsDictionary 4B60F1BCB491FF717C56441AE7783C74F417BE48 @@ -16,7 +16,7 @@ github.com:JohnEstropia/GCDKit.git IDESourceControlProjectPath - HardcoreDataDemo/HardcoreDataDemo.xcodeproj + HardcoreDataDemo/HardcoreDataDemo.xcodeproj/project.xcworkspace IDESourceControlProjectRelativeInstallPathDictionary 4B60F1BCB491FF717C56441AE7783C74F417BE48 diff --git a/HardcoreDataDemo/HardcoreDataDemo/AppDelegate.swift b/HardcoreDataDemo/HardcoreDataDemo/AppDelegate.swift index 7938abb..7a314ec 100644 --- a/HardcoreDataDemo/HardcoreDataDemo/AppDelegate.swift +++ b/HardcoreDataDemo/HardcoreDataDemo/AppDelegate.swift @@ -21,7 +21,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - HardcoreData.defaultStack.addSQLiteStore(resetStoreOnMigrationFailure: true) + HardcoreData.addSQLiteStore(resetStoreOnMigrationFailure: true) return true } } diff --git a/HardcoreDataDemo/HardcoreDataDemo/List and Object Observers Demo/ObjectListObserverDemoViewController.swift b/HardcoreDataDemo/HardcoreDataDemo/List and Object Observers Demo/ObjectListObserverDemoViewController.swift index 5da27c5..819a923 100644 --- a/HardcoreDataDemo/HardcoreDataDemo/List and Object Observers Demo/ObjectListObserverDemoViewController.swift +++ b/HardcoreDataDemo/HardcoreDataDemo/List and Object Observers Demo/ObjectListObserverDemoViewController.swift @@ -15,7 +15,7 @@ struct Shared { static let palettes = HardcoreData.observeSectionedList( From(Palette), SectionedBy("colorName"), - SortedBy(.Ascending("hue")) + OrderBy(.Ascending("hue")) ) } diff --git a/HardcoreDataDemo/HardcoreDataDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift b/HardcoreDataDemo/HardcoreDataDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift index 7497c37..edf1ce9 100644 --- a/HardcoreDataDemo/HardcoreDataDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift +++ b/HardcoreDataDemo/HardcoreDataDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift @@ -45,7 +45,7 @@ class ObjectObserverDemoViewController: UIViewController, ManagedObjectObserver required init(coder aDecoder: NSCoder) { - if let palette = HardcoreData.fetchOne(From(Palette), SortedBy(.Ascending("hue"))) { + if let palette = HardcoreData.fetchOne(From(Palette), OrderBy(.Ascending("hue"))) { self.objectController = HardcoreData.observeObject(palette) } @@ -59,7 +59,7 @@ class ObjectObserverDemoViewController: UIViewController, ManagedObjectObserver transaction.commit() } - let palette = HardcoreData.fetchOne(From(Palette), SortedBy(.Ascending("hue")))! + let palette = HardcoreData.fetchOne(From(Palette), OrderBy(.Ascending("hue")))! self.objectController = HardcoreData.observeObject(palette) } diff --git a/HardcoreDataTests/HardcoreDataTests.swift b/HardcoreDataTests/HardcoreDataTests.swift index c8c0a75..284ab56 100644 --- a/HardcoreDataTests/HardcoreDataTests.swift +++ b/HardcoreDataTests/HardcoreDataTests.swift @@ -112,7 +112,7 @@ class HardcoreDataTests: XCTestCase { let objs4test = transaction.fetchOne( From(TestEntity2), Where("testEntityID", isEqualTo: 4), - CustomizeFetch { (fetchRequest) -> Void in + Tweak { (fetchRequest) -> Void in fetchRequest.includesPendingChanges = true } @@ -122,7 +122,7 @@ class HardcoreDataTests: XCTestCase { let objs5test = transaction.fetchOne( From(TestEntity2), Where("testEntityID", isEqualTo: 4), - CustomizeFetch { (fetchRequest) -> Void in + Tweak { (fetchRequest) -> Void in fetchRequest.includesPendingChanges = false } @@ -137,7 +137,7 @@ class HardcoreDataTests: XCTestCase { let objs4test = HardcoreData.fetchOne( From(TestEntity2), Where("testEntityID", isEqualTo: 4), - CustomizeFetch { (fetchRequest) -> Void in + Tweak { (fetchRequest) -> Void in fetchRequest.includesPendingChanges = false } @@ -169,8 +169,8 @@ class HardcoreDataTests: XCTestCase { let objs2 = transaction.fetchAll( From(TestEntity2), Where("testNumber", isEqualTo: 100) || Where("%K == %@", "testNumber", 90), - SortedBy(.Ascending("testEntityID"), .Descending("testString")), - CustomizeFetch { (fetchRequest) -> Void in + OrderBy(.Ascending("testEntityID"), .Descending("testString")), + Tweak { (fetchRequest) -> Void in fetchRequest.includesPendingChanges = true } diff --git a/README.md b/README.md index 7b43fa7..2c96ad8 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,13 @@ Simple, elegant, and smart Core Data programming with Swift ## Features -- Supports multiple persistent stores per *data stack*, just the way .xcdatamodeld files are supposed to. HardcoreData will also manage one *data stack* by default, but you can create and manage as many as you need. (see "Setting up") -- Ability to plug-in your own logging framework (or your favorite 3rd-party logger). (see "Logging and error handling") -- Makes it hard to fall into common concurrency mistakes. All Core Data tasks are encapsulated into safer, higher-level abstractions without sacrificing flexibility and customizability. (see "Saving and processing transactions") -- Provides convenient API for common use cases. (see "Fetching and querying") -- Pleasant API designed around Swift’s code elegance and type safety. (see "TL;DR sample codes") +- Supports multiple persistent stores per *data stack*, just the way .xcdatamodeld files are supposed to. HardcoreData will also manage one *data stack* by default, but you can create and manage as many as you need. +- Ability to plug-in your own logging framework (or any of your favorite 3rd-party logger) +- Gets around a limitation with other Core Data wrappers where the entity name should be the same as the `NSManagedObject` subclass name. HardcoreData loads entity-to-class mappings from the .xcdatamodeld file, so you are free to name them independently. +- Observe a list of `NSManagedObject`'s using `ManagedObjectListController`, a clean wrapper for `NSFetchedResultsController`. Another controller, `ManagedObjectController`, lets you observe changes for a single object without using KVO. Both controllers can have multiple observers as well, so there is no extra overhead when sharing the same data source for multiple screens. +- Makes it hard to fall into common concurrency mistakes. All `NSManagedObjectContext` tasks are encapsulated into safer, higher-level abstractions without sacrificing flexibility and customizability. +- Provides convenient API for common use cases. +- Clean API designed around Swift’s code elegance and type safety. #### TL;DR sample codes @@ -43,8 +45,8 @@ let objects = HardcoreData.fetchAll(From(MyEntity)) let objects = HardcoreData.fetchAll( From(MyEntity), Where("entityID", isEqualTo: 1), - SortedBy(.Ascending("entityID"), .Descending("name")), - CustomizeFetch { (fetchRequest) -> Void in + OrderBy(.Ascending("entityID"), .Descending("name")), + Tweak { (fetchRequest) -> Void in fetchRequest.includesPendingChanges = true } ) @@ -119,7 +121,7 @@ case .Failure(let error): // error is an NSError instance HardcoreData.defaultStack = dataStack // pass the dataStack to HardcoreData for easier access later on ``` -Note that you dont need to do the `HardcoreData.defaultStack = dataStack` line. You can just as well hold a stack like this and call all methods directly from the `DataStack` instance: +Note that you dont need to do the `HardcoreData.defaultStack = dataStack` line. You can just as well hold a stack like below and call all methods directly from the `DataStack` instance: ```swift class MyViewController: UIViewController { let dataStack = DataStack(modelName: "MyModel") @@ -139,7 +141,7 @@ The difference is when you set the stack as the `HardcoreData.defaultStack`, you class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - HardcoreData.dataStack.addSQLiteStore() + HardcoreData.addSQLiteStore() } func methodToBeCalledLaterOn() { let objects = HardcoreData.fetchAll(From(MyEntity))