From 6e01a58c858243b382cb94a87c65c48522929da7 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Fri, 9 Jun 2017 11:25:28 +0900 Subject: [PATCH 01/56] Swift 4 support --- CoreStore.xcodeproj/project.pbxproj | 52 ++++++++++++------- .../CoreStoreDemo.xcodeproj/project.pbxproj | 10 ++-- .../MigrationsDemoViewController.swift | 7 +-- CoreStoreTests/DynamicModelTests.swift | 4 +- CoreStoreTests/FetchTests.swift | 4 +- CoreStoreTests/ImportTests.swift | 4 +- CoreStoreTests/ListObserverTests.swift | 28 +++++----- CoreStoreTests/ObjectObserverTests.swift | 6 +-- CoreStoreTests/TransactionTests.swift | 12 ++--- Sources/AsynchronousDataTransaction.swift | 17 +++--- Sources/CSAsynchronousDataTransaction.swift | 11 ++-- Sources/CSSelect.swift | 8 +++ Sources/CoreStoreSchema.swift | 2 +- Sources/DataStack+Migration.swift | 3 +- Sources/MigrationChain.swift | 7 +-- .../NSManagedObjectContext+Transaction.swift | 2 +- Sources/QueryableAttributeType.swift | 8 +++ 17 files changed, 116 insertions(+), 69 deletions(-) diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 0801a46..b49ea3b 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -1667,27 +1667,27 @@ TargetAttributes = { 2F03A52F19C5C6DA005002A5 = { CreatedOnToolsVersion = 6.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; 2F03A53A19C5C6DA005002A5 = { CreatedOnToolsVersion = 6.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; 82BA18881C4BBCBA00A0916E = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; 82BA18911C4BBCBA00A0916E = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; B52DD1731BE1F8CC00949AFE = { CreatedOnToolsVersion = 7.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; B52DD17C1BE1F8CC00949AFE = { CreatedOnToolsVersion = 7.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; B563216E1BD65082006C9394 = { CreatedOnToolsVersion = 7.0.1; @@ -2550,6 +2550,7 @@ PRODUCT_NAME = CoreStore; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -2602,6 +2603,7 @@ PRODUCT_NAME = CoreStore; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; @@ -2625,7 +2627,8 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -2643,7 +2646,8 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -2663,7 +2667,8 @@ SDKROOT = iphoneos; SWIFT_OBJC_BRIDGING_HEADER = "CoreStoreTests/CoreStoreTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -2678,7 +2683,8 @@ PRODUCT_NAME = CoreStoreTests; SDKROOT = iphoneos; SWIFT_OBJC_BRIDGING_HEADER = "CoreStoreTests/CoreStoreTests-Bridging-Header.h"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -2698,7 +2704,8 @@ SDKROOT = appletvos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; }; name = Debug; @@ -2719,7 +2726,8 @@ SDKROOT = appletvos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; }; name = Release; @@ -2737,7 +2745,8 @@ SDKROOT = appletvos; SWIFT_OBJC_BRIDGING_HEADER = "CoreStoreTests/CoreStoreTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; }; name = Debug; @@ -2755,7 +2764,8 @@ PRODUCT_NAME = CoreStoreTests; SDKROOT = appletvos; SWIFT_OBJC_BRIDGING_HEADER = "CoreStoreTests/CoreStoreTests-Bridging-Header.h"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; }; name = Release; @@ -2779,7 +2789,8 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -2803,7 +2814,8 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -2823,7 +2835,8 @@ SDKROOT = macosx; SWIFT_OBJC_BRIDGING_HEADER = "CoreStoreTests/CoreStoreTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -2843,7 +2856,8 @@ PRODUCT_NAME = CoreStoreTests; SDKROOT = macosx; SWIFT_OBJC_BRIDGING_HEADER = "CoreStoreTests/CoreStoreTests-Bridging-Header.h"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -2864,6 +2878,7 @@ SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; }; @@ -2887,6 +2902,7 @@ SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; }; diff --git a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj index 3417a4c..c92f2aa 100644 --- a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj +++ b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj @@ -270,7 +270,7 @@ TargetAttributes = { B54AAD481AF4D26E00848AE0 = { CreatedOnToolsVersion = 6.3; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; }; }; @@ -407,6 +407,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; }; name = Debug; }; @@ -445,6 +446,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; VALIDATE_PRODUCT = YES; }; name = Release; @@ -458,7 +460,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.corestore.demo; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -472,7 +475,8 @@ PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.corestore.demo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift index 292cae1..c4329f2 100644 --- a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift @@ -109,7 +109,7 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD let dna = (self.listMonitor?[indexPath] as? OrganismProtocol)?.dna.description ?? "" cell.dnaLabel?.text = "DNA: \(dna)" - cell.mutateButtonHandler = { [weak self] _ -> Void in + cell.mutateButtonHandler = { [weak self] () -> Void in guard let `self` = self, let dataStack = self.dataStack, @@ -361,9 +361,10 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD self.segmentedControl?.selectedSegmentIndex = self.models .index( - where: { (_, _, schemaHistory) -> Bool in + where: { (arg) -> Bool in - schemaHistory.currentModelVersion == model.schemaHistory.currentModelVersion + let (_, _, schemaHistory) = arg + return schemaHistory.currentModelVersion == model.schemaHistory.currentModelVersion } )! diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index b63c708..10a1c99 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -137,7 +137,7 @@ class DynamicModelTests: BaseTestDataTestCase { XCTAssertEqual(dog.master.value, person) XCTAssertEqual(dog.master.value?.pets.value.first, dog) }, - success: { + success: { _ in updateDone.fulfill() }, @@ -171,7 +171,7 @@ class DynamicModelTests: BaseTestDataTestCase { let p3 = Dog.where({ $0.age == 10 }) XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10)) }, - success: { + success: { _ in fetchDone.fulfill() }, diff --git a/CoreStoreTests/FetchTests.swift b/CoreStoreTests/FetchTests.swift index 92c1b5e..a700184 100644 --- a/CoreStoreTests/FetchTests.swift +++ b/CoreStoreTests/FetchTests.swift @@ -115,7 +115,7 @@ final class FetchTests: BaseTestDataTestCase { try transaction.cancel() }, - success: { + success: { _ in XCTFail() }, @@ -246,7 +246,7 @@ final class FetchTests: BaseTestDataTestCase { } try transaction.cancel() }, - success: { + success: { _ in XCTFail() }, diff --git a/CoreStoreTests/ImportTests.swift b/CoreStoreTests/ImportTests.swift index b248a10..e328891 100644 --- a/CoreStoreTests/ImportTests.swift +++ b/CoreStoreTests/ImportTests.swift @@ -565,7 +565,9 @@ class ImportTests: BaseTestDataTestCase { XCTAssertEqual(objects.count, 2) zip(objects, sourceArray) - .forEach { object, dictionary in + .forEach { + + let (object, dictionary) = $0 XCTAssertEqual(object.testEntityID, dictionary[(#keyPath(TestEntity1.testEntityID))] as? NSNumber) XCTAssertEqual(object.testBoolean, dictionary[(#keyPath(TestEntity1.testBoolean))] as? NSNumber) XCTAssertEqual(object.testNumber, dictionary[(#keyPath(TestEntity1.testNumber))] as? NSNumber) diff --git a/CoreStoreTests/ListObserverTests.swift b/CoreStoreTests/ListObserverTests.swift index 7e40beb..5e81c23 100644 --- a/CoreStoreTests/ListObserverTests.swift +++ b/CoreStoreTests/ListObserverTests.swift @@ -54,7 +54,7 @@ class ListObserverTests: BaseTestDataTestCase { var events = 0 let willChangeExpectation = self.expectation( - forNotification: "listMonitorWillChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -68,7 +68,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didInsertSectionExpectation = self.expectation( - forNotification: "listMonitor:didInsertSection:toSectionIndex:", + forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertSection:toSectionIndex:"), object: observer, handler: { (note) -> Bool in @@ -88,7 +88,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didInsertObjectExpectation = self.expectation( - forNotification: "listMonitor:didInsertObject:toIndexPath:", + forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -120,7 +120,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didChangeExpectation = self.expectation( - forNotification: "listMonitorDidChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in @@ -185,7 +185,7 @@ class ListObserverTests: BaseTestDataTestCase { var events = 0 let willChangeExpectation = self.expectation( - forNotification: "listMonitorWillChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -200,7 +200,7 @@ class ListObserverTests: BaseTestDataTestCase { ) let didUpdateObjectExpectation = self.expectation( - forNotification: "listMonitor:didUpdateObject:atIndexPath:", + forNotification: NSNotification.Name(rawValue: "listMonitor:didUpdateObject:atIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -251,7 +251,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didChangeExpectation = self.expectation( - forNotification: "listMonitorDidChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in @@ -330,7 +330,7 @@ class ListObserverTests: BaseTestDataTestCase { var events = 0 let willChangeExpectation = self.expectation( - forNotification: "listMonitorWillChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -344,7 +344,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didMoveObjectExpectation = self.expectation( - forNotification: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:", + forNotification: NSNotification.Name(rawValue: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -377,7 +377,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didChangeExpectation = self.expectation( - forNotification: "listMonitorDidChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in @@ -438,7 +438,7 @@ class ListObserverTests: BaseTestDataTestCase { var events = 0 let willChangeExpectation = self.expectation( - forNotification: "listMonitorWillChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -452,7 +452,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didUpdateObjectExpectation = self.expectation( - forNotification: "listMonitor:didDeleteObject:fromIndexPath:", + forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteObject:fromIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -481,7 +481,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didDeleteSectionExpectation = self.expectation( - forNotification: "listMonitor:didDeleteSection:fromSectionIndex:", + forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteSection:fromSectionIndex:"), object: observer, handler: { (note) -> Bool in @@ -509,7 +509,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) let didChangeExpectation = self.expectation( - forNotification: "listMonitorDidChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in diff --git a/CoreStoreTests/ObjectObserverTests.swift b/CoreStoreTests/ObjectObserverTests.swift index bc62132..c23ace9 100644 --- a/CoreStoreTests/ObjectObserverTests.swift +++ b/CoreStoreTests/ObjectObserverTests.swift @@ -58,7 +58,7 @@ class ObjectObserverTests: BaseTestDataTestCase { var events = 0 let willUpdateExpectation = self.expectation( - forNotification: "objectMonitor:willUpdateObject:", + forNotification: NSNotification.Name(rawValue: "objectMonitor:willUpdateObject:"), object: observer, handler: { (note) -> Bool in @@ -75,7 +75,7 @@ class ObjectObserverTests: BaseTestDataTestCase { } ) let didUpdateExpectation = self.expectation( - forNotification: "objectMonitor:didUpdateObject:changedPersistentKeys:", + forNotification: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"), object: observer, handler: { (note) -> Bool in @@ -155,7 +155,7 @@ class ObjectObserverTests: BaseTestDataTestCase { var events = 0 let didDeleteExpectation = self.expectation( - forNotification: "objectMonitor:didDeleteObject:", + forNotification: NSNotification.Name(rawValue: "objectMonitor:didDeleteObject:"), object: observer, handler: { (note) -> Bool in diff --git a/CoreStoreTests/TransactionTests.swift b/CoreStoreTests/TransactionTests.swift index 06252cc..36347b3 100644 --- a/CoreStoreTests/TransactionTests.swift +++ b/CoreStoreTests/TransactionTests.swift @@ -404,7 +404,7 @@ final class TransactionTests: BaseTestCase { var events = 0 let willChangeExpectation = self.expectation( - forNotification: "listMonitorWillChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -418,7 +418,7 @@ final class TransactionTests: BaseTestCase { } ) let didInsertObjectExpectation = self.expectation( - forNotification: "listMonitor:didInsertObject:toIndexPath:", + forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -448,7 +448,7 @@ final class TransactionTests: BaseTestCase { } ) let didChangeExpectation = self.expectation( - forNotification: "listMonitorDidChange:", + forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in @@ -737,7 +737,7 @@ final class TransactionTests: BaseTestCase { createDiscardExpectation.fulfill() try transaction.cancel() }, - success: { + success: { _ in XCTFail() }, @@ -795,7 +795,7 @@ final class TransactionTests: BaseTestCase { try transaction.cancel() }, - success: { + success: { _ in XCTFail() }, @@ -828,7 +828,7 @@ final class TransactionTests: BaseTestCase { try transaction.cancel() }, - success: { + success: { _ in XCTFail() }, diff --git a/Sources/AsynchronousDataTransaction.swift b/Sources/AsynchronousDataTransaction.swift index d31380b..111404a 100644 --- a/Sources/AsynchronousDataTransaction.swift +++ b/Sources/AsynchronousDataTransaction.swift @@ -203,10 +203,10 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { self.isCommitted = true let group = DispatchGroup() group.enter() - self.context.saveAsynchronouslyWithCompletion { (result) -> Void in + self.context.saveAsynchronouslyWithCompletion { (hasChanges, error) -> Void in - completion(result.0, result.1) - self.result = result + completion(hasChanges, error) + self.result = (hasChanges, error) group.leave() } group.wait() @@ -226,12 +226,15 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { !self.isCommitted, "Attempted to commit a \(cs_typeName(self)) more than once." ) - self.autoCommit { (result) in + self.autoCommit { (hasChanges, error) in - switch result { + if let error = error { - case (let hasChanges, nil): completion(SaveResult(hasChanges: hasChanges)) - case (_, let error?): completion(SaveResult(error)) + completion(SaveResult(error)) + } + else { + + completion(SaveResult(hasChanges: hasChanges)) } } } diff --git a/Sources/CSAsynchronousDataTransaction.swift b/Sources/CSAsynchronousDataTransaction.swift index 797127c..34074c2 100644 --- a/Sources/CSAsynchronousDataTransaction.swift +++ b/Sources/CSAsynchronousDataTransaction.swift @@ -54,12 +54,15 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction, CoreSto !self.bridgeToSwift.isCommitted, "Attempted to commit a \(cs_typeName(self)) more than once." ) - self.bridgeToSwift.autoCommit { (result) in + self.bridgeToSwift.autoCommit { (_, error) in - switch result { + if let error = error { - case (_, nil): success?() - case (_, let error?): failure?(error.bridgeToObjectiveC) + failure?(error.bridgeToObjectiveC) + } + else { + + success?() } } } diff --git a/Sources/CSSelect.swift b/Sources/CSSelect.swift index d755766..e47d107 100644 --- a/Sources/CSSelect.swift +++ b/Sources/CSSelect.swift @@ -218,6 +218,7 @@ public final class CSSelect: NSObject { ``` - parameter numberTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query */ + @objc public convenience init(numberTerm: CSSelectTerm) { self.init(Select(numberTerm.bridgeToSwift)) @@ -233,6 +234,7 @@ public final class CSSelect: NSObject { ``` - parameter decimalTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query */ + @objc public convenience init(decimalTerm: CSSelectTerm) { self.init(Select(decimalTerm.bridgeToSwift)) @@ -248,6 +250,7 @@ public final class CSSelect: NSObject { ``` - parameter stringTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query */ + @objc public convenience init(stringTerm: CSSelectTerm) { self.init(Select(stringTerm.bridgeToSwift)) @@ -263,6 +266,7 @@ public final class CSSelect: NSObject { ``` - parameter dateTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query */ + @objc public convenience init(dateTerm: CSSelectTerm) { self.init(Select(dateTerm.bridgeToSwift)) @@ -278,6 +282,7 @@ public final class CSSelect: NSObject { ``` - parameter dataTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query */ + @objc public convenience init(dataTerm: CSSelectTerm) { self.init(Select(dataTerm.bridgeToSwift)) @@ -292,6 +297,7 @@ public final class CSSelect: NSObject { // ... ``` */ + @objc public convenience init(objectIDTerm: ()) { self.init(Select(.objectID())) @@ -307,6 +313,7 @@ public final class CSSelect: NSObject { - parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query - returns: a `CSSelect` clause for querying an entity attribute */ + @objc public static func dictionaryForTerm(_ term: CSSelectTerm) -> CSSelect { return self.init(Select(term.bridgeToSwift)) @@ -325,6 +332,7 @@ public final class CSSelect: NSObject { - parameter terms: the `CSSelectTerm`s specifying the attribute/aggregate values to query - returns: a `CSSelect` clause for querying an entity attribute */ + @objc public static func dictionaryForTerms(_ terms: [CSSelectTerm]) -> CSSelect { return self.init(Select(terms.map { $0.bridgeToSwift })) diff --git a/Sources/CoreStoreSchema.swift b/Sources/CoreStoreSchema.swift index c041ecf..0f58b49 100644 --- a/Sources/CoreStoreSchema.swift +++ b/Sources/CoreStoreSchema.swift @@ -281,7 +281,7 @@ public final class CoreStoreSchema: DynamicSchema { case let attribute as AttributeProtocol: let description = NSAttributeDescription() description.name = attribute.keyPath - description.attributeType = type(of: attribute).attributeType + description.attributeType = Swift.type(of: attribute).attributeType description.isOptional = attribute.isOptional description.isIndexed = attribute.isIndexed description.defaultValue = attribute.defaultValue diff --git a/Sources/DataStack+Migration.swift b/Sources/DataStack+Migration.swift index d129ed0..df29437 100644 --- a/Sources/DataStack+Migration.swift +++ b/Sources/DataStack+Migration.swift @@ -647,8 +647,9 @@ public extension DataStack { let mappingProviders = storage.migrationMappingProviders do { - try withExtendedLifetime((sourceSchema.rawModel(), destinationSchema.rawModel())) { (sourceModel, destinationModel) in + try withExtendedLifetime((sourceSchema.rawModel(), destinationSchema.rawModel())) { + let (sourceModel, destinationModel) = $0 let mapping = try mappingProviders.findMapping( sourceSchema: sourceSchema, destinationSchema: destinationSchema, diff --git a/Sources/MigrationChain.swift b/Sources/MigrationChain.swift index 3f46dd8..c16e35b 100644 --- a/Sources/MigrationChain.swift +++ b/Sources/MigrationChain.swift @@ -117,8 +117,9 @@ public struct MigrationChain: ExpressibleByNilLiteral, ExpressibleByStringLitera var isValid = true var versionTree = [String: String]() - elements.forEach { (sourceVersion, destinationVersion) in + elements.forEach { + let (sourceVersion, destinationVersion) = $0 guard let _ = versionTree.updateValue(destinationVersion, forKey: sourceVersion) else { return @@ -130,8 +131,8 @@ public struct MigrationChain: ExpressibleByNilLiteral, ExpressibleByStringLitera } let leafVersions = Set( elements - .filter { versionTree[$1] == nil } - .map { $1 } + .filter { versionTree[$0.1] == nil } + .map { $0.1 } ) let isVersionAmbiguous = { (start: String) -> Bool in diff --git a/Sources/NSManagedObjectContext+Transaction.swift b/Sources/NSManagedObjectContext+Transaction.swift index f25f81d..7c76968 100644 --- a/Sources/NSManagedObjectContext+Transaction.swift +++ b/Sources/NSManagedObjectContext+Transaction.swift @@ -179,7 +179,7 @@ internal extension NSManagedObjectContext { } @nonobjc - internal func saveAsynchronouslyWithCompletion(_ completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void = { _ in }) { + internal func saveAsynchronouslyWithCompletion(_ completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void = { (_, _) in }) { self.perform { diff --git a/Sources/QueryableAttributeType.swift b/Sources/QueryableAttributeType.swift index a7ae995..f827a23 100644 --- a/Sources/QueryableAttributeType.swift +++ b/Sources/QueryableAttributeType.swift @@ -342,6 +342,7 @@ extension NSData: QueryableAttributeType { public typealias QueryableNativeType = NSData + @nonobjc public class var cs_rawAttributeType: NSAttributeType { return .binaryDataAttributeType @@ -372,6 +373,7 @@ extension NSDate: QueryableAttributeType { public typealias QueryableNativeType = NSDate + @nonobjc public class var cs_rawAttributeType: NSAttributeType { return .dateAttributeType @@ -413,6 +415,7 @@ extension NSManagedObjectID: QueryableAttributeType { public typealias QueryableNativeType = NSManagedObjectID + @nonobjc public class var cs_rawAttributeType: NSAttributeType { return .objectIDAttributeType @@ -443,6 +446,7 @@ extension NSNull: QueryableAttributeType { public typealias QueryableNativeType = NSNull + @nonobjc public class var cs_rawAttributeType: NSAttributeType { return .undefinedAttributeType @@ -473,6 +477,7 @@ extension NSNumber: QueryableAttributeType { public typealias QueryableNativeType = NSNumber + @objc public class var cs_rawAttributeType: NSAttributeType { return .integer64AttributeType @@ -503,6 +508,7 @@ extension NSString: QueryableAttributeType { public typealias QueryableNativeType = NSString + @nonobjc public class var cs_rawAttributeType: NSAttributeType { return .stringAttributeType @@ -533,6 +539,7 @@ extension NSURL: QueryableAttributeType { public typealias QueryableNativeType = NSString + @nonobjc public class var cs_rawAttributeType: NSAttributeType { return .stringAttributeType @@ -558,6 +565,7 @@ extension NSUUID: QueryableAttributeType { public typealias QueryableNativeType = NSString + @nonobjc public class var cs_rawAttributeType: NSAttributeType { return .stringAttributeType From ca9798be143d7a4ee5c5e50ab23663fbf6da76d4 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Fri, 9 Jun 2017 11:29:51 +0900 Subject: [PATCH 02/56] add constraints to Value.Optional and Value.Required native types --- Sources/Value.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Value.swift b/Sources/Value.swift index f2940fe..97fd6b2 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -86,7 +86,7 @@ public enum ValueContainer { ``` - Important: `Value.Required` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. */ - public final class Required: AttributeProtocol { + public final class Required: AttributeProtocol where V.QueryableNativeType: CoreDataNativeType { /** Initializes the metadata for the property. @@ -231,7 +231,7 @@ public enum ValueContainer { ``` - Important: `Value.Optional` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. */ - public final class Optional: AttributeProtocol { + public final class Optional: AttributeProtocol where V.QueryableNativeType: CoreDataNativeType { /** Initializes the metadata for the property. From 3b735d07ec44cf8ee78cfc772703fad7400c1a6b Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 11 Jun 2017 09:33:25 +0900 Subject: [PATCH 03/56] fix merge compile errors --- CoreStoreTests/DynamicModelTests.swift | 2 +- Sources/CoreStoreSchema.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index 8b59e55..6ac863d 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -159,7 +159,7 @@ class DynamicModelTests: BaseTestDataTestCase { XCTAssertTrue(person.pets.value.isEmpty) XCTAssertEqual( - object_getClass(person.rawObject!).keyPathsForValuesAffectingValue(forKey: "displayName"), + type(of: person.rawObject!).keyPathsForValuesAffectingValue(forKey: "displayName"), ["title", "name"] ) diff --git a/Sources/CoreStoreSchema.swift b/Sources/CoreStoreSchema.swift index aa17722..316923b 100644 --- a/Sources/CoreStoreSchema.swift +++ b/Sources/CoreStoreSchema.swift @@ -527,7 +527,7 @@ public final class CoreStoreSchema: DynamicSchema { let origSelector = #selector(NSManagedObject.keyPathsForValuesAffectingValue(forKey:)) let metaClass: AnyClass = object_getClass(managedObjectClass)! - let origMethod = class_getClassMethod(managedObjectClass, origSelector) + let origMethod = class_getClassMethod(managedObjectClass, origSelector)! let origImp = method_getImplementation(origMethod) let newImp = imp_implementationWithBlock(keyPathsForValuesAffectingValue) From eced8f2e93c717682696ff30e563e037fb2927e7 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 12 Jun 2017 22:39:06 +0900 Subject: [PATCH 04/56] fixed compile error on release mode --- Sources/DataStack.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/DataStack.swift b/Sources/DataStack.swift index 726ffb6..a42fe81 100644 --- a/Sources/DataStack.swift +++ b/Sources/DataStack.swift @@ -34,6 +34,11 @@ import CoreData */ public final class DataStack: Equatable { + /** + The resolved application name, used by the `DataStack` as the default Xcode model name (.xcdatamodel filename) if not explicitly provided. + */ + public static let applicationName = (Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String) ?? "CoreData" + /** Convenience initializer for `DataStack` that creates a `SchemaHistory` from the model with the specified `modelName` in the specified `bundle`. @@ -477,8 +482,6 @@ public final class DataStack: Equatable { internal static var defaultConfigurationName = "PF_DEFAULT_CONFIGURATION_NAME" - internal static let applicationName = (Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String) ?? "CoreData" - internal let coordinator: NSPersistentStoreCoordinator internal let rootSavingContext: NSManagedObjectContext internal let mainContext: NSManagedObjectContext From aff966aac9d40e7d0e7d3847e36cb5789d8c75a4 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 5 Jul 2017 08:45:10 +0900 Subject: [PATCH 05/56] WIP: Query builders --- CoreStore.xcodeproj/project.pbxproj | 10 ++ .../ObjectObserverDemoViewController.swift | 2 +- .../TransactionsDemoViewController.swift | 2 +- README.md | 2 +- Sources/CSGroupBy.swift | 6 +- Sources/CSSectionBy.swift | 4 +- Sources/CSSelect.swift | 14 +- Sources/CSWhere.swift | 4 +- Sources/CoreStoreBridge.h | 2 +- Sources/CoreStoreManagedObject.swift | 2 +- Sources/CoreStoreSchema.swift | 16 +- Sources/CustomSchemaMappingProvider.swift | 16 +- Sources/FetchCondition.swift | 143 ++++++++++++++++++ Sources/GroupBy.swift | 6 +- Sources/ListMonitor.swift | 2 +- .../NSEntityDescription+DynamicModel.swift | 8 +- Sources/NSManagedObject+Convenience.swift | 20 +-- Sources/NSManagedObject+ObjectiveC.swift | 4 +- Sources/ObjectObserver.swift | 4 +- Sources/OrderBy.swift | 12 +- Sources/Relationship.swift | 44 +++--- Sources/SectionBy.swift | 6 +- Sources/Select.swift | 26 ++-- Sources/Value.swift | 20 +-- Sources/Where.swift | 10 +- 25 files changed, 269 insertions(+), 116 deletions(-) create mode 100644 Sources/FetchCondition.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 1a0c170..c5a6d7e 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -324,6 +324,10 @@ B5519A601CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; B5519A611CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; B5519A621CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; + B55514EA1EED8BF900BAB888 /* FetchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* FetchCondition.swift */; }; + B55514EB1EED8BF900BAB888 /* FetchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* FetchCondition.swift */; }; + B55514EC1EED8BF900BAB888 /* FetchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* FetchCondition.swift */; }; + B55514ED1EED8BF900BAB888 /* FetchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* FetchCondition.swift */; }; B55717441D15B09E009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; B55717451D15B09F009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; B55717461D15B0A1009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -786,6 +790,7 @@ B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSAsynchronousDataTransaction.swift; sourceTree = ""; }; B5548CD51BD65AE00077652A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; B5548CD71BD65AE50077652A /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; + B55514E91EED8BF900BAB888 /* FetchCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchCondition.swift; sourceTree = ""; }; B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreStoreBridge.h; sourceTree = ""; }; B559CD421CAA8B6300E4D58B /* CSSetupResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSetupResult.swift; sourceTree = ""; }; B559CD481CAA8C6D00E4D58B /* CSStorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSStorageInterface.swift; sourceTree = ""; }; @@ -1374,6 +1379,7 @@ B5E84F061AFF847B0064E85B /* DataStack+Querying.swift */, B5E84F071AFF847B0064E85B /* CoreStore+Querying.swift */, B596BBB51DD5BC67001DCDD9 /* FetchableSource.swift */, + B55514E91EED8BF900BAB888 /* FetchCondition.swift */, B596BBBA1DD5C39F001DCDD9 /* QueryableSource.swift */, B549F65D1E569C7400FBAB2D /* QueryableAttributeType.swift */, B5E84F0A1AFF847B0064E85B /* Protocol Clauses */, @@ -1794,6 +1800,7 @@ B5D339D81E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5D3F6451C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */, B56923FA1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, + B55514EA1EED8BF900BAB888 /* FetchCondition.swift in Sources */, B596BBBB1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDBFF1CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC051CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -1980,6 +1987,7 @@ B5D339D91E9489AB00C880DE /* CoreStoreObject.swift in Sources */, 82BA18CE1C4BBD7100A0916E /* FetchedResultsControllerDelegate.swift in Sources */, B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, + B55514EB1EED8BF900BAB888 /* FetchCondition.swift in Sources */, B596BBBC1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC071CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -2166,6 +2174,7 @@ B5D339DB1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */, B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, + B55514ED1EED8BF900BAB888 /* FetchCondition.swift in Sources */, B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B5ECDC0F1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, @@ -2352,6 +2361,7 @@ B5D339DA1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */, B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, + B55514EC1EED8BF900BAB888 /* FetchCondition.swift in Sources */, B596BBBD1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */, B5E1B59B1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift index dc84a04..502bcbc 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift @@ -85,7 +85,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver { // MARK: ObjectObserver - func objectMonitor(_ monitor: ObjectMonitor, didUpdateObject object: Palette, changedPersistentKeys: Set) { + func objectMonitor(_ monitor: ObjectMonitor, didUpdateObject object: Palette, changedPersistentKeys: Set) { self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys) } diff --git a/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift index 5662123..2d9a259 100644 --- a/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift @@ -131,7 +131,7 @@ class TransactionsDemoViewController: UIViewController, MKMapViewDelegate, Objec // none } - func objectMonitor(_ monitor: ObjectMonitor, didUpdateObject object: Place, changedPersistentKeys: Set) { + func objectMonitor(_ monitor: ObjectMonitor, didUpdateObject object: Place, changedPersistentKeys: Set) { if let mapView = self.mapView { diff --git a/README.md b/README.md index 6a5f188..d03b127 100644 --- a/README.md +++ b/README.md @@ -1447,7 +1447,7 @@ class MyViewController: UIViewController, ObjectObserver { // ... } - func objectMonitor(monitor: ObjectMonitor, didUpdateObject object: MyPersonEntity, changedPersistentKeys: Set) { + func objectMonitor(monitor: ObjectMonitor, didUpdateObject object: MyPersonEntity, changedPersistentKeys: Set) { // ... } diff --git a/Sources/CSGroupBy.swift b/Sources/CSGroupBy.swift index 189f0a4..9f79679 100644 --- a/Sources/CSGroupBy.swift +++ b/Sources/CSGroupBy.swift @@ -41,7 +41,7 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType { The list of key path strings to group results with */ @objc - public var keyPaths: [RawKeyPath] { + public var keyPaths: [KeyPathString] { return self.bridgeToSwift.keyPaths } @@ -52,7 +52,7 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType { - parameter keyPath: a key path string to group results with */ @objc - public convenience init(keyPath: RawKeyPath) { + public convenience init(keyPath: KeyPathString) { self.init(GroupBy(keyPath)) } @@ -63,7 +63,7 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType { - parameter keyPaths: a list of key path strings to group results with */ @objc - public convenience init(keyPaths: [RawKeyPath]) { + public convenience init(keyPaths: [KeyPathString]) { self.init(GroupBy(keyPaths)) } diff --git a/Sources/CSSectionBy.swift b/Sources/CSSectionBy.swift index ba0a93c..bccdffc 100644 --- a/Sources/CSSectionBy.swift +++ b/Sources/CSSectionBy.swift @@ -45,7 +45,7 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType { - returns: a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections */ @objc - public static func keyPath(_ sectionKeyPath: RawKeyPath) -> CSSectionBy { + public static func keyPath(_ sectionKeyPath: KeyPathString) -> CSSectionBy { return self.init(SectionBy(sectionKeyPath)) } @@ -58,7 +58,7 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType { - returns: a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections */ @objc - public static func keyPath(_ sectionKeyPath: RawKeyPath, sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> CSSectionBy { + public static func keyPath(_ sectionKeyPath: KeyPathString, sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> CSSectionBy { return self.init(SectionBy(sectionKeyPath, sectionIndexTransformer)) } diff --git a/Sources/CSSelect.swift b/Sources/CSSelect.swift index e47d107..c06b4f6 100644 --- a/Sources/CSSelect.swift +++ b/Sources/CSSelect.swift @@ -48,7 +48,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { - parameter keyPath: the attribute name */ @objc - public convenience init(keyPath: RawKeyPath) { + public convenience init(keyPath: KeyPathString) { self.init(.attribute(keyPath)) } @@ -65,7 +65,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { - returns: a `CSSelectTerm` to a `CSSelect` clause for querying the average value of an attribute */ @objc - public static func average(_ keyPath: RawKeyPath, as alias: RawKeyPath?) -> CSSelectTerm { + public static func average(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm { return self.init(.average(keyPath, as: alias)) } @@ -82,7 +82,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { - returns: a `SelectTerm` to a `Select` clause for a count query */ @objc - public static func count(_ keyPath: RawKeyPath, as alias: RawKeyPath?) -> CSSelectTerm { + public static func count(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm { return self.init(.count(keyPath, as: alias)) } @@ -99,7 +99,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { - returns: a `CSSelectTerm` to a `CSSelect` clause for querying the maximum value for an attribute */ @objc - public static func maximum(_ keyPath: RawKeyPath, as alias: RawKeyPath?) -> CSSelectTerm { + public static func maximum(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm { return self.init(.maximum(keyPath, as: alias)) } @@ -116,7 +116,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { - returns: a `CSSelectTerm` to a `CSSelect` clause for querying the minimum value for an attribute */ @objc - public static func minimum(_ keyPath: RawKeyPath, as alias: RawKeyPath?) -> CSSelectTerm { + public static func minimum(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm { return self.init(.minimum(keyPath, as: alias)) } @@ -133,7 +133,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { - returns: a `CSSelectTerm` to a `CSSelect` clause for querying the sum value for an attribute */ @objc - public static func sum(_ keyPath: RawKeyPath, as alias: RawKeyPath?) -> CSSelectTerm { + public static func sum(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm { return self.init(.sum(keyPath, as: alias)) } @@ -150,7 +150,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { - returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute */ @objc - public static func objectIDAs(_ alias: RawKeyPath? = nil) -> CSSelectTerm { + public static func objectIDAs(_ alias: KeyPathString? = nil) -> CSSelectTerm { return self.init(.objectID(as: alias)) } diff --git a/Sources/CSWhere.swift b/Sources/CSWhere.swift index 5949990..f36ad16 100644 --- a/Sources/CSWhere.swift +++ b/Sources/CSWhere.swift @@ -85,7 +85,7 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau - parameter value: the arguments for the `==` operator */ @objc - public convenience init(keyPath: RawKeyPath, isEqualTo value: CoreDataNativeType?) { + public convenience init(keyPath: KeyPathString, isEqualTo value: CoreDataNativeType?) { self.init(value == nil || value is NSNull ? Where("\(keyPath) == nil") @@ -99,7 +99,7 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau - parameter list: the array to check membership of */ @objc - public convenience init(keyPath: RawKeyPath, isMemberOf list: [CoreDataNativeType]) { + public convenience init(keyPath: KeyPathString, isMemberOf list: [CoreDataNativeType]) { self.init(Where("\(keyPath) IN %@", list as NSArray)) } diff --git a/Sources/CoreStoreBridge.h b/Sources/CoreStoreBridge.h index 407258d..43dfc6d 100644 --- a/Sources/CoreStoreBridge.h +++ b/Sources/CoreStoreBridge.h @@ -43,7 +43,7 @@ #define CORESTORE_RETURNS_RETAINED __attribute__((ns_returns_retained)) -#pragma mark - RawKeyPath Utilities +#pragma mark - KeyPathString Utilities #define CSKeyPath(type, property) ({ \ type *_je_keypath_dummy __attribute__((unused)); \ diff --git a/Sources/CoreStoreManagedObject.swift b/Sources/CoreStoreManagedObject.swift index 5a851fd..4790660 100644 --- a/Sources/CoreStoreManagedObject.swift +++ b/Sources/CoreStoreManagedObject.swift @@ -30,5 +30,5 @@ import CoreData private enum Static { static let queue = DispatchQueue.concurrent("com.coreStore.coreStoreManagerObjectBarrierQueue") - static var cache: [ObjectIdentifier: [RawKeyPath: Set]] = [:] + static var cache: [ObjectIdentifier: [KeyPathString: Set]] = [:] } diff --git a/Sources/CoreStoreSchema.swift b/Sources/CoreStoreSchema.swift index 316923b..22f284d 100644 --- a/Sources/CoreStoreSchema.swift +++ b/Sources/CoreStoreSchema.swift @@ -208,7 +208,7 @@ public final class CoreStoreSchema: DynamicSchema { } let rawModel = NSManagedObjectModel() var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:] - var allCustomGettersSetters: [DynamicEntity: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:] + var allCustomGettersSetters: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]] = [:] for entity in self.allEntities { let (entityDescription, customGetterSetterByKeyPaths) = self.entityDescription( @@ -253,10 +253,10 @@ public final class CoreStoreSchema: DynamicSchema { private let allEntities: Set private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:] - private var customGettersSettersByEntity: [DynamicEntity: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:] + private var customGettersSettersByEntity: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]] = [:] private weak var cachedRawModel: NSManagedObjectModel? - private func entityDescription(for entity: DynamicEntity, initializer: (DynamicEntity, ModelVersion) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter])) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]) { + private func entityDescription(for entity: DynamicEntity, initializer: (DynamicEntity, ModelVersion) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter])) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]) { if let cachedEntityDescription = self.entityDescriptionsByEntity[entity] { @@ -269,7 +269,7 @@ public final class CoreStoreSchema: DynamicSchema { return (entityDescription, customGetterSetterByKeyPaths) } - private static func firstPassCreateEntityDescription(from entity: DynamicEntity, in modelVersion: ModelVersion) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]) { + private static func firstPassCreateEntityDescription(from entity: DynamicEntity, in modelVersion: ModelVersion) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]) { let entityDescription = NSEntityDescription() entityDescription.coreStoreEntity = entity @@ -278,8 +278,8 @@ public final class CoreStoreSchema: DynamicSchema { entityDescription.versionHashModifier = entity.versionHashModifier entityDescription.managedObjectClassName = CoreStoreManagedObject.cs_subclassName(for: entity, in: modelVersion) - var keyPathsByAffectedKeyPaths: [RawKeyPath: Set] = [:] - var customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter] = [:] + var keyPathsByAffectedKeyPaths: [KeyPathString: Set] = [:] + var customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter] = [:] func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] { var propertyDescriptions: [NSPropertyDescription] = [] @@ -443,9 +443,9 @@ public final class CoreStoreSchema: DynamicSchema { } } - private static func fourthPassSynthesizeManagedObjectClasses(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription], allCustomGettersSetters: [DynamicEntity: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]]) { + private static func fourthPassSynthesizeManagedObjectClasses(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription], allCustomGettersSetters: [DynamicEntity: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]]) { - func createManagedObjectSubclass(for entityDescription: NSEntityDescription, customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter]?) { + func createManagedObjectSubclass(for entityDescription: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter]?) { let superEntity = entityDescription.superentity let className = entityDescription.managedObjectClassName! diff --git a/Sources/CustomSchemaMappingProvider.swift b/Sources/CustomSchemaMappingProvider.swift index a166685..e028433 100644 --- a/Sources/CustomSchemaMappingProvider.swift +++ b/Sources/CustomSchemaMappingProvider.swift @@ -210,7 +210,7 @@ public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider { /** Accesses the property value via its keyPath. */ - public subscript(attribute: RawKeyPath) -> Any? { + public subscript(attribute: KeyPathString) -> Any? { return self.rawObject.cs_accessValueForKVCKey(attribute) } @@ -267,7 +267,7 @@ public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider { /** Accesses or mutates the property value via its keyPath. */ - public subscript(attribute: RawKeyPath) -> Any? { + public subscript(attribute: KeyPathString) -> Any? { get { return self.rawObject.cs_accessValueForKVCKey(attribute) } set { self.rawObject.cs_setValue(newValue, forKVCKey: attribute) } @@ -304,7 +304,7 @@ public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider { // MARK: Internal - internal init(_ rawObject: NSManagedObject, _ sourceAttributesByDestinationKey: [RawKeyPath: NSAttributeDescription]) { + internal init(_ rawObject: NSManagedObject, _ sourceAttributesByDestinationKey: [KeyPathString: NSAttributeDescription]) { self.rawObject = rawObject self.sourceAttributesByDestinationKey = sourceAttributesByDestinationKey @@ -314,7 +314,7 @@ public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider { // MARK: FilePrivate fileprivate let rawObject: NSManagedObject - fileprivate let sourceAttributesByDestinationKey: [RawKeyPath: NSAttributeDescription] + fileprivate let sourceAttributesByDestinationKey: [KeyPathString: NSAttributeDescription] } @@ -477,7 +477,7 @@ public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider { let transformedRenamingIdentifiers = Set(destinationAttributes.keys) .intersection(sourceAttributes.keys) - var sourceAttributesByDestinationKey: [RawKeyPath: NSAttributeDescription] = [:] + var sourceAttributesByDestinationKey: [KeyPathString: NSAttributeDescription] = [:] for renamingIdentifier in transformedRenamingIdentifiers { let sourceAttribute = sourceAttributes[renamingIdentifier]!.attribute @@ -535,7 +535,7 @@ public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider { let userInfo = mapping.userInfo! let transformer = userInfo[CustomEntityMigrationPolicy.UserInfoKey.transformer]! as! CustomMapping.Transformer - let sourceAttributesByDestinationKey = userInfo[CustomEntityMigrationPolicy.UserInfoKey.sourceAttributesByDestinationKey] as! [RawKeyPath: NSAttributeDescription] + let sourceAttributesByDestinationKey = userInfo[CustomEntityMigrationPolicy.UserInfoKey.sourceAttributesByDestinationKey] as! [KeyPathString: NSAttributeDescription] var destinationObject: UnsafeDestinationObject? try transformer( @@ -585,8 +585,8 @@ public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider { var insertMappings: Set = [] var copyMappings: Set = [] var transformMappings: Set = [] - var allMappedSourceKeys: [RawKeyPath: RawKeyPath] = [:] - var allMappedDestinationKeys: [RawKeyPath: RawKeyPath] = [:] + var allMappedSourceKeys: [KeyPathString: KeyPathString] = [:] + var allMappedDestinationKeys: [KeyPathString: KeyPathString] = [:] let sourceRenamingIdentifiers = sourceModel.cs_resolvedRenamingIdentities() let sourceEntityNames = sourceModel.entitiesByName diff --git a/Sources/FetchCondition.swift b/Sources/FetchCondition.swift new file mode 100644 index 0000000..ac05514 --- /dev/null +++ b/Sources/FetchCondition.swift @@ -0,0 +1,143 @@ +// +// FetchCondition.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + +public struct ChainedClauseBuilder { + + public let from: From + public let fetchClauses: [FetchClause] = [] + + internal init(from: From) { + + self.from = from + } +} + +extension From: ClauseChain { + + public typealias ObjectType = T + public typealias TraitType = FetchTrait + + public var builder: ChainedClauseBuilder { + + return .init(from: self) + } +} + +public struct ChainedWhere: ClauseChain { + + public typealias ObjectType = D + public typealias TraitType = T + + public let builder: ChainedClauseBuilder + + fileprivate init(builder: ChainedClauseBuilder) { + + var newBuilder = builder +// newBuilder.fetchClauses.append(Where()) + self.builder = newBuilder + } +} + +public struct ChainedOrderBy: ClauseChain { + + public typealias ObjectType = D + public typealias TraitType = T + + public let builder: ChainedClauseBuilder + + fileprivate init(builder: ChainedClauseBuilder) { + + var newBuilder = builder +// newBuilder.fetchClauses.append(Where()) + self.builder = newBuilder + } +} + +public struct ChainedSelect: ClauseChain { + + public typealias ObjectType = D + public typealias TraitType = T + + public let builder: ChainedClauseBuilder + + fileprivate init(builder: ChainedClauseBuilder) { + + var newBuilder = builder +// newBuilder.fetchClauses.append(Where()) + self.builder = newBuilder + } +} + + + + + +public protocol ClauseTrait {} +public enum FetchTrait: ClauseTrait {} +public enum QueryTrait: ClauseTrait {} +public enum SectionTrait: ClauseTrait {} + + +public protocol ClauseChain { + + associatedtype ObjectType: DynamicObject + associatedtype TraitType: ClauseTrait + + var builder: ChainedClauseBuilder { get } +} + +public extension ClauseChain where Self.TraitType == FetchTrait { + + public func `where`() -> ChainedWhere { + + return .init(builder: self.builder) + } + + public func orderBy() -> ChainedOrderBy { + + return .init(builder: self.builder) + } + + public func select() -> ChainedSelect { + + return .init(builder: self.builder) + } +} + +public extension ClauseChain where Self.TraitType == QueryTrait { + + public func `where`() -> ChainedWhere { + + return .init(builder: self.builder) + } + + public func orderBy() -> ChainedOrderBy { + + return .init(builder: self.builder) + } +} diff --git a/Sources/GroupBy.swift b/Sources/GroupBy.swift index 11e7fd2..0c6153f 100644 --- a/Sources/GroupBy.swift +++ b/Sources/GroupBy.swift @@ -37,7 +37,7 @@ public struct GroupBy: QueryClause, Hashable { /** The list of key path strings to group results with */ - public let keyPaths: [RawKeyPath] + public let keyPaths: [KeyPathString] /** Initializes a `GroupBy` clause with an empty list of key path strings @@ -53,7 +53,7 @@ public struct GroupBy: QueryClause, Hashable { - parameter keyPath: a key path string to group results with - parameter keyPaths: a series of key path strings to group results with */ - public init(_ keyPath: RawKeyPath, _ keyPaths: RawKeyPath...) { + public init(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) { self.init([keyPath] + keyPaths) } @@ -63,7 +63,7 @@ public struct GroupBy: QueryClause, Hashable { - parameter keyPaths: a list of key path strings to group results with */ - public init(_ keyPaths: [RawKeyPath]) { + public init(_ keyPaths: [KeyPathString]) { self.keyPaths = keyPaths } diff --git a/Sources/ListMonitor.swift b/Sources/ListMonitor.swift index de330a2..1492405 100644 --- a/Sources/ListMonitor.swift +++ b/Sources/ListMonitor.swift @@ -997,7 +997,7 @@ public final class ListMonitor: Hashable { fileprivate var fetchedResultsController: CoreStoreFetchedResultsController fileprivate let taskGroup = DispatchGroup() - fileprivate let sectionIndexTransformer: (_ sectionName: RawKeyPath?) -> String? + fileprivate let sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String? private let isSectioned: Bool diff --git a/Sources/NSEntityDescription+DynamicModel.swift b/Sources/NSEntityDescription+DynamicModel.swift index a7bad50..1c9ff17 100644 --- a/Sources/NSEntityDescription+DynamicModel.swift +++ b/Sources/NSEntityDescription+DynamicModel.swift @@ -76,14 +76,14 @@ internal extension NSEntityDescription { } @nonobjc - internal var keyPathsByAffectedKeyPaths: [RawKeyPath: Set] { + internal var keyPathsByAffectedKeyPaths: [KeyPathString: Set] { get { if let userInfo = self.userInfo, let value = userInfo[UserInfoKey.CoreStoreManagedObjectKeyPathsByAffectedKeyPaths] { - return value as! [RawKeyPath: Set] + return value as! [KeyPathString: Set] } return [:] } @@ -97,14 +97,14 @@ internal extension NSEntityDescription { } @nonobjc - internal var customGetterSetterByKeyPaths: [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter] { + internal var customGetterSetterByKeyPaths: [KeyPathString: CoreStoreManagedObject.CustomGetterSetter] { get { if let userInfo = self.userInfo, let value = userInfo[UserInfoKey.CoreStoreManagedObjectCustomGetterSetterByKeyPaths] { - return value as! [RawKeyPath: CoreStoreManagedObject.CustomGetterSetter] + return value as! [KeyPathString: CoreStoreManagedObject.CustomGetterSetter] } return [:] } diff --git a/Sources/NSManagedObject+Convenience.swift b/Sources/NSManagedObject+Convenience.swift index 5c272e3..021bb82 100644 --- a/Sources/NSManagedObject+Convenience.swift +++ b/Sources/NSManagedObject+Convenience.swift @@ -84,7 +84,7 @@ public extension NSManagedObject { - returns: the primitive value for the KVC key */ @nonobjc @inline(__always) - public func getValue(forKvcKey kvcKey: RawKeyPath) -> Any? { + public func getValue(forKvcKey kvcKey: KeyPathString) -> Any? { self.willAccessValue(forKey: kvcKey) defer { @@ -102,7 +102,7 @@ public extension NSManagedObject { - returns: the primitive value transformed by the `didGetValue` closure */ @nonobjc @inline(__always) - public func getValue(forKvcKey kvcKey: RawKeyPath, didGetValue: (Any?) throws -> T) rethrows -> T { + public func getValue(forKvcKey kvcKey: KeyPathString, didGetValue: (Any?) throws -> T) rethrows -> T { self.willAccessValue(forKey: kvcKey) defer { @@ -121,7 +121,7 @@ public extension NSManagedObject { - returns: the primitive value transformed by the `didGetValue` closure */ @nonobjc @inline(__always) - public func getValue(forKvcKey kvcKey: RawKeyPath, willGetValue: () throws -> Void, didGetValue: (Any?) throws -> T) rethrows -> T { + public func getValue(forKvcKey kvcKey: KeyPathString, willGetValue: () throws -> Void, didGetValue: (Any?) throws -> T) rethrows -> T { self.willAccessValue(forKey: kvcKey) defer { @@ -139,7 +139,7 @@ public extension NSManagedObject { - parameter KVCKey: the KVC key */ @nonobjc @inline(__always) - public func setValue(_ value: Any?, forKvcKey KVCKey: RawKeyPath) { + public func setValue(_ value: Any?, forKvcKey KVCKey: KeyPathString) { self.willChangeValue(forKey: KVCKey) defer { @@ -157,7 +157,7 @@ public extension NSManagedObject { - parameter didSetValue: called after executing `setPrimitiveValue(forKey:)`. */ @nonobjc @inline(__always) - public func setValue(_ value: Any?, forKvcKey KVCKey: RawKeyPath, didSetValue: () -> Void) { + public func setValue(_ value: Any?, forKvcKey KVCKey: KeyPathString, didSetValue: () -> Void) { self.willChangeValue(forKey: KVCKey) defer { @@ -177,7 +177,7 @@ public extension NSManagedObject { - parameter didSetValue: called after executing `setPrimitiveValue(forKey:)`. */ @nonobjc @inline(__always) - public func setValue(_ value: T, forKvcKey KVCKey: RawKeyPath, willSetValue: (T) throws -> Any?, didSetValue: (Any?) -> Void = { _ in }) rethrows { + public func setValue(_ value: T, forKvcKey KVCKey: KeyPathString, willSetValue: (T) throws -> Any?, didSetValue: (Any?) -> Void = { _ in }) rethrows { self.willChangeValue(forKey: KVCKey) defer { @@ -212,7 +212,7 @@ public extension NSManagedObject { @available(*, deprecated, renamed: "getValue(forKvcKey:)") @nonobjc - public func accessValueForKVCKey(_ KVCKey: RawKeyPath) -> Any? { + public func accessValueForKVCKey(_ KVCKey: KeyPathString) -> Any? { self.willAccessValue(forKey: KVCKey) defer { @@ -225,7 +225,7 @@ public extension NSManagedObject { @available(*, deprecated, renamed: "getValue(forKvcKey:didGetValue:)") @discardableResult @nonobjc - public func accessValueForKVCKey(_ KVCKey: RawKeyPath, _ didAccessPrimitiveValue: (Any?) throws -> T) rethrows -> T { + public func accessValueForKVCKey(_ KVCKey: KeyPathString, _ didAccessPrimitiveValue: (Any?) throws -> T) rethrows -> T { self.willAccessValue(forKey: KVCKey) defer { @@ -237,7 +237,7 @@ public extension NSManagedObject { @available(*, deprecated, renamed: "setValue(_:forKvcKey:)") @nonobjc - public func setValue(_ value: Any?, forKVCKey KVCKey: RawKeyPath) { + public func setValue(_ value: Any?, forKVCKey KVCKey: KeyPathString) { self.willChangeValue(forKey: KVCKey) defer { @@ -250,7 +250,7 @@ public extension NSManagedObject { @available(*, deprecated, renamed: "setValue(_:forKvcKey:didSetValue:)") @discardableResult @nonobjc - public func setValue(_ value: Any?, forKVCKey KVCKey: RawKeyPath, _ didSetPrimitiveValue: (Any?) throws -> T) rethrows -> T { + public func setValue(_ value: Any?, forKVCKey KVCKey: KeyPathString, _ didSetPrimitiveValue: (Any?) throws -> T) rethrows -> T { self.willChangeValue(forKey: KVCKey) defer { diff --git a/Sources/NSManagedObject+ObjectiveC.swift b/Sources/NSManagedObject+ObjectiveC.swift index fdb702d..66301cd 100644 --- a/Sources/NSManagedObject+ObjectiveC.swift +++ b/Sources/NSManagedObject+ObjectiveC.swift @@ -38,7 +38,7 @@ public extension NSManagedObject { - returns: the primitive value for the KVC key */ @objc - public func cs_accessValueForKVCKey(_ KVCKey: RawKeyPath) -> Any? { + public func cs_accessValueForKVCKey(_ KVCKey: KeyPathString) -> Any? { return self.getValue(forKvcKey: KVCKey) } @@ -50,7 +50,7 @@ public extension NSManagedObject { - parameter KVCKey: the KVC key */ @objc - public func cs_setValue(_ value: Any?, forKVCKey KVCKey: RawKeyPath) { + public func cs_setValue(_ value: Any?, forKVCKey KVCKey: KeyPathString) { self.setValue(value, forKvcKey: KVCKey) } diff --git a/Sources/ObjectObserver.swift b/Sources/ObjectObserver.swift index 0a86067..5ace163 100644 --- a/Sources/ObjectObserver.swift +++ b/Sources/ObjectObserver.swift @@ -61,7 +61,7 @@ public protocol ObjectObserver: class { - parameter object: the `DynamicObject` instance being observed - parameter 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 objectMonitor(_ monitor: ObjectMonitor, didUpdateObject object: ObjectEntityType, changedPersistentKeys: Set) + func objectMonitor(_ monitor: ObjectMonitor, didUpdateObject object: ObjectEntityType, changedPersistentKeys: Set) /** Handles processing right after `object` is deleted. (Optional) @@ -81,7 +81,7 @@ public extension ObjectObserver { public func objectMonitor(_ monitor: ObjectMonitor, willUpdateObject object: ObjectEntityType) { } - public func objectMonitor(_ monitor: ObjectMonitor, didUpdateObject object: ObjectEntityType, changedPersistentKeys: Set) { } + public func objectMonitor(_ monitor: ObjectMonitor, didUpdateObject object: ObjectEntityType, changedPersistentKeys: Set) { } public func objectMonitor(_ monitor: ObjectMonitor, didDeleteObject object: ObjectEntityType) { } } diff --git a/Sources/OrderBy.swift b/Sources/OrderBy.swift index 862dbda..71f5e3e 100644 --- a/Sources/OrderBy.swift +++ b/Sources/OrderBy.swift @@ -27,9 +27,9 @@ import Foundation import CoreData -// MARK: - RawKeyPath +// MARK: - KeyPathString -public typealias RawKeyPath = String +public typealias KeyPathString = String // MARK: - SortKey @@ -40,14 +40,14 @@ public typealias RawKeyPath = String public enum SortKey { /** - Indicates that the `RawKeyPath` should be sorted in ascending order + Indicates that the `KeyPathString` should be sorted in ascending order */ - case ascending(RawKeyPath) + case ascending(KeyPathString) /** - Indicates that the `RawKeyPath` should be sorted in descending order + Indicates that the `KeyPathString` should be sorted in descending order */ - case descending(RawKeyPath) + case descending(KeyPathString) } diff --git a/Sources/Relationship.swift b/Sources/Relationship.swift index 59807dd..2dacf7f 100644 --- a/Sources/Relationship.swift +++ b/Sources/Relationship.swift @@ -95,7 +95,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, @@ -129,7 +129,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, inverse: @escaping (D) -> RelationshipContainer.ToOne, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, @@ -164,7 +164,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, inverse: @escaping (D) -> RelationshipContainer.ToManyOrdered, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, @@ -199,7 +199,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, inverse: @escaping (D) -> RelationshipContainer.ToManyUnordered, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, @@ -234,14 +234,14 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isToMany = false internal let isOrdered = false internal let deleteRule: NSDeleteRule internal let minCount: Int = 0 internal let maxCount: Int = 1 - internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> RawKeyPath?) + internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?) internal let versionHashModifier: String? internal let renamingIdentifier: String? internal let affectedByKeyPaths: () -> Set @@ -294,7 +294,7 @@ public enum RelationshipContainer { // MARK: Private - private init(keyPath: RawKeyPath, inverseKeyPath: @escaping () -> RawKeyPath?, deleteRule: DeleteRule, versionHashModifier: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { + private init(keyPath: KeyPathString, inverseKeyPath: @escaping () -> KeyPathString?, deleteRule: DeleteRule, versionHashModifier: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { self.keyPath = keyPath self.deleteRule = deleteRule.nativeValue @@ -341,7 +341,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, minCount: Int = 0, maxCount: Int = 0, deleteRule: DeleteRule = .nullify, @@ -381,7 +381,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, minCount: Int = 0, maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer.ToOne, @@ -422,7 +422,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, minCount: Int = 0, maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer.ToManyOrdered, @@ -463,7 +463,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, minCount: Int = 0, maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer.ToManyUnordered, @@ -502,7 +502,7 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isToMany = true internal let isOptional = true @@ -510,7 +510,7 @@ public enum RelationshipContainer { internal let deleteRule: NSDeleteRule internal let minCount: Int internal let maxCount: Int - internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> RawKeyPath?) + internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?) internal let versionHashModifier: String? internal let renamingIdentifier: String? internal let affectedByKeyPaths: () -> Set @@ -615,7 +615,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, @@ -655,7 +655,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, inverse: @escaping (D) -> RelationshipContainer.ToOne, deleteRule: DeleteRule = .nullify, minCount: Int = 0, @@ -696,7 +696,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, inverse: @escaping (D) -> RelationshipContainer.ToManyOrdered, deleteRule: DeleteRule = .nullify, minCount: Int = 0, @@ -737,7 +737,7 @@ public enum RelationshipContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, inverse: @escaping (D) -> RelationshipContainer.ToManyUnordered, deleteRule: DeleteRule = .nullify, minCount: Int = 0, @@ -776,7 +776,7 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isToMany = true internal let isOptional = true @@ -784,7 +784,7 @@ public enum RelationshipContainer { internal let deleteRule: NSDeleteRule internal let minCount: Int internal let maxCount: Int - internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> RawKeyPath?) + internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?) internal let versionHashModifier: String? internal let renamingIdentifier: String? internal let affectedByKeyPaths: () -> Set @@ -837,7 +837,7 @@ public enum RelationshipContainer { // MARK: Private - private init(keyPath: RawKeyPath, inverseKeyPath: @escaping () -> RawKeyPath?, deleteRule: DeleteRule, minCount: Int, maxCount: Int, versionHashModifier: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { + private init(keyPath: KeyPathString, inverseKeyPath: @escaping () -> KeyPathString?, deleteRule: DeleteRule, minCount: Int, maxCount: Int, versionHashModifier: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { self.keyPath = keyPath self.deleteRule = deleteRule.nativeValue @@ -1210,11 +1210,11 @@ extension RelationshipContainer.ToManyUnordered { internal protocol RelationshipProtocol: class { - var keyPath: RawKeyPath { get } + var keyPath: KeyPathString { get } var isToMany: Bool { get } var isOrdered: Bool { get } var deleteRule: NSDeleteRule { get } - var inverse: (type: CoreStoreObject.Type, keyPath: () -> RawKeyPath?) { get } + var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?) { get } var affectedByKeyPaths: () -> Set { get } weak var parentObject: CoreStoreObject? { get set } var versionHashModifier: String? { get } diff --git a/Sources/SectionBy.swift b/Sources/SectionBy.swift index c847ac7..f9ac2b7 100644 --- a/Sources/SectionBy.swift +++ b/Sources/SectionBy.swift @@ -47,7 +47,7 @@ public struct SectionBy { - parameter sectionKeyPath: the key path to use to group the objects into sections */ - public init(_ sectionKeyPath: RawKeyPath) { + public init(_ sectionKeyPath: KeyPathString) { self.init(sectionKeyPath, { $0 }) } @@ -59,7 +59,7 @@ public struct SectionBy { - parameter sectionKeyPath: the key path to use to group the objects into sections - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name */ - public init(_ sectionKeyPath: RawKeyPath, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) { + public init(_ sectionKeyPath: KeyPathString, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) { self.sectionKeyPath = sectionKeyPath self.sectionIndexTransformer = sectionIndexTransformer @@ -68,6 +68,6 @@ public struct SectionBy { // MARK: Internal - internal let sectionKeyPath: RawKeyPath + internal let sectionKeyPath: KeyPathString internal let sectionIndexTransformer: (_ sectionName: String?) -> String? } diff --git a/Sources/Select.swift b/Sources/Select.swift index 10a9149..7fe2efd 100644 --- a/Sources/Select.swift +++ b/Sources/Select.swift @@ -74,7 +74,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter keyPath: the attribute name - returns: a `SelectTerm` to a `Select` clause for querying an entity attribute */ - public static func attribute(_ keyPath: RawKeyPath) -> SelectTerm { + public static func attribute(_ keyPath: KeyPathString) -> SelectTerm { return ._attribute(keyPath) } @@ -91,7 +91,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: RawKeyPath, as alias: RawKeyPath? = nil) -> SelectTerm { + public static func average(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "average:", @@ -113,7 +113,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: RawKeyPath, as alias: RawKeyPath? = nil) -> SelectTerm { + public static func count(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "count:", @@ -135,7 +135,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: RawKeyPath, as alias: RawKeyPath? = nil) -> SelectTerm { + public static func maximum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "max:", @@ -157,7 +157,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: RawKeyPath, as alias: RawKeyPath? = nil) -> SelectTerm { + public static func minimum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "min:", @@ -179,7 +179,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: RawKeyPath, as alias: RawKeyPath? = nil) -> SelectTerm { + public static func sum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "sum:", @@ -202,7 +202,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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 "objecID" is used - returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute */ - public static func objectID(as alias: RawKeyPath? = nil) -> SelectTerm { + public static func objectID(as alias: KeyPathString? = nil) -> SelectTerm { return ._identity( alias: alias ?? "objectID", @@ -213,17 +213,17 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { // MARK: ExpressibleByStringLiteral - public init(stringLiteral value: RawKeyPath) { + public init(stringLiteral value: KeyPathString) { self = ._attribute(value) } - public init(unicodeScalarLiteral value: RawKeyPath) { + public init(unicodeScalarLiteral value: KeyPathString) { self = ._attribute(value) } - public init(extendedGraphemeClusterLiteral value: RawKeyPath) { + public init(extendedGraphemeClusterLiteral value: KeyPathString) { self = ._attribute(value) } @@ -274,8 +274,8 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { // MARK: Internal - case _attribute(RawKeyPath) - case _aggregate(function: String, keyPath: RawKeyPath, alias: String, nativeType: NSAttributeType) + case _attribute(KeyPathString) + case _aggregate(function: String, keyPath: KeyPathString, alias: String, nativeType: NSAttributeType) case _identity(alias: String, nativeType: NSAttributeType) } @@ -479,7 +479,7 @@ internal extension Collection where Iterator.Element == SelectTerm { fetchRequest.propertiesToFetch = propertiesToFetch } - internal func keyPathForFirstSelectTerm() -> RawKeyPath { + internal func keyPathForFirstSelectTerm() -> KeyPathString { switch self.first! { diff --git a/Sources/Value.swift b/Sources/Value.swift index b7e8f4c..3ba55ab 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -124,7 +124,7 @@ public enum ValueContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, `default`: @autoclosure @escaping () -> V, isIndexed: Bool = false, isTransient: Bool = false, @@ -207,7 +207,7 @@ public enum ValueContainer { return V.cs_rawAttributeType } - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isOptional = false internal let isIndexed: Bool @@ -323,7 +323,7 @@ public enum ValueContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, `default`: @autoclosure @escaping () -> V? = nil, isIndexed: Bool = false, isTransient: Bool = false, @@ -405,7 +405,7 @@ public enum ValueContainer { return V.cs_rawAttributeType } - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isOptional = true internal let isIndexed: Bool internal let isTransient: Bool @@ -485,7 +485,7 @@ public extension ValueContainer.Required where V: EmptyableAttributeType { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, @@ -579,7 +579,7 @@ public enum TransformableContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, `default`: @autoclosure @escaping () -> V, isIndexed: Bool = false, isTransient: Bool = false, @@ -660,7 +660,7 @@ public enum TransformableContainer { return .transformableAttributeType } - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isOptional = false internal let isIndexed: Bool @@ -774,7 +774,7 @@ public enum TransformableContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, `default`: @autoclosure @escaping () -> V? = nil, isIndexed: Bool = false, isTransient: Bool = false, @@ -855,7 +855,7 @@ public enum TransformableContainer { return .transformableAttributeType } - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isOptional = true internal let isIndexed: Bool @@ -1211,7 +1211,7 @@ internal protocol AttributeProtocol: class { static var attributeType: NSAttributeType { get } - var keyPath: RawKeyPath { get } + var keyPath: KeyPathString { get } var isOptional: Bool { get } var isIndexed: Bool { get } var isTransient: Bool { get } diff --git a/Sources/Where.swift b/Sources/Where.swift index 49fbf36..65cea6a 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -177,7 +177,7 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { - parameter keyPath: the keyPath to compare with - parameter value: the arguments for the `==` operator */ - public init(_ keyPath: RawKeyPath, isEqualTo value: Void?) { + public init(_ keyPath: KeyPathString, isEqualTo value: Void?) { self.init(NSPredicate(format: "\(keyPath) == nil")) } @@ -188,7 +188,7 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { - parameter keyPath: the keyPath to compare with - parameter value: the arguments for the `==` operator */ - public init(_ keyPath: RawKeyPath, isEqualTo value: T?) { + public init(_ keyPath: KeyPathString, isEqualTo value: T?) { switch value { @@ -207,7 +207,7 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { - parameter keyPath: the keyPath to compare with - parameter object: the arguments for the `==` operator */ - public init(_ keyPath: RawKeyPath, isEqualTo object: T?) { + public init(_ keyPath: KeyPathString, isEqualTo object: T?) { switch object { @@ -226,7 +226,7 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { - parameter keyPath: the keyPath to compare with - parameter list: the sequence to check membership of */ - public init(_ keyPath: RawKeyPath, isMemberOf list: S) where S.Iterator.Element: QueryableAttributeType { + public init(_ keyPath: KeyPathString, isMemberOf list: S) where S.Iterator.Element: QueryableAttributeType { self.init(NSPredicate(format: "\(keyPath) IN %@", list.map({ $0.cs_toQueryableNativeType() }) as NSArray)) } @@ -237,7 +237,7 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { - parameter keyPath: the keyPath to compare with - parameter list: the sequence to check membership of */ - public init(_ keyPath: RawKeyPath, isMemberOf list: S) where S.Iterator.Element: DynamicObject { + public init(_ keyPath: KeyPathString, isMemberOf list: S) where S.Iterator.Element: DynamicObject { self.init(NSPredicate(format: "\(keyPath) IN %@", list.map({ $0.cs_id() }) as NSArray)) } From 535eb76adc64e5725cd6ce24f09cfa68591bf30b Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 9 Jul 2017 10:44:53 +0900 Subject: [PATCH 06/56] WIP: query chains! --- CoreStore.xcodeproj/project.pbxproj | 40 +- CoreStoreTests/ConvenienceTests.swift | 16 +- CoreStoreTests/FetchTests.swift | 692 +++++++++--------- CoreStoreTests/ImportTests.swift | 20 +- CoreStoreTests/ListObserverTests.swift | 16 +- CoreStoreTests/ObjectObserverTests.swift | 4 +- CoreStoreTests/OrderByTests.swift | 88 +-- CoreStoreTests/QueryTests.swift | 20 +- CoreStoreTests/TransactionTests.swift | 2 +- CoreStoreTests/WhereTests.swift | 180 ++--- Sources/AsynchronousDataTransaction.swift | 10 +- Sources/BaseDataTransaction+Importing.swift | 46 +- Sources/BaseDataTransaction+Querying.swift | 111 ++- Sources/BaseDataTransaction.swift | 22 +- Sources/CSFrom.swift | 4 +- Sources/CSInto.swift | 4 +- Sources/CSOrderBy.swift | 18 +- Sources/CSWhere.swift | 18 +- Sources/ChainedClauseBuilder.swift | 320 ++++++++ Sources/ClauseTypes.swift | 4 +- Sources/CoreStore+Observing.swift | 18 +- Sources/CoreStore+Querying.swift | 36 +- .../CoreStoreFetchedResultsController.swift | 4 +- Sources/CoreStoreObject+Querying.swift | 113 ++- Sources/DataStack+Observing.swift | 26 +- Sources/DataStack+Querying.swift | 86 ++- Sources/DynamicObject.swift | 8 +- Sources/FetchCondition.swift | 143 ---- Sources/FetchableSource.swift | 43 +- Sources/From.swift | 20 +- Sources/Into.swift | 14 +- ...FetchedResultsController+Convenience.swift | 20 +- Sources/NSManagedObjectContext+Querying.swift | 93 ++- Sources/ObjectMonitor.swift | 2 +- Sources/OrderBy.swift | 192 +++-- Sources/QueryableSource.swift | 8 +- Sources/SynchronousDataTransaction.swift | 10 +- Sources/UnsafeDataTransaction+Observing.swift | 26 +- Sources/Where+NSManagedObject.swift | 220 ++++++ Sources/Where.swift | 70 +- 40 files changed, 1783 insertions(+), 1004 deletions(-) create mode 100644 Sources/ChainedClauseBuilder.swift delete mode 100644 Sources/FetchCondition.swift create mode 100644 Sources/Where+NSManagedObject.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index c5a6d7e..cd4304e 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -324,10 +324,10 @@ B5519A601CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; B5519A611CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; B5519A621CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; - B55514EA1EED8BF900BAB888 /* FetchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* FetchCondition.swift */; }; - B55514EB1EED8BF900BAB888 /* FetchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* FetchCondition.swift */; }; - B55514EC1EED8BF900BAB888 /* FetchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* FetchCondition.swift */; }; - B55514ED1EED8BF900BAB888 /* FetchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* FetchCondition.swift */; }; + B55514EA1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; + B55514EB1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; + B55514EC1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; + B55514ED1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; B55717441D15B09E009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; B55717451D15B09F009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; B55717461D15B0A1009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -477,6 +477,10 @@ B59FA0B01CCBACA7007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; }; B59FA0B11CCBACA7007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; }; B59FA0B21CCBACA8007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; }; + B5A1DAC81F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */; }; + B5A1DAC91F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */; }; + B5A1DACA1F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */; }; + B5A1DACB1F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */; }; B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A261201B64BFDB006EB6D3 /* MigrationType.swift */; }; B5A5F2661CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; }; B5A5F2681CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; }; @@ -790,7 +794,7 @@ B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSAsynchronousDataTransaction.swift; sourceTree = ""; }; B5548CD51BD65AE00077652A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; B5548CD71BD65AE50077652A /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; - B55514E91EED8BF900BAB888 /* FetchCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchCondition.swift; sourceTree = ""; }; + B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainedClauseBuilder.swift; sourceTree = ""; }; B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreStoreBridge.h; sourceTree = ""; }; B559CD421CAA8B6300E4D58B /* CSSetupResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSetupResult.swift; sourceTree = ""; }; B559CD481CAA8C6D00E4D58B /* CSStorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSStorageInterface.swift; sourceTree = ""; }; @@ -827,6 +831,7 @@ B596BBBA1DD5C39F001DCDD9 /* QueryableSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryableSource.swift; sourceTree = ""; }; B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStoreCoordinator+Setup.swift"; sourceTree = ""; }; B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ICloudStore.swift; sourceTree = ""; }; + B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Where+NSManagedObject.swift"; sourceTree = ""; }; B5A261201B64BFDB006EB6D3 /* MigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationType.swift; sourceTree = ""; }; B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSelect.swift; sourceTree = ""; }; B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionLock.swift; sourceTree = ""; }; @@ -1251,6 +1256,15 @@ name = "Dynamic Models"; sourceTree = ""; }; + B5A1DAC61F111BBE003CF369 /* KeyPath Utilities */ = { + isa = PBXGroup; + children = ( + B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */, + B5D339EB1E9495E500C880DE /* CoreStoreObject+Querying.swift */, + ); + name = "KeyPath Utilities"; + sourceTree = ""; + }; B5A5F26B1CAFF8D0004AB9AF /* Swift */ = { isa = PBXGroup; children = ( @@ -1374,14 +1388,14 @@ B5E84EFD1AFF847B0064E85B /* Fetching and Querying */ = { isa = PBXGroup; children = ( - B5D339EB1E9495E500C880DE /* CoreStoreObject+Querying.swift */, B5E84EFE1AFF847B0064E85B /* BaseDataTransaction+Querying.swift */, B5E84F061AFF847B0064E85B /* DataStack+Querying.swift */, B5E84F071AFF847B0064E85B /* CoreStore+Querying.swift */, B596BBB51DD5BC67001DCDD9 /* FetchableSource.swift */, - B55514E91EED8BF900BAB888 /* FetchCondition.swift */, + B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */, B596BBBA1DD5C39F001DCDD9 /* QueryableSource.swift */, B549F65D1E569C7400FBAB2D /* QueryableAttributeType.swift */, + B5A1DAC61F111BBE003CF369 /* KeyPath Utilities */, B5E84F0A1AFF847B0064E85B /* Protocol Clauses */, B5E84EFF1AFF847B0064E85B /* Concrete Clauses */, ); @@ -1800,7 +1814,7 @@ B5D339D81E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5D3F6451C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */, B56923FA1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EA1EED8BF900BAB888 /* FetchCondition.swift in Sources */, + B55514EA1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, B596BBBB1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDBFF1CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC051CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -1860,6 +1874,7 @@ B501FDE71CA8D20500BE22EF /* CSListObserver.swift in Sources */, B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B501FDE21CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, + B5A1DAC81F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */, 2F291E2719C6D3CF007AF63F /* CoreStore.swift in Sources */, B5ECDC111CA816E500C7F112 /* CSTweak.swift in Sources */, B56923C41EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, @@ -1987,7 +2002,7 @@ B5D339D91E9489AB00C880DE /* CoreStoreObject.swift in Sources */, 82BA18CE1C4BBD7100A0916E /* FetchedResultsControllerDelegate.swift in Sources */, B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EB1EED8BF900BAB888 /* FetchCondition.swift in Sources */, + B55514EB1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, B596BBBC1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC071CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -2047,6 +2062,7 @@ B501FDE91CA8D20500BE22EF /* CSListObserver.swift in Sources */, B5E41EC11EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B501FDE41CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, + B5A1DAC91F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */, B5FE4DA31C8481E100FA6A91 /* StorageInterface.swift in Sources */, B5ECDC131CA816E500C7F112 /* CSTweak.swift in Sources */, B56923C51EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, @@ -2174,7 +2190,7 @@ B5D339DB1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */, B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514ED1EED8BF900BAB888 /* FetchCondition.swift in Sources */, + B55514ED1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B5ECDC0F1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, @@ -2234,6 +2250,7 @@ B559CD4D1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, B5E41EC31EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B5ECDBE91CA6BEA300C7F112 /* CSClauseTypes.swift in Sources */, + B5A1DACB1F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */, B52DD1B81BE1F94000949AFE /* DataStack+Migration.swift in Sources */, B5ECDC091CA8138100C7F112 /* CSOrderBy.swift in Sources */, B56923C71EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, @@ -2361,7 +2378,7 @@ B5D339DA1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */, B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EC1EED8BF900BAB888 /* FetchCondition.swift in Sources */, + B55514EC1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, B596BBBD1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */, B5E1B59B1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, @@ -2421,6 +2438,7 @@ B501FDE51CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, B5E41EC21EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B5ECDC141CA816E500C7F112 /* CSTweak.swift in Sources */, + B5A1DACA1F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */, B56321AE1BD6521C006C9394 /* NotificationObserver.swift in Sources */, B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */, B56923C61EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, diff --git a/CoreStoreTests/ConvenienceTests.swift b/CoreStoreTests/ConvenienceTests.swift index 807d943..98b24cf 100644 --- a/CoreStoreTests/ConvenienceTests.swift +++ b/CoreStoreTests/ConvenienceTests.swift @@ -40,8 +40,8 @@ class ConvenienceTests: BaseTestCase { let controller = stack.createFetchedResultsController( From(), SectionBy(#keyPath(TestEntity1.testString)), - Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100), - OrderBy(.ascending(#keyPath(TestEntity1.testString))), + Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100), + OrderBy(.ascending(#keyPath(TestEntity1.testString))), Tweak { $0.fetchLimit = 10 } ) XCTAssertEqual(controller.managedObjectContext, stack.mainContext) @@ -49,11 +49,11 @@ class ConvenienceTests: BaseTestCase { XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString)) XCTAssertEqual( controller.fetchRequest.sortDescriptors!, - OrderBy(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors + OrderBy(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors ) XCTAssertEqual( controller.fetchRequest.predicate, - Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate + Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate ) XCTAssertEqual(controller.fetchRequest.fetchLimit, 10) } @@ -69,8 +69,8 @@ class ConvenienceTests: BaseTestCase { let controller = transaction.createFetchedResultsController( From(), SectionBy(#keyPath(TestEntity1.testString)), - Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100), - OrderBy(.ascending(#keyPath(TestEntity1.testString))), + Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100), + OrderBy(.ascending(#keyPath(TestEntity1.testString))), Tweak { $0.fetchLimit = 10 } ) XCTAssertEqual(controller.managedObjectContext, transaction.context) @@ -78,11 +78,11 @@ class ConvenienceTests: BaseTestCase { XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString)) XCTAssertEqual( controller.fetchRequest.sortDescriptors!, - OrderBy(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors + OrderBy(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors ) XCTAssertEqual( controller.fetchRequest.predicate, - Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate + Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate ) XCTAssertEqual(controller.fetchRequest.fetchLimit, 10) } diff --git a/CoreStoreTests/FetchTests.swift b/CoreStoreTests/FetchTests.swift index a700184..5d1037e 100644 --- a/CoreStoreTests/FetchTests.swift +++ b/CoreStoreTests/FetchTests.swift @@ -43,7 +43,7 @@ final class FetchTests: BaseTestDataTestCase { let from = From() let fetchClauses: [FetchClause] = [ - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses)! do { @@ -146,7 +146,7 @@ final class FetchTests: BaseTestDataTestCase { let from = From() let fetchClauses: [FetchClause] = [ - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses)! do { @@ -285,8 +285,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -299,8 +299,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -313,8 +313,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -329,8 +329,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -343,8 +343,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -357,8 +357,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -373,8 +373,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = self.expectLogger([.logWarning]) { @@ -391,8 +391,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = self.expectLogger([.logWarning]) { @@ -424,8 +424,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -438,8 +438,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -452,8 +452,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -468,8 +468,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -482,8 +482,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -496,8 +496,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -512,8 +512,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -526,8 +526,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -540,8 +540,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -556,8 +556,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = self.expectLogger([.logWarning]) { @@ -574,8 +574,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = self.expectLogger([.logWarning]) { @@ -607,8 +607,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -621,8 +621,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -635,8 +635,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -651,8 +651,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -665,8 +665,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -679,8 +679,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -695,8 +695,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -709,8 +709,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -723,8 +723,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = stack.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -750,8 +750,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -776,8 +776,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -802,8 +802,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -820,8 +820,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -846,8 +846,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -872,8 +872,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -890,8 +890,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -908,8 +908,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -926,8 +926,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -959,8 +959,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -976,8 +976,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -993,8 +993,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -1011,8 +1011,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1036,8 +1036,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1061,8 +1061,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -1079,8 +1079,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1104,8 +1104,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1129,8 +1129,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -1147,7 +1147,7 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -1164,7 +1164,7 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -1196,8 +1196,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1213,8 +1213,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1230,8 +1230,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -1248,8 +1248,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1273,8 +1273,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1298,8 +1298,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -1316,8 +1316,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1341,8 +1341,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = stack.fetchAll(from, fetchClauses) @@ -1366,8 +1366,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = stack.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -1396,8 +1396,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1407,8 +1407,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1418,8 +1418,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -1432,8 +1432,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1443,8 +1443,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1454,8 +1454,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -1470,8 +1470,8 @@ final class FetchTests: BaseTestDataTestCase { stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -1482,8 +1482,8 @@ final class FetchTests: BaseTestDataTestCase { stack.fetchCount( from, - Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -1494,8 +1494,8 @@ final class FetchTests: BaseTestDataTestCase { stack.fetchCount( from, - Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -1519,8 +1519,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1530,8 +1530,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1541,8 +1541,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -1555,8 +1555,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1566,8 +1566,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1577,8 +1577,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -1591,8 +1591,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1602,8 +1602,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1613,8 +1613,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -1629,8 +1629,8 @@ final class FetchTests: BaseTestDataTestCase { stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -1641,8 +1641,8 @@ final class FetchTests: BaseTestDataTestCase { stack.fetchCount( from, - Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -1653,8 +1653,8 @@ final class FetchTests: BaseTestDataTestCase { stack.fetchCount( from, - Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -1678,8 +1678,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1689,8 +1689,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1700,8 +1700,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -1714,8 +1714,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1725,8 +1725,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1736,8 +1736,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -1750,8 +1750,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1761,8 +1761,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -1772,8 +1772,8 @@ final class FetchTests: BaseTestDataTestCase { let count = stack.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -1796,8 +1796,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -1810,8 +1810,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -1824,8 +1824,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -1843,8 +1843,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -1857,8 +1857,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -1871,8 +1871,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -1890,8 +1890,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = self.expectLogger([.logWarning]) { @@ -1908,8 +1908,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = self.expectLogger([.logWarning]) { @@ -1943,8 +1943,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -1957,8 +1957,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -1971,8 +1971,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -1990,8 +1990,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2004,8 +2004,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2018,8 +2018,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -2037,8 +2037,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2051,8 +2051,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2065,8 +2065,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -2084,8 +2084,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = self.expectLogger([.logWarning]) { @@ -2102,8 +2102,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = self.expectLogger([.logWarning]) { @@ -2137,8 +2137,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2151,8 +2151,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2165,8 +2165,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -2184,8 +2184,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2198,8 +2198,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2212,8 +2212,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -2231,8 +2231,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2245,8 +2245,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNotNil(object) @@ -2259,8 +2259,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let object = transaction.fetchOne(from, fetchClauses) XCTAssertNil(object) @@ -2288,8 +2288,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2314,8 +2314,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2340,8 +2340,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = transaction.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -2361,8 +2361,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2387,8 +2387,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2413,8 +2413,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = transaction.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -2434,8 +2434,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -2452,8 +2452,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -2470,8 +2470,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -2505,8 +2505,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2528,8 +2528,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2551,8 +2551,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = transaction.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -2572,8 +2572,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2597,8 +2597,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2622,8 +2622,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = transaction.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -2643,8 +2643,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2668,8 +2668,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2693,8 +2693,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = transaction.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -2714,7 +2714,7 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -2731,7 +2731,7 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ] let objects = self.expectLogger([.logWarning]) { @@ -2765,8 +2765,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2788,8 +2788,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2811,8 +2811,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = transaction.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -2832,8 +2832,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2857,8 +2857,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2882,8 +2882,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = transaction.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -2903,8 +2903,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2928,8 +2928,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ] let objects = transaction.fetchAll(from, fetchClauses) @@ -2953,8 +2953,8 @@ final class FetchTests: BaseTestDataTestCase { do { let fetchClauses: [FetchClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] let objects = transaction.fetchAll(from, fetchClauses) XCTAssertNotNil(objects) @@ -2985,8 +2985,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -2996,8 +2996,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3007,8 +3007,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -3024,8 +3024,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3035,8 +3035,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3046,8 +3046,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -3065,8 +3065,8 @@ final class FetchTests: BaseTestDataTestCase { transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -3077,8 +3077,8 @@ final class FetchTests: BaseTestDataTestCase { transaction.fetchCount( from, - Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -3089,8 +3089,8 @@ final class FetchTests: BaseTestDataTestCase { transaction.fetchCount( from, - Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -3116,8 +3116,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3127,8 +3127,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3138,8 +3138,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -3155,8 +3155,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3166,8 +3166,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3177,8 +3177,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -3194,8 +3194,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3205,8 +3205,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3216,8 +3216,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -3235,8 +3235,8 @@ final class FetchTests: BaseTestDataTestCase { transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K < %@", #keyPath(TestEntity1.testNumber), 4), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -3247,8 +3247,8 @@ final class FetchTests: BaseTestDataTestCase { transaction.fetchCount( from, - Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: 0), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -3259,8 +3259,8 @@ final class FetchTests: BaseTestDataTestCase { transaction.fetchCount( from, - Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) + Where(#keyPath(TestEntity1.testNumber), isEqualTo: nil), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))) ) } XCTAssertNil(count) @@ -3286,8 +3286,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3297,8 +3297,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3308,8 +3308,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -3325,8 +3325,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3336,8 +3336,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3347,8 +3347,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) @@ -3364,8 +3364,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3375,8 +3375,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), + Where("%K < %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.descending(#keyPath(TestEntity1.testEntityID))), Tweak { $0.fetchLimit = 3 } ) XCTAssertNotNil(count) @@ -3386,8 +3386,8 @@ final class FetchTests: BaseTestDataTestCase { let count = transaction.fetchCount( from, - Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 5), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ) XCTAssertNotNil(count) XCTAssertEqual(count, 0) diff --git a/CoreStoreTests/ImportTests.swift b/CoreStoreTests/ImportTests.swift index e328891..05cb7fc 100644 --- a/CoreStoreTests/ImportTests.swift +++ b/CoreStoreTests/ImportTests.swift @@ -444,7 +444,7 @@ class ImportTests: BaseTestDataTestCase { XCTAssertNil(object) XCTAssertEqual(transaction.fetchCount(From()), 5) - let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) + let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) XCTAssertNotNil(existingObjects) XCTAssertEqual(existingObjects?.count, 1) @@ -620,7 +620,7 @@ class ImportTests: BaseTestDataTestCase { errorExpectation.fulfill() XCTAssertEqual(transaction.fetchCount(From()), 6) - let object = transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) + let object = transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) XCTAssertNotNil(object) XCTAssertEqual(object?.testEntityID, NSNumber(value: 106)) XCTAssertNil(object?.testBoolean) @@ -659,7 +659,7 @@ class ImportTests: BaseTestDataTestCase { errorExpectation.fulfill() XCTAssertEqual(transaction.fetchCount(From()), 6) - let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) + let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) XCTAssertNotNil(existingObjects) XCTAssertEqual(existingObjects?.count, 1) @@ -745,7 +745,7 @@ class ImportTests: BaseTestDataTestCase { XCTAssertEqual(object?.testData, ("nil:TestEntity1:7" as NSString).data(using: String.Encoding.utf8.rawValue)!) XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-07T00:00:00Z")!) - let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) + let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) XCTAssertNotNil(existingObjects) XCTAssertEqual(existingObjects?.count, 1) @@ -866,8 +866,8 @@ class ImportTests: BaseTestDataTestCase { errorExpectation.fulfill() XCTAssertEqual(transaction.fetchCount(From()), 5) - XCTAssertNil(transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106))) - XCTAssertNil(transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 107))) + XCTAssertNil(transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106))) + XCTAssertNil(transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 107))) } transaction.unsafeContext().reset() self.checkExpectationsImmediately() @@ -910,7 +910,7 @@ class ImportTests: BaseTestDataTestCase { errorExpectation.fulfill() - let object = transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) + let object = transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) XCTAssertNotNil(object) XCTAssertEqual(object?.testEntityID, NSNumber(value: 106)) XCTAssertNil(object?.testBoolean) @@ -953,7 +953,7 @@ class ImportTests: BaseTestDataTestCase { errorExpectation.fulfill() XCTAssertEqual(transaction.fetchCount(From()), 5) - let object = transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) + let object = transaction.fetchOne(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) XCTAssertNotNil(object) XCTAssertEqual(object?.testEntityID, NSNumber(value: 105)) XCTAssertEqual(object?.testBoolean, NSNumber(value: true)) @@ -963,7 +963,7 @@ class ImportTests: BaseTestDataTestCase { XCTAssertEqual(object?.testData, ("nil:TestEntity1:5" as NSString).data(using: String.Encoding.utf8.rawValue)!) XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-05T00:00:00Z")!) - let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) + let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) XCTAssertNotNil(existingObjects) XCTAssertEqual(existingObjects?.count, 1) @@ -1032,7 +1032,7 @@ class ImportTests: BaseTestDataTestCase { XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data) XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date) } - let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) + let existingObjects = transaction.fetchAll(From(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) XCTAssertNotNil(existingObjects) XCTAssertEqual(existingObjects?.count, 1) diff --git a/CoreStoreTests/ListObserverTests.swift b/CoreStoreTests/ListObserverTests.swift index 5e81c23..64ad58f 100644 --- a/CoreStoreTests/ListObserverTests.swift +++ b/CoreStoreTests/ListObserverTests.swift @@ -43,7 +43,7 @@ class ListObserverTests: BaseTestDataTestCase { let monitor = stack.monitorSectionedList( From(), SectionBy(#keyPath(TestEntity1.testBoolean)), - OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) ) monitor.addObserver(observer) @@ -171,7 +171,7 @@ class ListObserverTests: BaseTestDataTestCase { let monitor = stack.monitorSectionedList( From(), SectionBy(#keyPath(TestEntity1.testBoolean)), - OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) ) monitor.addObserver(observer) @@ -270,7 +270,7 @@ class ListObserverTests: BaseTestDataTestCase { if let object = transaction.fetchOne( From(), - Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) { + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) { object.testNumber = NSNumber(value: 11) object.testDecimal = NSDecimalNumber(string: "11") @@ -284,7 +284,7 @@ class ListObserverTests: BaseTestDataTestCase { } if let object = transaction.fetchOne( From(), - Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { object.testNumber = NSNumber(value: 22) object.testDecimal = NSDecimalNumber(string: "22") @@ -323,7 +323,7 @@ class ListObserverTests: BaseTestDataTestCase { let monitor = stack.monitorSectionedList( From(), SectionBy(#keyPath(TestEntity1.testBoolean)), - OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) ) monitor.addObserver(observer) @@ -396,7 +396,7 @@ class ListObserverTests: BaseTestDataTestCase { if let object = transaction.fetchOne( From(), - Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { object.testBoolean = NSNumber(value: true) } @@ -431,7 +431,7 @@ class ListObserverTests: BaseTestDataTestCase { let monitor = stack.monitorSectionedList( From(), SectionBy(#keyPath(TestEntity1.testBoolean)), - OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) + OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) ) monitor.addObserver(observer) @@ -528,7 +528,7 @@ class ListObserverTests: BaseTestDataTestCase { let count = transaction.deleteAll( From(), - Where(#keyPath(TestEntity1.testBoolean), isEqualTo: false) + Where(#keyPath(TestEntity1.testBoolean), isEqualTo: false) ) XCTAssertEqual(count, 2) return transaction.hasChanges diff --git a/CoreStoreTests/ObjectObserverTests.swift b/CoreStoreTests/ObjectObserverTests.swift index c23ace9..c544f3d 100644 --- a/CoreStoreTests/ObjectObserverTests.swift +++ b/CoreStoreTests/ObjectObserverTests.swift @@ -43,7 +43,7 @@ class ObjectObserverTests: BaseTestDataTestCase { guard let object = stack.fetchOne( From(), - Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else { + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else { XCTFail() return @@ -140,7 +140,7 @@ class ObjectObserverTests: BaseTestDataTestCase { guard let object = stack.fetchOne( From(), - Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else { + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else { XCTFail() return diff --git a/CoreStoreTests/OrderByTests.swift b/CoreStoreTests/OrderByTests.swift index 69af761..c1d861e 100644 --- a/CoreStoreTests/OrderByTests.swift +++ b/CoreStoreTests/OrderByTests.swift @@ -38,21 +38,21 @@ final class OrderByTests: XCTestCase { do { - let orderBy = OrderBy() - XCTAssertEqual(orderBy, OrderBy([NSSortDescriptor]())) - XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key", ascending: false))) + let orderBy = OrderBy() + XCTAssertEqual(orderBy, OrderBy([NSSortDescriptor]())) + XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key", ascending: false))) XCTAssertTrue(orderBy.sortDescriptors.isEmpty) } do { let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true) - let orderBy = OrderBy(sortDescriptor) + let orderBy = OrderBy(sortDescriptor) XCTAssertEqual(orderBy, OrderBy(sortDescriptor)) - XCTAssertEqual(orderBy, OrderBy(.ascending("key1"))) - XCTAssertNotEqual(orderBy, OrderBy(.ascending("key2"))) - XCTAssertNotEqual(orderBy, OrderBy(.descending("key1"))) - XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key1", ascending: false))) - XCTAssertEqual(orderBy, OrderBy([sortDescriptor])) + XCTAssertEqual(orderBy, OrderBy(.ascending("key1"))) + XCTAssertNotEqual(orderBy, OrderBy(.ascending("key2"))) + XCTAssertNotEqual(orderBy, OrderBy(.descending("key1"))) + XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key1", ascending: false))) + XCTAssertEqual(orderBy, OrderBy([sortDescriptor])) XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor]) } do { @@ -61,76 +61,76 @@ final class OrderByTests: XCTestCase { NSSortDescriptor(key: "key1", ascending: true), NSSortDescriptor(key: "key2", ascending: false) ] - let orderBy = OrderBy(sortDescriptors) - XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) - XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) + let orderBy = OrderBy(sortDescriptors) + XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) + XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) XCTAssertNotEqual( orderBy, - OrderBy( + OrderBy( [ NSSortDescriptor(key: "key1", ascending: false), NSSortDescriptor(key: "key2", ascending: false) ] ) ) - XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) - XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) + XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) + XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors) } do { - let orderBy = OrderBy(.ascending("key1")) + let orderBy = OrderBy(.ascending("key1")) let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true) - XCTAssertEqual(orderBy, OrderBy(sortDescriptor)) - XCTAssertEqual(orderBy, OrderBy(.ascending("key1"))) - XCTAssertNotEqual(orderBy, OrderBy(.descending("key1"))) - XCTAssertNotEqual(orderBy, OrderBy(.ascending("key2"))) - XCTAssertEqual(orderBy, OrderBy([sortDescriptor])) + XCTAssertEqual(orderBy, OrderBy(sortDescriptor)) + XCTAssertEqual(orderBy, OrderBy(.ascending("key1"))) + XCTAssertNotEqual(orderBy, OrderBy(.descending("key1"))) + XCTAssertNotEqual(orderBy, OrderBy(.ascending("key2"))) + XCTAssertEqual(orderBy, OrderBy([sortDescriptor])) XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor]) } do { - let orderBy = OrderBy(.ascending("key1"), .descending("key2")) + let orderBy = OrderBy(.ascending("key1"), .descending("key2")) let sortDescriptors = [ NSSortDescriptor(key: "key1", ascending: true), NSSortDescriptor(key: "key2", ascending: false) ] - XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) - XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) + XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) + XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) XCTAssertNotEqual( orderBy, - OrderBy( + OrderBy( [ NSSortDescriptor(key: "key1", ascending: false), NSSortDescriptor(key: "key2", ascending: false) ] ) ) - XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) - XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) + XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) + XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors) } do { - let sortKeys: [SortKey] = [.ascending("key1"), .descending("key2")] - let orderBy = OrderBy(sortKeys) + let sortKeys: [OrderBy.SortKey] = [.ascending("key1"), .descending("key2")] + let orderBy = OrderBy(sortKeys) let sortDescriptors = [ NSSortDescriptor(key: "key1", ascending: true), NSSortDescriptor(key: "key2", ascending: false) ] - XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) - XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) + XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) + XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) XCTAssertNotEqual( orderBy, - OrderBy( + OrderBy( [ NSSortDescriptor(key: "key1", ascending: false), NSSortDescriptor(key: "key2", ascending: false) ] ) ) - XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) - XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) + XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) + XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors) } } @@ -138,15 +138,15 @@ final class OrderByTests: XCTestCase { @objc dynamic func test_ThatOrderByClauseOperations_ComputeCorrectly() { - let orderBy1 = OrderBy(.ascending("key1")) - let orderBy2 = OrderBy(.descending("key2")) - let orderBy3 = OrderBy(.ascending("key3")) + let orderBy1 = OrderBy(.ascending("key1")) + let orderBy2 = OrderBy(.descending("key2")) + let orderBy3 = OrderBy(.ascending("key3")) do { let plusOrderBy = orderBy1 + orderBy2 + orderBy3 - XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"), .ascending("key3"))) - XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1")) + OrderBy(.descending("key2"), .ascending("key3"))) + XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"), .ascending("key3"))) + XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1")) + OrderBy(.descending("key2"), .ascending("key3"))) XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1) @@ -158,14 +158,14 @@ final class OrderByTests: XCTestCase { var plusOrderBy = orderBy1 plusOrderBy += orderBy2 - XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"))) - XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1")) + OrderBy(.descending("key2"))) + XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"))) + XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1")) + OrderBy(.descending("key2"))) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1) XCTAssertEqual(plusOrderBy.sortDescriptors, orderBy1.sortDescriptors + orderBy2.sortDescriptors) plusOrderBy += orderBy3 - XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"), .ascending("key3"))) - XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2")) + OrderBy(.ascending("key3"))) + XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"), .ascending("key3"))) + XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2")) + OrderBy(.ascending("key3"))) XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1) @@ -178,7 +178,7 @@ final class OrderByTests: XCTestCase { @objc dynamic func test_ThatOrderByClauses_ApplyToFetchRequestsCorrectly() { - let orderBy = OrderBy(.ascending("key")) + let orderBy = OrderBy(.ascending("key")) let request = CoreStoreFetchRequest() orderBy.applyToFetchRequest(request) XCTAssertNotNil(request.sortDescriptors) diff --git a/CoreStoreTests/QueryTests.swift b/CoreStoreTests/QueryTests.swift index 215061d..a82b963 100644 --- a/CoreStoreTests/QueryTests.swift +++ b/CoreStoreTests/QueryTests.swift @@ -43,7 +43,7 @@ class QueryTests: BaseTestDataTestCase { let from = From(configurations) let queryClauses: [QueryClause] = [ - Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101) + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101) ] do { @@ -227,8 +227,8 @@ class QueryTests: BaseTestDataTestCase { let from = From(configurations) let queryClauses: [QueryClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] do { @@ -406,7 +406,7 @@ class QueryTests: BaseTestDataTestCase { let from = From(configurations) let queryClauses: [QueryClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) ] do { @@ -583,7 +583,7 @@ class QueryTests: BaseTestDataTestCase { let from = From(configurations) let queryClauses: [QueryClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) ] do { @@ -767,7 +767,7 @@ class QueryTests: BaseTestDataTestCase { let from = From(configurations) let queryClauses: [QueryClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) ] do { @@ -951,7 +951,7 @@ class QueryTests: BaseTestDataTestCase { let from = From(configurations) let queryClauses: [QueryClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) ] do { @@ -1129,7 +1129,7 @@ class QueryTests: BaseTestDataTestCase { let from = From(configurations) let queryClauses: [QueryClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 1) ] do { @@ -1297,8 +1297,8 @@ class QueryTests: BaseTestDataTestCase { let from = From(configurations) let queryClauses: [QueryClause] = [ - Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), - OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) + Where("%K > %@", #keyPath(TestEntity1.testNumber), 3), + OrderBy(.ascending(#keyPath(TestEntity1.testEntityID))) ] do { diff --git a/CoreStoreTests/TransactionTests.swift b/CoreStoreTests/TransactionTests.swift index 36347b3..c138f5b 100644 --- a/CoreStoreTests/TransactionTests.swift +++ b/CoreStoreTests/TransactionTests.swift @@ -396,7 +396,7 @@ final class TransactionTests: BaseTestCase { let observer = TestListObserver() let monitor = stack.monitorList( From(), - OrderBy(.ascending("testEntityID")) + OrderBy(.ascending("testEntityID")) ) monitor.addObserver(observer) diff --git a/CoreStoreTests/WhereTests.swift b/CoreStoreTests/WhereTests.swift index 307e404..4722b37 100644 --- a/CoreStoreTests/WhereTests.swift +++ b/CoreStoreTests/WhereTests.swift @@ -31,12 +31,12 @@ import CoreStore // MARK: - XCTAssertAllEqual -private func XCTAssertAllEqual(_ whereClauses: Where...) { +private func XCTAssertAllEqual(_ whereClauses: Where...) { XCTAssertAllEqual(whereClauses) } -private func XCTAssertAllEqual(_ whereClauses: [Where]) { +private func XCTAssertAllEqual(_ whereClauses: [Where]) { for i in whereClauses.indices { @@ -57,51 +57,51 @@ final class WhereTests: XCTestCase { do { - let whereClause = Where() - XCTAssertEqual(whereClause, Where(true)) - XCTAssertNotEqual(whereClause, Where(false)) + let whereClause = Where() + XCTAssertEqual(whereClause, Where(true)) + XCTAssertNotEqual(whereClause, Where(false)) XCTAssertEqual(whereClause.predicate, NSPredicate(value: true)) } do { - let whereClause = Where(true) - XCTAssertEqual(whereClause, Where()) - XCTAssertNotEqual(whereClause, Where(false)) + let whereClause = Where(true) + XCTAssertEqual(whereClause, Where()) + XCTAssertNotEqual(whereClause, Where(false)) XCTAssertEqual(whereClause.predicate, NSPredicate(value: true)) } do { let predicate = NSPredicate(format: "%K == %@", "key", "value") - let whereClause = Where(predicate) - XCTAssertEqual(whereClause, Where(predicate)) + let whereClause = Where(predicate) + XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause.predicate, predicate) } do { - let whereClause = Where("%K == %@", "key", "value") + let whereClause = Where("%K == %@", "key", "value") let predicate = NSPredicate(format: "%K == %@", "key", "value") - XCTAssertEqual(whereClause, Where(predicate)) + XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause.predicate, predicate) } do { - let whereClause = Where("%K == %@", argumentArray: ["key", "value"]) + let whereClause = Where("%K == %@", argumentArray: ["key", "value"]) let predicate = NSPredicate(format: "%K == %@", "key", "value") - XCTAssertEqual(whereClause, Where(predicate)) + XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause.predicate, predicate) } do { - let whereClause = Where("key", isEqualTo: "value") + let whereClause = Where("key", isEqualTo: "value") let predicate = NSPredicate(format: "%K == %@", "key", "value") - XCTAssertEqual(whereClause, Where(predicate)) + XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause.predicate, predicate) } do { - let whereClause = Where("key", isMemberOf: ["value1", "value2", "value3"]) + let whereClause = Where("key", isMemberOf: ["value1", "value2", "value3"]) let predicate = NSPredicate(format: "%K IN %@", "key", ["value1", "value2", "value3"]) - XCTAssertEqual(whereClause, Where(predicate)) + XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause.predicate, predicate) } } @@ -113,112 +113,112 @@ final class WhereTests: XCTestCase { let value: Int = 100 XCTAssertAllEqual( - Where("%K == %d", "key", value), - Where("%K == %d", "key", value as AnyObject), - Where("%K == %d", "key", NSNumber(value: value)), - Where("%K == %@", "key", value), - Where("%K == %@", "key", value as AnyObject), - Where("%K == %@", "key", NSNumber(value: value)), - Where("key", isEqualTo: value), - Where("key", isEqualTo: NSNumber(value: value)) + Where("%K == %d", "key", value), + Where("%K == %d", "key", value as AnyObject), + Where("%K == %d", "key", NSNumber(value: value)), + Where("%K == %@", "key", value), + Where("%K == %@", "key", value as AnyObject), + Where("%K == %@", "key", NSNumber(value: value)), + Where("key", isEqualTo: value), + Where("key", isEqualTo: NSNumber(value: value)) ) } do { let value = NSNumber(value: 100) XCTAssertAllEqual( - Where("%K == %d", "key", value), - Where("%K == %d", "key", value as AnyObject), - Where("%K == %d", "key", value.intValue), - Where("%K == %@", "key", value), - Where("%K == %@", "key", value as AnyObject), - Where("%K == %@", "key", value.intValue), - Where("key", isEqualTo: value), - Where("key", isEqualTo: value.intValue) + Where("%K == %d", "key", value), + Where("%K == %d", "key", value as AnyObject), + Where("%K == %d", "key", value.intValue), + Where("%K == %@", "key", value), + Where("%K == %@", "key", value as AnyObject), + Where("%K == %@", "key", value.intValue), + Where("key", isEqualTo: value), + Where("key", isEqualTo: value.intValue) ) } do { let value: Int64 = Int64.max XCTAssertAllEqual( - Where("%K == %d", "key", value), - Where("%K == %d", "key", value as AnyObject), - Where("%K == %d", "key", NSNumber(value: value)), - Where("%K == %@", "key", value), - Where("%K == %@", "key", value as AnyObject), - Where("%K == %@", "key", NSNumber(value: value)), - Where("key", isEqualTo: value), - Where("key", isEqualTo: NSNumber(value: value)) + Where("%K == %d", "key", value), + Where("%K == %d", "key", value as AnyObject), + Where("%K == %d", "key", NSNumber(value: value)), + Where("%K == %@", "key", value), + Where("%K == %@", "key", value as AnyObject), + Where("%K == %@", "key", NSNumber(value: value)), + Where("key", isEqualTo: value), + Where("key", isEqualTo: NSNumber(value: value)) ) } do { let value = NSNumber(value: Int64.max) XCTAssertAllEqual( - Where("%K == %d", "key", value), - Where("%K == %d", "key", value as AnyObject), - Where("%K == %d", "key", value.int64Value), - Where("%K == %@", "key", value), - Where("%K == %@", "key", value as AnyObject), - Where("%K == %@", "key", value.int64Value), - Where("key", isEqualTo: value), - Where("key", isEqualTo: value.int64Value) + Where("%K == %d", "key", value), + Where("%K == %d", "key", value as AnyObject), + Where("%K == %d", "key", value.int64Value), + Where("%K == %@", "key", value), + Where("%K == %@", "key", value as AnyObject), + Where("%K == %@", "key", value.int64Value), + Where("key", isEqualTo: value), + Where("key", isEqualTo: value.int64Value) ) } do { let value: String = "value" XCTAssertAllEqual( - Where("%K == %s", "key", value), - Where("%K == %s", "key", value as AnyObject), - Where("%K == %s", "key", NSString(string: value)), - Where("%K == %@", "key", value), - Where("%K == %@", "key", value as AnyObject), - Where("%K == %@", "key", NSString(string: value)), - Where("key", isEqualTo: value), - Where("key", isEqualTo: value as NSString), - Where("key", isEqualTo: NSString(string: value)) + Where("%K == %s", "key", value), + Where("%K == %s", "key", value as AnyObject), + Where("%K == %s", "key", NSString(string: value)), + Where("%K == %@", "key", value), + Where("%K == %@", "key", value as AnyObject), + Where("%K == %@", "key", NSString(string: value)), + Where("key", isEqualTo: value), + Where("key", isEqualTo: value as NSString), + Where("key", isEqualTo: NSString(string: value)) ) } do { let value = NSString(string: "value") XCTAssertAllEqual( - Where("%K == %s", "key", value), - Where("%K == %s", "key", value as String), - Where("%K == %s", "key", value as String as AnyObject), - Where("%K == %@", "key", value), - Where("%K == %@", "key", value as String), - Where("%K == %@", "key", value as String as AnyObject), - Where("key", isEqualTo: value), - Where("key", isEqualTo: value as String), - Where("key", isEqualTo: value as String as NSString) + Where("%K == %s", "key", value), + Where("%K == %s", "key", value as String), + Where("%K == %s", "key", value as String as AnyObject), + Where("%K == %@", "key", value), + Where("%K == %@", "key", value as String), + Where("%K == %@", "key", value as String as AnyObject), + Where("key", isEqualTo: value), + Where("key", isEqualTo: value as String), + Where("key", isEqualTo: value as String as NSString) ) } do { let value: [Int] = [100, 200] XCTAssertAllEqual( - Where("%K IN %@", "key", value), - Where("%K IN %@", "key", value as AnyObject), - Where("%K IN %@", "key", value as [AnyObject]), - Where("%K IN %@", "key", value as NSArray), - Where("%K IN %@", "key", NSArray(array: value)), - Where("%K IN %@", "key", value as AnyObject as! NSArray), - Where("key", isMemberOf: value) + Where("%K IN %@", "key", value), + Where("%K IN %@", "key", value as AnyObject), + Where("%K IN %@", "key", value as [AnyObject]), + Where("%K IN %@", "key", value as NSArray), + Where("%K IN %@", "key", NSArray(array: value)), + Where("%K IN %@", "key", value as AnyObject as! NSArray), + Where("key", isMemberOf: value) ) } do { let value: [Int64] = [Int64.min, 100, Int64.max] XCTAssertAllEqual( - Where("%K IN %@", "key", value), - Where("%K IN %@", "key", value as AnyObject), - Where("%K IN %@", "key", value as [AnyObject]), - Where("%K IN %@", "key", value as NSArray), - Where("%K IN %@", "key", NSArray(array: value)), - Where("%K IN %@", "key", value as AnyObject as! NSArray), - Where("key", isMemberOf: value) + Where("%K IN %@", "key", value), + Where("%K IN %@", "key", value as AnyObject), + Where("%K IN %@", "key", value as [AnyObject]), + Where("%K IN %@", "key", value as NSArray), + Where("%K IN %@", "key", NSArray(array: value)), + Where("%K IN %@", "key", value as AnyObject as! NSArray), + Where("key", isMemberOf: value) ) } } @@ -226,9 +226,9 @@ final class WhereTests: XCTestCase { @objc dynamic func test_ThatWhereClauseOperations_ComputeCorrectly() { - let whereClause1 = Where("key1", isEqualTo: "value1") - let whereClause2 = Where("key2", isEqualTo: "value2") - let whereClause3 = Where("key3", isEqualTo: "value3") + let whereClause1 = Where("key1", isEqualTo: "value1") + let whereClause2 = Where("key2", isEqualTo: "value2") + let whereClause3 = Where("key3", isEqualTo: "value3") do { @@ -259,8 +259,8 @@ final class WhereTests: XCTestCase { do { let andWhere = whereClause1 && whereClause2 && whereClause3 - let noneWhere: Where? = nil - let someWhere: Where? = Where("key4", isEqualTo: "value4") + let noneWhere: Where? = nil + let someWhere: Where? = Where("key4", isEqualTo: "value4") let finalNoneWhere = andWhere && noneWhere @@ -290,8 +290,8 @@ final class WhereTests: XCTestCase { do { let orWhere = whereClause1 || whereClause2 || whereClause3 - let noneWhere: Where? = nil - let someWhere: Where? = Where("key4", isEqualTo: "value4") + let noneWhere: Where? = nil + let someWhere: Where? = Where("key4", isEqualTo: "value4") let finalNoneWhere = orWhere && noneWhere @@ -307,7 +307,7 @@ final class WhereTests: XCTestCase { @objc dynamic func test_ThatWhereClauses_ApplyToFetchRequestsCorrectly() { - let whereClause = Where("key", isEqualTo: "value") + let whereClause = Where("key", isEqualTo: "value") let request = CoreStoreFetchRequest() whereClause.applyToFetchRequest(request) XCTAssertNotNil(request.predicate) diff --git a/Sources/AsynchronousDataTransaction.swift b/Sources/AsynchronousDataTransaction.swift index 0bb75fb..c3601d4 100644 --- a/Sources/AsynchronousDataTransaction.swift +++ b/Sources/AsynchronousDataTransaction.swift @@ -100,7 +100,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { - parameter into: the `Into` clause indicating the destination `NSManagedObject` or `CoreStoreObject` entity type and the destination configuration - returns: a new `NSManagedObject` or `CoreStoreObject` instance of the specified entity type. */ - public override func create(_ into: Into) -> T { + public override func create(_ into: Into) -> D { CoreStore.assert( !self.isCommitted, @@ -116,7 +116,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { - parameter object: the `NSManagedObject` or `CoreStoreObject` to be edited - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`. */ - public override func edit(_ object: T?) -> T? { + public override func edit(_ object: D?) -> D? { CoreStore.assert( !self.isCommitted, @@ -133,7 +133,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { - parameter objectID: the `NSManagedObjectID` for the object to be edited - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`. */ - public override func edit(_ into: Into, _ objectID: NSManagedObjectID) -> T? { + public override func edit(_ into: Into, _ objectID: NSManagedObjectID) -> D? { CoreStore.assert( !self.isCommitted, @@ -148,7 +148,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { - parameter object: the `NSManagedObject` or `CoreStoreObject` to be deleted */ - public override func delete(_ object: T?) { + public override func delete(_ object: D?) { CoreStore.assert( !self.isCommitted, @@ -165,7 +165,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { - parameter object2: another `DynamicObject` to be deleted - parameter objects: other `DynamicObject`s to be deleted */ - public override func delete(_ object1: T?, _ object2: T?, _ objects: T?...) { + public override func delete(_ object1: D?, _ object2: D?, _ objects: D?...) { CoreStore.assert( !self.isCommitted, diff --git a/Sources/BaseDataTransaction+Importing.swift b/Sources/BaseDataTransaction+Importing.swift index acdee54..4905fa5 100644 --- a/Sources/BaseDataTransaction+Importing.swift +++ b/Sources/BaseDataTransaction+Importing.swift @@ -39,9 +39,9 @@ public extension BaseDataTransaction { - throws: an `Error` thrown from any of the `ImportableObject` methods - returns: the created `ImportableObject` instance, or `nil` if the import was ignored */ - public func importObject( - _ into: Into, - source: T.ImportSource) throws -> T? { + public func importObject( + _ into: Into, + source: D.ImportSource) throws -> D? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -69,9 +69,9 @@ public extension BaseDataTransaction { - parameter source: the object to import values from - throws: an `Error` thrown from any of the `ImportableObject` methods */ - public func importObject( - _ object: T, - source: T.ImportSource) throws { + public func importObject( + _ object: D, + source: D.ImportSource) throws { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -97,9 +97,9 @@ public extension BaseDataTransaction { - throws: an `Error` thrown from any of the `ImportableObject` methods - returns: the array of created `ImportableObject` instances */ - public func importObjects( - _ into: Into, - sourceArray: S) throws -> [T] where S.Iterator.Element == T.ImportSource { + public func importObjects( + _ into: Into, + sourceArray: S) throws -> [D] where S.Iterator.Element == D.ImportSource { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -108,7 +108,7 @@ public extension BaseDataTransaction { return try autoreleasepool { - return try sourceArray.flatMap { (source) -> T? in + return try sourceArray.flatMap { (source) -> D? in let entityType = into.entityClass guard entityType.shouldInsert(from: source, in: self) else { @@ -133,9 +133,9 @@ public extension BaseDataTransaction { - throws: an `Error` thrown from any of the `ImportableUniqueObject` methods - returns: the created/updated `ImportableUniqueObject` instance, or `nil` if the import was ignored */ - public func importUniqueObject( - _ into: Into, - source: T.ImportSource) throws -> T? { + public func importUniqueObject( + _ into: Into, + source: D.ImportSource) throws -> D? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -151,7 +151,7 @@ public extension BaseDataTransaction { return nil } - if let object = self.fetchOne(From(entityType), Where(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) { + if let object = self.fetchOne(From(entityType), Where(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) { guard entityType.shouldUpdate(from: source, in: self) else { @@ -185,10 +185,10 @@ public extension BaseDataTransaction { - throws: an `Error` thrown from any of the `ImportableUniqueObject` methods - returns: the array of created/updated `ImportableUniqueObject` instances */ - public func importUniqueObjects( - _ into: Into, + public func importUniqueObjects( + _ into: Into, sourceArray: S, - preProcess: @escaping (_ mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }) throws -> [T] where S.Iterator.Element == T.ImportSource { + preProcess: @escaping (_ mapping: [D.UniqueIDType: D.ImportSource]) throws -> [D.UniqueIDType: D.ImportSource] = { $0 }) throws -> [D] where S.Iterator.Element == D.ImportSource { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -198,10 +198,10 @@ public extension BaseDataTransaction { return try autoreleasepool { let entityType = into.entityClass - var importSourceByID = Dictionary() + var importSourceByID = Dictionary() let sortedIDs = try autoreleasepool { - return try sourceArray.flatMap { (source) -> T.UniqueIDType? in + return try sourceArray.flatMap { (source) -> D.UniqueIDType? in guard let uniqueIDValue = try entityType.uniqueID(from: source, in: self) else { @@ -214,12 +214,12 @@ public extension BaseDataTransaction { importSourceByID = try autoreleasepool { try preProcess(importSourceByID) } - var existingObjectsByID = Dictionary() - self.fetchAll(From(entityType), Where(entityType.uniqueIDKeyPath, isMemberOf: sortedIDs))? + var existingObjectsByID = Dictionary() + self.fetchAll(From(entityType), Where(entityType.uniqueIDKeyPath, isMemberOf: sortedIDs))? .forEach { existingObjectsByID[$0.uniqueIDValue] = $0 } - var processedObjectIDs = Set() - var result = [T]() + var processedObjectIDs = Set() + var result = [D]() for objectID in sortedIDs where !processedObjectIDs.contains(objectID) { diff --git a/Sources/BaseDataTransaction+Querying.swift b/Sources/BaseDataTransaction+Querying.swift index 776507f..8cc55b1 100644 --- a/Sources/BaseDataTransaction+Querying.swift +++ b/Sources/BaseDataTransaction+Querying.swift @@ -39,13 +39,12 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - returns: the number of `DynamicObject`s deleted */ @discardableResult - public func deleteAll(_ from: From, _ deleteClauses: DeleteClause...) -> Int? { + public func deleteAll(_ from: From, _ deleteClauses: DeleteClause...) -> Int? { CoreStore.assert( self.isRunningInAllowedQueue(), "Attempted to delete from a \(cs_typeName(self)) outside its designated queue." ) - return self.context.deleteAll(from, deleteClauses) } @@ -57,14 +56,32 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - returns: the number of `DynamicObject`s deleted */ @discardableResult - public func deleteAll(_ from: From, _ deleteClauses: [DeleteClause]) -> Int? { + public func deleteAll(_ from: From, _ deleteClauses: [DeleteClause]) -> Int? { + + CoreStore.assert( + self.isRunningInAllowedQueue(), + "Attempted to delete from a \(cs_typeName(self)) outside its designated queue." + ) + return self.context.deleteAll(from, deleteClauses) + } + + /** + Deletes all `DynamicObject`s that satisfy the specified conditions. + ``` + transaction.deleteAll(From().where(\.age > 50) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` clause chain created from a `From` clause + - returns: the number of `DynamicObject`s deleted + */ + @discardableResult + public func deleteAll(_ clauseChain: B) -> Int? { CoreStore.assert( self.isRunningInAllowedQueue(), "Attempted to delete from a \(cs_typeName(self)) outside its designated queue." ) - return self.context.deleteAll(from, deleteClauses) + return self.context.deleteAll(clauseChain.from, clauseChain.fetchClauses) } @@ -76,7 +93,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter object: a reference to the object created/fetched outside the transaction - returns: the `DynamicObject` instance if the object exists in the transaction, or `nil` if not found. */ - public func fetchExisting(_ object: T) -> T? { + public func fetchExisting(_ object: D) -> D? { return self.context.fetchExisting(object) } @@ -87,7 +104,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter objectID: the `NSManagedObjectID` for the object - returns: the `DynamicObject` instance if the object exists in the transaction, or `nil` if not found. */ - public func fetchExisting(_ objectID: NSManagedObjectID) -> T? { + public func fetchExisting(_ objectID: NSManagedObjectID) -> D? { return self.context.fetchExisting(objectID) } @@ -98,7 +115,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter objects: an array of `DynamicObject`s created/fetched outside the transaction - returns: the `DynamicObject` array for objects that exists in the transaction */ - public func fetchExisting(_ objects: S) -> [T] where S.Iterator.Element == T { + public func fetchExisting(_ objects: S) -> [D] where S.Iterator.Element == D { return self.context.fetchExisting(objects) } @@ -109,7 +126,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter objectIDs: the `NSManagedObjectID` array for the objects - returns: the `DynamicObject` array for objects that exists in the transaction */ - public func fetchExisting(_ objectIDs: S) -> [T] where S.Iterator.Element == NSManagedObjectID { + public func fetchExisting(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID { return self.context.fetchExisting(objectIDs) } @@ -121,7 +138,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s */ - public func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> T? { + public func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> D? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -137,7 +154,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s */ - public func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> T? { + public func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> D? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -146,6 +163,16 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchOne(from, fetchClauses) } + // TODO: docs + public func fetchOne(_ clauseChain: B) -> B.ObjectType? { + + CoreStore.assert( + self.isRunningInAllowedQueue(), + "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." + ) + return self.context.fetchOne(clauseChain) + } + /** Fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -153,7 +180,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s */ - public func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [T]? { + public func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [D]? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -169,7 +196,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s */ - public func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [T]? { + public func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [D]? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -178,6 +205,16 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchAll(from, fetchClauses) } + // TODO: docs + public func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { + + CoreStore.assert( + self.isRunningInAllowedQueue(), + "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." + ) + return self.context.fetchAll(clauseChain) + } + /** Fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -185,7 +222,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s */ - public func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? { + public func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -201,7 +238,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s */ - public func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? { + public func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -210,6 +247,16 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchCount(from, fetchClauses) } + // TODO: docs + public func fetchCount(_ clauseChain: B) -> Int? { + + CoreStore.assert( + self.isRunningInAllowedQueue(), + "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." + ) + return self.context.fetchCount(clauseChain) + } + /** Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -217,7 +264,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s */ - public func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { + public func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -233,7 +280,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s */ - public func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { + public func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -242,6 +289,16 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchObjectID(from, fetchClauses) } + // TODO: docs + public func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? { + + CoreStore.assert( + self.isRunningInAllowedQueue(), + "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." + ) + return self.context.fetchObjectID(clauseChain) + } + /** Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -249,7 +306,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s */ - public func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { + public func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -265,7 +322,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s */ - public func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { + public func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -274,6 +331,16 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchObjectIDs(from, fetchClauses) } + // TODO: docs + public func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? { + + CoreStore.assert( + self.isRunningInAllowedQueue(), + "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." + ) + return self.context.fetchObjectIDs(clauseChain) + } + // MARK: QueryableSource @@ -287,7 +354,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter 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? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -306,7 +373,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter 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? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -325,7 +392,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter 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...) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -344,7 +411,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter 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]) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { CoreStore.assert( self.isRunningInAllowedQueue(), diff --git a/Sources/BaseDataTransaction.swift b/Sources/BaseDataTransaction.swift index 3a657f7..5af87d1 100644 --- a/Sources/BaseDataTransaction.swift +++ b/Sources/BaseDataTransaction.swift @@ -50,7 +50,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter into: the `Into` clause indicating the destination `NSManagedObject` or `CoreStoreObject` entity type and the destination configuration - returns: a new `NSManagedObject` or `CoreStoreObject` instance of the specified entity type. */ - public func create(_ into: Into) -> T { + public func create(_ into: Into) -> D { let entityClass = into.entityClass CoreStore.assert( @@ -121,7 +121,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter object: the `NSManagedObject` type to be edited - returns: an editable proxy for the specified `NSManagedObject`. */ - public func edit(_ object: T?) -> T? { + public func edit(_ object: D?) -> D? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -141,7 +141,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter objectID: the `NSManagedObjectID` for the object to be edited - returns: an editable proxy for the specified `NSManagedObject`. */ - public func edit(_ into: Into, _ objectID: NSManagedObjectID) -> T? { + public func edit(_ into: Into, _ objectID: NSManagedObjectID) -> D? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -160,7 +160,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter object: the `NSManagedObject` to be deleted */ - public func delete(_ object: T?) { + public func delete(_ object: D?) { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -179,7 +179,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter object2: another `NSManagedObject` to be deleted - parameter objects: other `NSManagedObject`s to be deleted */ - public func delete(_ object1: T?, _ object2: T?, _ objects: T?...) { + public func delete(_ object1: D?, _ object2: D?, _ objects: D?...) { self.delete(([object1, object2] + objects).flatMap { $0 }) } @@ -220,7 +220,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter entity: the `DynamicObject` subclass to filter - returns: a `Set` of pending `DynamicObject`s of the specified type that were inserted to the transaction. */ - public func insertedObjects(_ entity: T.Type) -> Set { + public func insertedObjects(_ entity: D.Type) -> Set { CoreStore.assert( self.transactionQueue.cs_isCurrentExecutionContext(), @@ -257,7 +257,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter entity: the `DynamicObject` subclass to filter - returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were inserted to the transaction. */ - public func insertedObjectIDs(_ entity: T.Type) -> Set { + public func insertedObjectIDs(_ entity: D.Type) -> Set { CoreStore.assert( self.transactionQueue.cs_isCurrentExecutionContext(), @@ -276,7 +276,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter entity: the `DynamicObject` subclass to filter - returns: a `Set` of pending `DynamicObject`s of the specified type that were updated in the transaction. */ - public func updatedObjects(_ entity: T.Type) -> Set { + public func updatedObjects(_ entity: D.Type) -> Set { CoreStore.assert( self.transactionQueue.cs_isCurrentExecutionContext(), @@ -313,7 +313,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter entity: the `DynamicObject` subclass to filter - returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were updated in the transaction. */ - public func updatedObjectIDs(_ entity: T.Type) -> Set { + public func updatedObjectIDs(_ entity: D.Type) -> Set { CoreStore.assert( self.transactionQueue.cs_isCurrentExecutionContext(), @@ -332,7 +332,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter entity: the `DynamicObject` subclass to filter - returns: a `Set` of pending `DynamicObject`s of the specified type that were deleted from the transaction. */ - public func deletedObjects(_ entity: T.Type) -> Set { + public func deletedObjects(_ entity: D.Type) -> Set { CoreStore.assert( self.transactionQueue.cs_isCurrentExecutionContext(), @@ -370,7 +370,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter entity: the `DynamicObject` subclass to filter - returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. */ - public func deletedObjectIDs(_ entity: T.Type) -> Set { + public func deletedObjectIDs(_ entity: D.Type) -> Set { CoreStore.assert( self.transactionQueue.cs_isCurrentExecutionContext(), diff --git a/Sources/CSFrom.swift b/Sources/CSFrom.swift index fca0c17..50d9d6c 100644 --- a/Sources/CSFrom.swift +++ b/Sources/CSFrom.swift @@ -145,7 +145,7 @@ public final class CSFrom: NSObject { public let bridgeToSwift: From - public init(_ swiftValue: From) { + public init(_ swiftValue: From) { self.bridgeToSwift = swiftValue.downcast() super.init() @@ -155,7 +155,7 @@ public final class CSFrom: NSObject { // MARK: - From -extension From where T: NSManagedObject { +extension From where D: NSManagedObject { // MARK: CoreStoreSwiftType diff --git a/Sources/CSInto.swift b/Sources/CSInto.swift index 0510b32..6f3687f 100644 --- a/Sources/CSInto.swift +++ b/Sources/CSInto.swift @@ -112,7 +112,7 @@ public final class CSInto: NSObject { public let bridgeToSwift: Into - public required init(_ swiftValue: Into) { + public required init(_ swiftValue: Into) { self.bridgeToSwift = swiftValue.downcast() super.init() @@ -122,7 +122,7 @@ public final class CSInto: NSObject { // MARK: - Into -extension Into where T: NSManagedObject { +extension Into where D: NSManagedObject { // MARK: CoreStoreSwiftType diff --git a/Sources/CSOrderBy.swift b/Sources/CSOrderBy.swift index a7928ff..403fbfc 100644 --- a/Sources/CSOrderBy.swift +++ b/Sources/CSOrderBy.swift @@ -35,7 +35,7 @@ import CoreData - SeeAlso: `OrderBy` */ @objc -public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause, CoreStoreObjectiveCType { +public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause { /** The list of sort descriptors @@ -110,11 +110,11 @@ public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteCl // MARK: CoreStoreObjectiveCType - public let bridgeToSwift: OrderBy + public let bridgeToSwift: OrderBy - public init(_ swiftValue: OrderBy) { + public init(_ swiftValue: OrderBy) { - self.bridgeToSwift = swiftValue + self.bridgeToSwift = swiftValue.downcast() super.init() } } @@ -122,7 +122,7 @@ public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteCl // MARK: - OrderBy -extension OrderBy: CoreStoreSwiftType { +extension OrderBy where D: NSManagedObject { // MARK: CoreStoreSwiftType @@ -130,4 +130,12 @@ extension OrderBy: CoreStoreSwiftType { return CSOrderBy(self) } + + + // MARK: FilePrivate + + fileprivate func downcast() -> OrderBy { + + return OrderBy(self.sortDescriptors) + } } diff --git a/Sources/CSWhere.swift b/Sources/CSWhere.swift index f36ad16..0bd637b 100644 --- a/Sources/CSWhere.swift +++ b/Sources/CSWhere.swift @@ -35,7 +35,7 @@ import CoreData - SeeAlso: `Where` */ @objc -public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause, CoreStoreObjectiveCType { +public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause { /** The internal `NSPredicate` instance for the `Where` clause @@ -149,11 +149,11 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau // MARK: CoreStoreObjectiveCType - public let bridgeToSwift: Where + public let bridgeToSwift: Where - public init(_ swiftValue: Where) { + public init(_ swiftValue: Where) { - self.bridgeToSwift = swiftValue + self.bridgeToSwift = swiftValue.downcast() super.init() } } @@ -161,7 +161,7 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau // MARK: - Where -extension Where: CoreStoreSwiftType { +extension Where where D: NSManagedObject { // MARK: CoreStoreSwiftType @@ -169,4 +169,12 @@ extension Where: CoreStoreSwiftType { return CSWhere(self) } + + + // MARK: FilePrivate + + fileprivate func downcast() -> Where { + + return Where(self.predicate) + } } diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift new file mode 100644 index 0000000..df13948 --- /dev/null +++ b/Sources/ChainedClauseBuilder.swift @@ -0,0 +1,320 @@ +// +// FetchCondition.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +public protocol FetchChainableBuilderType { + + associatedtype ObjectType: DynamicObject + + var from: From { get set } + var fetchClauses: [FetchClause] { get set } +} + +public protocol QueryChainableBuilderType { + + associatedtype ObjectType: DynamicObject + associatedtype ResultType: SelectResultType + + var from: From { get set } + var select: Select { get set } + var groupBy: GroupBy { get set } + var fetchClauses: [FetchClause] { get set } +} + +public protocol SectionMonitorBuilderType { + + associatedtype ObjectType: DynamicObject + + var from: From { get set } + var sectionBy: SectionBy { get set } + var fetchClauses: [FetchClause] { get set } +} + + +// MARK: - FetchChainBuilder + +public struct FetchChainBuilder: FetchChainableBuilderType { + + // MARK: FetchChainableBuilderType + + public typealias ObjectType = D + + public var from: From + public var fetchClauses: [FetchClause] = [] +} + + +// MARK: - QueryChainBuilder + +public struct QueryChainBuilder: QueryChainableBuilderType { + + // MARK: QueryChainableBuilderType + + public typealias ObjectType = D + public typealias ResultType = R + + public var from: From + public var select: Select + public var groupBy: GroupBy + public var fetchClauses: [FetchClause] = [] +} + + +// MARK: - SectionMonitorChainBuilder + +public struct SectionMonitorChainBuilder: SectionMonitorBuilderType { + + // MARK: SectionMonitorBuilderType + + public var from: From + public var sectionBy: SectionBy + public var fetchClauses: [FetchClause] = [] +} + + +// MARK: - From + +public extension From { + + public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { + + return self.select(resultType, [selectTerm] + selectTerms) + } + + public func select(_ resultType: R.Type, _ selectTerms: [SelectTerm]) -> QueryChainBuilder { + + return .init( + from: self, + select: .init(selectTerms), + groupBy: .init(), + fetchClauses: [] + ) + } + + public func sectionBy(_ sectionKeyPath: KeyPathString) -> SectionMonitorChainBuilder { + + return self.sectionBy(sectionKeyPath, { $0 }) + } + + public func sectionBy(_ sectionKeyPath: KeyPathString, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { + + return .init( + from: self, + sectionBy: .init(sectionKeyPath, sectionIndexTransformer), + fetchClauses: [] + ) + } + + public func `where`(_ clause: Where) -> FetchChainBuilder { + + return self.fetchChain(appending: clause) + } + + public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { + + return self.fetchChain(appending: Where(format, argumentArray: args)) + } + + public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { + + return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) + } + + public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { + + return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) + } + + public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { + + return self.fetchChain(appending: Tweak(fetchRequest)) + } + + public func appending(_ clause: FetchClause) -> FetchChainBuilder { + + return self.fetchChain(appending: clause) + } + + + // MARK: Private + + private func fetchChain(appending clause: FetchClause) -> FetchChainBuilder { + + return .init(from: self, fetchClauses: [clause]) + } +} + +public extension FetchChainBuilder { + + public func `where`(_ clause: Where) -> FetchChainBuilder { + + return self.fetchChain(appending: clause) + } + + public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { + + return self.fetchChain(appending: Where(format, argumentArray: args)) + } + + public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { + + return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) + } + + public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { + + return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) + } + + public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { + + return self.fetchChain(appending: Tweak(fetchRequest)) + } + + public func appending(_ clause: FetchClause) -> FetchChainBuilder { + + return self.fetchChain(appending: clause) + } + + + // MARK: Private + + private func fetchChain(appending clause: FetchClause) -> FetchChainBuilder { + + return .init( + from: self.from, + fetchClauses: self.fetchClauses + [clause] + ) + } +} + +public extension QueryChainBuilder { + + public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder { + + return self.groupBy([keyPath] + keyPaths) + } + + public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder { + + return .init( + from: self.from, + select: self.select, + groupBy: .init(keyPaths), + fetchClauses: self.fetchClauses + ) + } + + public func `where`(_ clause: Where) -> QueryChainBuilder { + + return self.queryChain(appending: clause) + } + + public func `where`(format: String, _ args: Any...) -> QueryChainBuilder { + + return self.queryChain(appending: Where(format, argumentArray: args)) + } + + public func `where`(format: String, argumentArray: [Any]?) -> QueryChainBuilder { + + return self.queryChain(appending: Where(format, argumentArray: argumentArray)) + } + + public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> QueryChainBuilder { + + return self.queryChain(appending: OrderBy([sortKey] + sortKeys)) + } + + public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> QueryChainBuilder { + + return self.queryChain(appending: Tweak(fetchRequest)) + } + + public func appending(_ clause: FetchClause) -> QueryChainBuilder { + + return self.queryChain(appending: clause) + } + + + // MARK: Private + + private func queryChain(appending clause: FetchClause) -> QueryChainBuilder { + + return .init( + from: self.from, + select: self.select, + groupBy: self.groupBy, + fetchClauses: self.fetchClauses + [clause] + ) + } +} + +public extension SectionMonitorChainBuilder { + + public func `where`(_ clause: Where) -> SectionMonitorChainBuilder { + + return self.sectionMonitorChain(appending: clause) + } + + public func `where`(format: String, _ args: Any...) -> SectionMonitorChainBuilder { + + return self.sectionMonitorChain(appending: Where(format, argumentArray: args)) + } + + public func `where`(format: String, argumentArray: [Any]?) -> SectionMonitorChainBuilder { + + return self.sectionMonitorChain(appending: Where(format, argumentArray: argumentArray)) + } + + public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> SectionMonitorChainBuilder { + + return self.sectionMonitorChain(appending: OrderBy([sortKey] + sortKeys)) + } + + public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> SectionMonitorChainBuilder { + + return self.sectionMonitorChain(appending: Tweak(fetchRequest)) + } + + public func appending(_ clause: FetchClause) -> SectionMonitorChainBuilder { + + return self.sectionMonitorChain(appending: clause) + } + + + // MARK: Private + + private func sectionMonitorChain(appending clause: FetchClause) -> SectionMonitorChainBuilder { + + return .init( + from: self.from, + sectionBy: self.sectionBy, + fetchClauses: self.fetchClauses + [clause] + ) + } +} diff --git a/Sources/ClauseTypes.swift b/Sources/ClauseTypes.swift index b03f3e2..aa92003 100644 --- a/Sources/ClauseTypes.swift +++ b/Sources/ClauseTypes.swift @@ -43,7 +43,7 @@ public protocol FetchClause { /** The `QueryClause` implement clauses used to configure `NSFetchRequest`s. */ -public protocol QueryClause { +public protocol QueryClause: FetchClause { func applyToFetchRequest(_ fetchRequest: NSFetchRequest) } @@ -54,7 +54,7 @@ public protocol QueryClause { /** The `DeleteClause` implement clauses used to configure `NSFetchRequest`s. */ -public protocol DeleteClause { +public protocol DeleteClause: FetchClause { func applyToFetchRequest(_ fetchRequest: NSFetchRequest) } diff --git a/Sources/CoreStore+Observing.swift b/Sources/CoreStore+Observing.swift index d5e543b..d785255 100644 --- a/Sources/CoreStore+Observing.swift +++ b/Sources/CoreStore+Observing.swift @@ -38,7 +38,7 @@ public extension CoreStore { - parameter object: the `DynamicObject` to observe changes from - returns: a `ObjectMonitor` that monitors changes to `object` */ - public static func monitorObject(_ object: T) -> ObjectMonitor { + public static func monitorObject(_ object: D) -> ObjectMonitor { return self.defaultStack.monitorObject(object) } @@ -50,7 +50,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public static func monitorList(_ from: From, _ fetchClauses: FetchClause...) -> ListMonitor { + public static func monitorList(_ from: From, _ fetchClauses: FetchClause...) -> ListMonitor { return self.defaultStack.monitorList(from, fetchClauses) } @@ -62,7 +62,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public static func monitorList(_ from: From, _ fetchClauses: [FetchClause]) -> ListMonitor { + public static func monitorList(_ from: From, _ fetchClauses: [FetchClause]) -> ListMonitor { return self.defaultStack.monitorList(from, fetchClauses) } @@ -74,7 +74,7 @@ public extension CoreStore { - parameter from: a `From` clause indicating the entity type - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public static func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: FetchClause...) { + public static func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: FetchClause...) { self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses) } @@ -86,7 +86,7 @@ public extension CoreStore { - parameter from: a `From` clause indicating the entity type - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public static func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: [FetchClause]) { + public static func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: [FetchClause]) { self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses) } @@ -99,7 +99,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public static func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { + public static func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) } @@ -112,7 +112,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public static func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { + public static func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) } @@ -125,7 +125,7 @@ public extension CoreStore { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { + public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } @@ -138,7 +138,7 @@ public extension CoreStore { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { + public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } diff --git a/Sources/CoreStore+Querying.swift b/Sources/CoreStore+Querying.swift index 48cf117..3d403ef 100644 --- a/Sources/CoreStore+Querying.swift +++ b/Sources/CoreStore+Querying.swift @@ -37,7 +37,7 @@ public extension CoreStore { - parameter object: a reference to the object created/fetched outside the `DataStack` - returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found. */ - public static func fetchExisting(_ object: T) -> T? { + public static func fetchExisting(_ object: D) -> D? { return self.defaultStack.fetchExisting(object) } @@ -48,7 +48,7 @@ public extension CoreStore { - parameter objectID: the `NSManagedObjectID` for the object - returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found. */ - public static func fetchExisting(_ objectID: NSManagedObjectID) -> T? { + public static func fetchExisting(_ objectID: NSManagedObjectID) -> D? { return self.defaultStack.fetchExisting(objectID) } @@ -59,7 +59,7 @@ public extension CoreStore { - parameter objects: an array of `DynamicObject`s created/fetched outside the `DataStack` - returns: the `DynamicObject` array for objects that exists in the `DataStack` */ - public static func fetchExisting(_ objects: S) -> [T] where S.Iterator.Element == T { + public static func fetchExisting(_ objects: S) -> [D] where S.Iterator.Element == D { return self.defaultStack.fetchExisting(objects) } @@ -70,7 +70,7 @@ public extension CoreStore { - parameter objectIDs: the `NSManagedObjectID` array for the objects - returns: the `DynamicObject` array for objects that exists in the `DataStack` */ - public static func fetchExisting(_ objectIDs: S) -> [T] where S.Iterator.Element == NSManagedObjectID { + public static func fetchExisting(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID { return self.defaultStack.fetchExisting(objectIDs) } @@ -82,7 +82,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s */ - public static func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> T? { + public static func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> D? { return self.defaultStack.fetchOne(from, fetchClauses) } @@ -94,7 +94,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s */ - public static func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> T? { + public static func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> D? { return self.defaultStack.fetchOne(from, fetchClauses) } @@ -106,7 +106,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s */ - public static func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [T]? { + public static func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [D]? { return self.defaultStack.fetchAll(from, fetchClauses) } @@ -118,7 +118,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s */ - public static func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [T]? { + public static func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [D]? { return self.defaultStack.fetchAll(from, fetchClauses) } @@ -130,7 +130,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s */ - public static func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? { + public static func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? { return self.defaultStack.fetchCount(from, fetchClauses) } @@ -142,7 +142,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s */ - public static func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? { + public static func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? { return self.defaultStack.fetchCount(from, fetchClauses) } @@ -154,7 +154,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s */ - public static func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { + public static func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { return self.defaultStack.fetchObjectID(from, fetchClauses) } @@ -166,7 +166,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s */ - public static func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { + public static func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { return self.defaultStack.fetchObjectID(from, fetchClauses) } @@ -178,7 +178,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s */ - public static func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { + public static func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { return self.defaultStack.fetchObjectIDs(from, fetchClauses) } @@ -190,7 +190,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s */ - public static func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { + public static func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { return self.defaultStack.fetchObjectIDs(from, fetchClauses) } @@ -205,7 +205,7 @@ public extension CoreStore { - parameter 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? { + public static func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { return self.defaultStack.queryValue(from, selectClause, queryClauses) } @@ -220,7 +220,7 @@ public extension CoreStore { - parameter 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? { + public static func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { return self.defaultStack.queryValue(from, selectClause, queryClauses) } @@ -235,7 +235,7 @@ public extension CoreStore { - parameter 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...) -> [[String: Any]]? { + public static func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { return self.defaultStack.queryAttributes(from, selectClause, queryClauses) } @@ -250,7 +250,7 @@ public extension CoreStore { - parameter 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]) -> [[String: Any]]? { + public static func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { return self.defaultStack.queryAttributes(from, selectClause, queryClauses) } diff --git a/Sources/CoreStoreFetchedResultsController.swift b/Sources/CoreStoreFetchedResultsController.swift index 6fe2cf3..b0c18cb 100644 --- a/Sources/CoreStoreFetchedResultsController.swift +++ b/Sources/CoreStoreFetchedResultsController.swift @@ -35,7 +35,7 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll // MARK: Internal @nonobjc - internal convenience init(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { + internal convenience init(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { self.init( context: dataStack.mainContext, @@ -47,7 +47,7 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll } @nonobjc - internal init(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { + internal init(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { _ = from.applyToFetchRequest( fetchRequest, diff --git a/Sources/CoreStoreObject+Querying.swift b/Sources/CoreStoreObject+Querying.swift index 0a87043..5f8c1ef 100644 --- a/Sources/CoreStoreObject+Querying.swift +++ b/Sources/CoreStoreObject+Querying.swift @@ -26,6 +26,58 @@ import CoreData import Foundation +//public extension From where D: NSManagedObject { +// +//// public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { +//// +//// return self.select(resultType, [selectTerm] + selectTerms) +//// } +//// +//// public func select(_ resultType: R.Type, _ selectTerms: [SelectTerm]) -> QueryChainBuilder { +//// +//// return .init( +//// from: self, +//// select: .init(selectTerms), +//// groupBy: .init(), +//// fetchClauses: [] +//// ) +//// } +// +// public func sectionBy(_ sectionKeyPath: KeyPath) -> SectionMonitorChainBuilder { +// +// return self.sectionBy(sectionKeyPath._kvcKeyPathString!, { $0 }) +// } +// +// public func sectionBy(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { +// +// return self.sectionBy(sectionKeyPath._kvcKeyPathString!, sectionIndexTransformer) +// } +// +// public func `where`(_ keyPath: KeyPath, isEqualTo value: Void?) -> FetchChainBuilder { +// +// return self.where(keyPath._kvcKeyPathString!, isEqualTo: value) +// } +// +// public func `where`(_ keyPath: KeyPath, isEqualTo value: U?) -> FetchChainBuilder { +// +// return self.where(keyPath._kvcKeyPathString!, isEqualTo: value) +// } +// +// public func `where`(_ keyPath: KeyPath, isEqualTo object: D?) -> FetchChainBuilder { +// +// return self.where(keyPath._kvcKeyPathString!, isEqualTo: object) +// } +// +// public func `where`(_ keyPath: KeyPath, isMemberOf list: S) -> FetchChainBuilder where S.Iterator.Element: QueryableAttributeType { +// +// return self.where(keyPath._kvcKeyPathString!, isMemberOf: list) +// } +// +// public func `where`(_ keyPath: KeyPath, isMemberOf list: S) -> FetchChainBuilder where S.Iterator.Element == D { +// +// return self.where(keyPath._kvcKeyPathString!, isMemberOf: list) +// } +//} // MARK: - DynamicObject @@ -92,7 +144,7 @@ public extension DynamicObject where Self: CoreStoreObject { let person = CoreStore.fetchOne(From(), Person.where { $0.nickname == "John" }) ``` */ - public static func `where`(_ condition: (Self) -> Where) -> Where { + public static func `where`(_ condition: (Self) -> Where) -> Where { return condition(self.meta) } @@ -103,7 +155,7 @@ public extension DynamicObject where Self: CoreStoreObject { let person = CoreStore.fetchAll(From(), Person.orderBy(ascending: { $0.age })) ``` */ - public static func orderBy(ascending attribute: (Self) -> ValueContainer.Required) -> OrderBy { + public static func orderBy(ascending attribute: (Self) -> ValueContainer.Required) -> OrderBy { return OrderBy(.ascending(attribute(self.meta).keyPath)) } @@ -114,7 +166,7 @@ public extension DynamicObject where Self: CoreStoreObject { let person = CoreStore.fetchAll(From(), Person.orderBy(ascending: { $0.age })) ``` */ - public static func orderBy(ascending attribute: (Self) -> ValueContainer.Optional) -> OrderBy { + public static func orderBy(ascending attribute: (Self) -> ValueContainer.Optional) -> OrderBy { return OrderBy(.ascending(attribute(self.meta).keyPath)) } @@ -125,7 +177,7 @@ public extension DynamicObject where Self: CoreStoreObject { let person = CoreStore.fetchAll(From(), Person.orderBy(descending: { $0.age })) ``` */ - public static func orderBy(descending attribute: (Self) -> ValueContainer.Required) -> OrderBy { + public static func orderBy(descending attribute: (Self) -> ValueContainer.Required) -> OrderBy { return OrderBy(.descending(attribute(self.meta).keyPath)) } @@ -136,22 +188,7 @@ public extension DynamicObject where Self: CoreStoreObject { let person = CoreStore.fetchAll(From(), Person.orderBy(descending: { $0.age })) ``` */ - public static func orderBy(descending attribute: (Self) -> ValueContainer.Optional) -> OrderBy { - - return OrderBy(.descending(attribute(self.meta).keyPath)) - } - - - // MARK: Deprecated - - @available(*, deprecated, renamed: "orderBy(ascending:)") - public static func ascending(_ attribute: (Self) -> ValueContainer.Optional) -> OrderBy { - - return OrderBy(.ascending(attribute(self.meta).keyPath)) - } - - @available(*, deprecated, renamed: "orderBy(descending:)") - public static func descending(_ attribute: (Self) -> ValueContainer.Optional) -> OrderBy { + public static func orderBy(descending attribute: (Self) -> ValueContainer.Optional) -> OrderBy { return OrderBy(.descending(attribute(self.meta).keyPath)) } @@ -169,7 +206,7 @@ public extension ValueContainer.Required { ``` */ @inline(__always) - public static func == (_ attribute: ValueContainer.Required, _ value: V) -> Where { + public static func == (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where(attribute.keyPath, isEqualTo: value) } @@ -181,7 +218,7 @@ public extension ValueContainer.Required { ``` */ @inline(__always) - public static func != (_ attribute: ValueContainer.Required, _ value: V) -> Where { + public static func != (_ attribute: ValueContainer.Required, _ value: V) -> Where { return !Where(attribute.keyPath, isEqualTo: value) } @@ -193,7 +230,7 @@ public extension ValueContainer.Required { ``` */ @inline(__always) - public static func < (_ attribute: ValueContainer.Required, _ value: V) -> Where { + public static func < (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where("%K < %@", attribute.keyPath, value) } @@ -205,7 +242,7 @@ public extension ValueContainer.Required { ``` */ @inline(__always) - public static func > (_ attribute: ValueContainer.Required, _ value: V) -> Where { + public static func > (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where("%K > %@", attribute.keyPath, value) } @@ -217,7 +254,7 @@ public extension ValueContainer.Required { ``` */ @inline(__always) - public static func <= (_ attribute: ValueContainer.Required, _ value: V) -> Where { + public static func <= (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where("%K <= %@", attribute.keyPath, value) } @@ -229,7 +266,7 @@ public extension ValueContainer.Required { ``` */ @inline(__always) - public static func >= (_ attribute: ValueContainer.Required, _ value: V) -> Where { + public static func >= (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where("%K >= %@", attribute.keyPath, value) } @@ -241,7 +278,7 @@ public extension ValueContainer.Required { ``` */ @inline(__always) - public static func ~= (_ sequence: S, _ attribute: ValueContainer.Required) -> Where where S.Iterator.Element == V { + public static func ~= (_ sequence: S, _ attribute: ValueContainer.Required) -> Where where S.Iterator.Element == V { return Where(attribute.keyPath, isMemberOf: sequence) } @@ -259,7 +296,7 @@ public extension ValueContainer.Optional { ``` */ @inline(__always) - public static func == (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { + public static func == (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { return Where(attribute.keyPath, isEqualTo: value) } @@ -271,7 +308,7 @@ public extension ValueContainer.Optional { ``` */ @inline(__always) - public static func != (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { + public static func != (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { return !Where(attribute.keyPath, isEqualTo: value) } @@ -283,7 +320,7 @@ public extension ValueContainer.Optional { ``` */ @inline(__always) - public static func < (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { + public static func < (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { if let value = value { @@ -302,7 +339,7 @@ public extension ValueContainer.Optional { ``` */ @inline(__always) - public static func > (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { + public static func > (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { if let value = value { @@ -321,7 +358,7 @@ public extension ValueContainer.Optional { ``` */ @inline(__always) - public static func <= (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { + public static func <= (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { if let value = value { @@ -340,7 +377,7 @@ public extension ValueContainer.Optional { ``` */ @inline(__always) - public static func >= (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { + public static func >= (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { if let value = value { @@ -359,7 +396,7 @@ public extension ValueContainer.Optional { ``` */ @inline(__always) - public static func ~= (_ sequence: S, _ attribute: ValueContainer.Optional) -> Where where S.Iterator.Element == V { + public static func ~= (_ sequence: S, _ attribute: ValueContainer.Optional) -> Where where S.Iterator.Element == V { return Where(attribute.keyPath, isMemberOf: sequence) } @@ -377,7 +414,7 @@ public extension RelationshipContainer.ToOne { ``` */ @inline(__always) - public static func == (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { + public static func == (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { return Where(relationship.keyPath, isEqualTo: object) } @@ -389,7 +426,7 @@ public extension RelationshipContainer.ToOne { ``` */ @inline(__always) - public static func != (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { + public static func != (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { return !Where(relationship.keyPath, isEqualTo: object) } @@ -401,7 +438,7 @@ public extension RelationshipContainer.ToOne { ``` */ @inline(__always) - public static func ~= (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { + public static func ~= (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { return Where(relationship.keyPath, isEqualTo: object) } @@ -413,7 +450,7 @@ public extension RelationshipContainer.ToOne { ``` */ @inline(__always) - public static func ~= (_ sequence: S, _ relationship: RelationshipContainer.ToOne) -> Where where S.Iterator.Element == D { + public static func ~= (_ sequence: S, _ relationship: RelationshipContainer.ToOne) -> Where where S.Iterator.Element == D { return Where(relationship.keyPath, isMemberOf: sequence) } diff --git a/Sources/DataStack+Observing.swift b/Sources/DataStack+Observing.swift index e4dea00..7777e82 100644 --- a/Sources/DataStack+Observing.swift +++ b/Sources/DataStack+Observing.swift @@ -38,7 +38,7 @@ public extension DataStack { - parameter object: the `DynamicObject` to observe changes from - returns: a `ObjectMonitor` that monitors changes to `object` */ - public func monitorObject(_ object: T) -> ObjectMonitor { + public func monitorObject(_ object: D) -> ObjectMonitor { CoreStore.assert( Thread.isMainThread, @@ -54,7 +54,7 @@ public extension DataStack { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorList(_ from: From, _ fetchClauses: FetchClause...) -> ListMonitor { + public func monitorList(_ from: From, _ fetchClauses: FetchClause...) -> ListMonitor { return self.monitorList(from, fetchClauses) } @@ -66,7 +66,7 @@ public extension DataStack { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorList(_ from: From, _ fetchClauses: [FetchClause]) -> ListMonitor { + public func monitorList(_ from: From, _ fetchClauses: [FetchClause]) -> ListMonitor { CoreStore.assert( Thread.isMainThread, @@ -82,7 +82,7 @@ public extension DataStack { CoreStore.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(cs_typeName(ListMonitor.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." + "An \(cs_typeName(ListMonitor.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." ) } ) @@ -95,7 +95,7 @@ public extension DataStack { - parameter from: a `From` clause indicating the entity type - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: FetchClause...) { + public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: FetchClause...) { self.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses) } @@ -107,7 +107,7 @@ public extension DataStack { - parameter from: a `From` clause indicating the entity type - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: [FetchClause]) { + public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: [FetchClause]) { CoreStore.assert( Thread.isMainThread, @@ -123,7 +123,7 @@ public extension DataStack { CoreStore.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(cs_typeName(ListMonitor.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." + "An \(cs_typeName(ListMonitor.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." ) }, createAsynchronously: createAsynchronously @@ -138,7 +138,7 @@ public extension DataStack { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { + public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { return self.monitorSectionedList(from, sectionBy, fetchClauses) } @@ -151,7 +151,7 @@ public extension DataStack { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { + public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { CoreStore.assert( Thread.isMainThread, @@ -168,7 +168,7 @@ public extension DataStack { CoreStore.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(cs_typeName(ListMonitor.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." + "An \(cs_typeName(ListMonitor.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." ) } ) @@ -182,7 +182,7 @@ public extension DataStack { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } @@ -195,7 +195,7 @@ public extension DataStack { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { CoreStore.assert( Thread.isMainThread, @@ -212,7 +212,7 @@ public extension DataStack { CoreStore.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(cs_typeName(ListMonitor.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." + "An \(cs_typeName(ListMonitor.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." ) }, createAsynchronously: createAsynchronously diff --git a/Sources/DataStack+Querying.swift b/Sources/DataStack+Querying.swift index e87b40d..e600784 100644 --- a/Sources/DataStack+Querying.swift +++ b/Sources/DataStack+Querying.swift @@ -39,7 +39,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter object: a reference to the object created/fetched outside the `DataStack` - returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found. */ - public func fetchExisting(_ object: T) -> T? { + public func fetchExisting(_ object: D) -> D? { return self.mainContext.fetchExisting(object) } @@ -50,7 +50,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter objectID: the `NSManagedObjectID` for the object - returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found. */ - public func fetchExisting(_ objectID: NSManagedObjectID) -> T? { + public func fetchExisting(_ objectID: NSManagedObjectID) -> D? { return self.mainContext.fetchExisting(objectID) } @@ -61,7 +61,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter objects: an array of `DynamicObject`s created/fetched outside the `DataStack` - returns: the `DynamicObject` array for objects that exists in the `DataStack` */ - public func fetchExisting(_ objects: S) -> [T] where S.Iterator.Element == T { + public func fetchExisting(_ objects: S) -> [D] where S.Iterator.Element == D { return self.mainContext.fetchExisting(objects) } @@ -72,7 +72,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter objectIDs: the `NSManagedObjectID` array for the objects - returns: the `DynamicObject` array for objects that exists in the `DataStack` */ - public func fetchExisting(_ objectIDs: S) -> [T] where S.Iterator.Element == NSManagedObjectID { + public func fetchExisting(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID { return self.mainContext.fetchExisting(objectIDs) } @@ -84,7 +84,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s */ - public func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> T? { + public func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> D? { CoreStore.assert( Thread.isMainThread, @@ -100,7 +100,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s */ - public func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> T? { + public func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> D? { CoreStore.assert( Thread.isMainThread, @@ -109,6 +109,16 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchOne(from, fetchClauses) } + // TODO: docs + public func fetchOne(_ clauseChain: B) -> B.ObjectType? { + + CoreStore.assert( + Thread.isMainThread, + "Attempted to fetch from a \(cs_typeName(self)) outside the main thread." + ) + return self.mainContext.fetchOne(clauseChain) + } + /** Fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -116,7 +126,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s */ - public func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [T]? { + public func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [D]? { CoreStore.assert( Thread.isMainThread, @@ -132,7 +142,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s */ - public func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [T]? { + public func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [D]? { CoreStore.assert( Thread.isMainThread, @@ -141,6 +151,16 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchAll(from, fetchClauses) } + // TODO: docs + public func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { + + CoreStore.assert( + Thread.isMainThread, + "Attempted to fetch from a \(cs_typeName(self)) outside the main thread." + ) + return self.mainContext.fetchAll(clauseChain) + } + /** Fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -148,7 +168,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s */ - public func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? { + public func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? { CoreStore.assert( Thread.isMainThread, @@ -164,7 +184,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s */ - public func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? { + public func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? { CoreStore.assert( Thread.isMainThread, @@ -173,6 +193,16 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchCount(from, fetchClauses) } + // TODO: docs + public func fetchCount(_ clauseChain: B) -> Int? { + + CoreStore.assert( + Thread.isMainThread, + "Attempted to fetch from a \(cs_typeName(self)) outside the main thread." + ) + return self.mainContext.fetchCount(clauseChain) + } + /** Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -180,7 +210,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s */ - public func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { + public func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { CoreStore.assert( Thread.isMainThread, @@ -196,7 +226,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s */ - public func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { + public func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { CoreStore.assert( Thread.isMainThread, @@ -205,6 +235,16 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchObjectID(from, fetchClauses) } + // TODO: docs + public func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? { + + CoreStore.assert( + Thread.isMainThread, + "Attempted to fetch from a \(cs_typeName(self)) outside the main thread." + ) + return self.mainContext.fetchObjectID(clauseChain) + } + /** Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -212,7 +252,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s */ - public func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { + public func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { CoreStore.assert( Thread.isMainThread, @@ -228,7 +268,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s */ - public func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { + public func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { CoreStore.assert( Thread.isMainThread, @@ -237,6 +277,16 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchObjectIDs(from, fetchClauses) } + // TODO: docs + public func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? { + + CoreStore.assert( + Thread.isMainThread, + "Attempted to fetch from a \(cs_typeName(self)) outside the main thread." + ) + return self.mainContext.fetchObjectIDs(clauseChain) + } + // MARK: QueryableSource @@ -250,7 +300,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter 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? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { CoreStore.assert( Thread.isMainThread, @@ -269,7 +319,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter 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? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { CoreStore.assert( Thread.isMainThread, @@ -288,7 +338,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter 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...) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { CoreStore.assert( Thread.isMainThread, @@ -307,7 +357,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter 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]) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { CoreStore.assert( Thread.isMainThread, diff --git a/Sources/DynamicObject.swift b/Sources/DynamicObject.swift index c48bdba..664d013 100644 --- a/Sources/DynamicObject.swift +++ b/Sources/DynamicObject.swift @@ -79,9 +79,9 @@ extension NSManagedObject: DynamicObject { public class func cs_fromRaw(object: NSManagedObject) -> Self { @inline(__always) - func forceCast(_ value: Any) -> T { + func forceCast(_ value: Any) -> D { - return value as! T + return value as! D } return forceCast(object) } @@ -125,9 +125,9 @@ extension CoreStoreObject { if let coreStoreObject = object.coreStoreObject { @inline(__always) - func forceCast(_ value: CoreStoreObject) -> T { + func forceCast(_ value: CoreStoreObject) -> D { - return value as! T + return value as! D } return forceCast(coreStoreObject) } diff --git a/Sources/FetchCondition.swift b/Sources/FetchCondition.swift deleted file mode 100644 index ac05514..0000000 --- a/Sources/FetchCondition.swift +++ /dev/null @@ -1,143 +0,0 @@ -// -// FetchCondition.swift -// CoreStore -// -// Copyright © 2017 John Rommel Estropia -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -import Foundation -import CoreData - -public struct ChainedClauseBuilder { - - public let from: From - public let fetchClauses: [FetchClause] = [] - - internal init(from: From) { - - self.from = from - } -} - -extension From: ClauseChain { - - public typealias ObjectType = T - public typealias TraitType = FetchTrait - - public var builder: ChainedClauseBuilder { - - return .init(from: self) - } -} - -public struct ChainedWhere: ClauseChain { - - public typealias ObjectType = D - public typealias TraitType = T - - public let builder: ChainedClauseBuilder - - fileprivate init(builder: ChainedClauseBuilder) { - - var newBuilder = builder -// newBuilder.fetchClauses.append(Where()) - self.builder = newBuilder - } -} - -public struct ChainedOrderBy: ClauseChain { - - public typealias ObjectType = D - public typealias TraitType = T - - public let builder: ChainedClauseBuilder - - fileprivate init(builder: ChainedClauseBuilder) { - - var newBuilder = builder -// newBuilder.fetchClauses.append(Where()) - self.builder = newBuilder - } -} - -public struct ChainedSelect: ClauseChain { - - public typealias ObjectType = D - public typealias TraitType = T - - public let builder: ChainedClauseBuilder - - fileprivate init(builder: ChainedClauseBuilder) { - - var newBuilder = builder -// newBuilder.fetchClauses.append(Where()) - self.builder = newBuilder - } -} - - - - - -public protocol ClauseTrait {} -public enum FetchTrait: ClauseTrait {} -public enum QueryTrait: ClauseTrait {} -public enum SectionTrait: ClauseTrait {} - - -public protocol ClauseChain { - - associatedtype ObjectType: DynamicObject - associatedtype TraitType: ClauseTrait - - var builder: ChainedClauseBuilder { get } -} - -public extension ClauseChain where Self.TraitType == FetchTrait { - - public func `where`() -> ChainedWhere { - - return .init(builder: self.builder) - } - - public func orderBy() -> ChainedOrderBy { - - return .init(builder: self.builder) - } - - public func select() -> ChainedSelect { - - return .init(builder: self.builder) - } -} - -public extension ClauseChain where Self.TraitType == QueryTrait { - - public func `where`() -> ChainedWhere { - - return .init(builder: self.builder) - } - - public func orderBy() -> ChainedOrderBy { - - return .init(builder: self.builder) - } -} diff --git a/Sources/FetchableSource.swift b/Sources/FetchableSource.swift index 12f677a..ce63ef3 100644 --- a/Sources/FetchableSource.swift +++ b/Sources/FetchableSource.swift @@ -40,7 +40,7 @@ public protocol FetchableSource: class { - parameter object: a reference to the object created/fetched outside the `FetchableSource`'s context - returns: the `DynamicObject` instance if the object exists in the `FetchableSource`'s context, or `nil` if not found. */ - func fetchExisting(_ object: T) -> T? + func fetchExisting(_ object: D) -> D? /** Fetches the `DynamicObject` instance in the `FetchableSource`'s context from an `NSManagedObjectID`. @@ -48,7 +48,7 @@ public protocol FetchableSource: class { - parameter objectID: the `NSManagedObjectID` for the object - returns: the `DynamicObject` instance if the object exists in the `FetchableSource`, or `nil` if not found. */ - func fetchExisting(_ objectID: NSManagedObjectID) -> T? + func fetchExisting(_ objectID: NSManagedObjectID) -> D? /** Fetches the `DynamicObject` instances in the `FetchableSource`'s context from references created from another managed object context. @@ -56,7 +56,7 @@ public protocol FetchableSource: class { - parameter objects: an array of `DynamicObject`s created/fetched outside the `FetchableSource`'s context - returns: the `DynamicObject` array for objects that exists in the `FetchableSource` */ - func fetchExisting(_ objects: S) -> [T] where S.Iterator.Element == T + func fetchExisting(_ objects: S) -> [D] where S.Iterator.Element == D /** Fetches the `DynamicObject` instances in the `FetchableSource`'s context from a list of `NSManagedObjectID`. @@ -64,7 +64,7 @@ public protocol FetchableSource: class { - parameter objectIDs: the `NSManagedObjectID` array for the objects - returns: the `DynamicObject` array for objects that exists in the `FetchableSource`'s context */ - func fetchExisting(_ objectIDs: S) -> [T] where S.Iterator.Element == NSManagedObjectID + func fetchExisting(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID /** Fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -73,7 +73,7 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s */ - func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> T? + func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> D? /** Fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -82,7 +82,10 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s */ - func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> T? + func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> D? + + // TODO: docs + func fetchOne(_ clauseChain: B) -> B.ObjectType? /** Fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -91,7 +94,7 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s */ - func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [T]? + func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [D]? /** Fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -100,7 +103,10 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s */ - func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [T]? + func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [D]? + + // TODO: docs + func fetchAll(_ clauseChain: B) -> [B.ObjectType]? /** Fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -109,7 +115,7 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s */ - func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? + func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? /** Fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -118,7 +124,10 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s */ - func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? + func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? + + // TODO: docs + func fetchCount(_ clauseChain: B) -> Int? /** Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -127,7 +136,7 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s */ - func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? + func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? /** Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -136,7 +145,10 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s */ - func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? + func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? + + // TODO: docs + func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? /** Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -145,7 +157,7 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s */ - func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? + func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? /** Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -154,7 +166,10 @@ public protocol FetchableSource: class { - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s */ - func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? + func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? + + // TODO: docs + func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? /** The internal `NSManagedObjectContext` managed by this `FetchableSource`. Using this context directly should typically be avoided, and is provided by CoreStore only for extremely specialized cases. diff --git a/Sources/From.swift b/Sources/From.swift index 6a746fa..67ec6fe 100644 --- a/Sources/From.swift +++ b/Sources/From.swift @@ -39,12 +39,12 @@ import CoreData let person = transaction.fetchOne(From("Configuration1")) ``` */ -public struct From { +public struct From { /** The associated `NSManagedObject` or `CoreStoreObject` entity class */ - public let entityClass: T.Type + public let entityClass: D.Type /** The `NSPersistentStore` configuration names to associate objects from. @@ -60,7 +60,7 @@ public struct From { */ public init() { - self.init(entityClass: T.self, configurations: nil) + self.init(entityClass: D.self, configurations: nil) } /** @@ -70,7 +70,7 @@ public struct From { ``` - parameter entity: the associated `NSManagedObject` or `CoreStoreObject` type */ - public init(_ entity: T.Type) { + public init(_ entity: D.Type) { self.init(entityClass: entity, configurations: nil) } @@ -85,7 +85,7 @@ public struct From { */ public init(_ configuration: ModelConfiguration, _ otherConfigurations: ModelConfiguration...) { - self.init(entityClass: T.self, configurations: [configuration] + otherConfigurations) + self.init(entityClass: D.self, configurations: [configuration] + otherConfigurations) } /** @@ -97,7 +97,7 @@ public struct From { */ public init(_ configurations: [ModelConfiguration]) { - self.init(entityClass: T.self, configurations: configurations) + self.init(entityClass: D.self, configurations: configurations) } /** @@ -109,7 +109,7 @@ public struct From { - parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject` or `CoreStoreObject`'s entity type. Set to `nil` to use the default configuration. - parameter otherConfigurations: an optional list of other configuration names to associate objects from (see `configuration` parameter) */ - public init(_ entity: T.Type, _ configuration: ModelConfiguration, _ otherConfigurations: ModelConfiguration...) { + public init(_ entity: D.Type, _ configuration: ModelConfiguration, _ otherConfigurations: ModelConfiguration...) { self.init(entityClass: entity, configurations: [configuration] + otherConfigurations) } @@ -122,7 +122,7 @@ public struct From { - parameter entity: the associated `NSManagedObject` or `CoreStoreObject` type - parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject` or `CoreStoreObject`'s entity type. Set to `nil` to use the default configuration. */ - public init(_ entity: T.Type, _ configurations: [ModelConfiguration]) { + public init(_ entity: D.Type, _ configurations: [ModelConfiguration]) { self.init(entityClass: entity, configurations: configurations) } @@ -132,7 +132,7 @@ public struct From { internal let findPersistentStores: (_ context: NSManagedObjectContext) -> [NSPersistentStore]? - internal init(entityClass: T.Type, configurations: [ModelConfiguration]?, findPersistentStores: @escaping (_ context: NSManagedObjectContext) -> [NSPersistentStore]?) { + internal init(entityClass: D.Type, configurations: [ModelConfiguration]?, findPersistentStores: @escaping (_ context: NSManagedObjectContext) -> [NSPersistentStore]?) { self.entityClass = entityClass self.configurations = configurations @@ -167,7 +167,7 @@ public struct From { // MARK: Private - private init(entityClass: T.Type, configurations: [ModelConfiguration]?) { + private init(entityClass: D.Type, configurations: [ModelConfiguration]?) { self.entityClass = entityClass self.configurations = configurations diff --git a/Sources/Into.swift b/Sources/Into.swift index 8730af9..f016a05 100644 --- a/Sources/Into.swift +++ b/Sources/Into.swift @@ -39,12 +39,12 @@ import CoreData let person = transaction.create(Into("Configuration1")) ``` */ -public struct Into: Hashable { +public struct Into: Hashable { /** The associated `NSManagedObject` or `CoreStoreObject` entity class */ - public let entityClass: T.Type + public let entityClass: D.Type /** The `NSPersistentStore` configuration name to associate objects from. @@ -60,7 +60,7 @@ public struct Into: Hashable { */ public init() { - self.init(entityClass: T.self, configuration: nil, inferStoreIfPossible: true) + self.init(entityClass: D.self, configuration: nil, inferStoreIfPossible: true) } /** @@ -70,7 +70,7 @@ public struct Into: Hashable { ``` - parameter entity: the `NSManagedObject` type to be created */ - public init(_ entity: T.Type) { + public init(_ entity: D.Type) { self.init(entityClass: entity, configuration: nil, inferStoreIfPossible: true) } @@ -84,7 +84,7 @@ public struct Into: Hashable { */ public init(_ configuration: ModelConfiguration) { - self.init(entityClass: T.self, configuration: configuration, inferStoreIfPossible: false) + self.init(entityClass: D.self, configuration: configuration, inferStoreIfPossible: false) } /** @@ -95,7 +95,7 @@ public struct Into: Hashable { - parameter entity: the `NSManagedObject` type to be created - parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration. */ - public init(_ entity: T.Type, _ configuration: ModelConfiguration) { + public init(_ entity: D.Type, _ configuration: ModelConfiguration) { self.init(entityClass: entity, configuration: configuration, inferStoreIfPossible: false) } @@ -125,7 +125,7 @@ public struct Into: Hashable { internal let inferStoreIfPossible: Bool - internal init(entityClass: T.Type, configuration: ModelConfiguration, inferStoreIfPossible: Bool) { + internal init(entityClass: D.Type, configuration: ModelConfiguration, inferStoreIfPossible: Bool) { self.entityClass = entityClass self.configuration = configuration diff --git a/Sources/NSFetchedResultsController+Convenience.swift b/Sources/NSFetchedResultsController+Convenience.swift index d4d7252..3d1c552 100644 --- a/Sources/NSFetchedResultsController+Convenience.swift +++ b/Sources/NSFetchedResultsController+Convenience.swift @@ -42,7 +42,7 @@ public extension DataStack { - returns: an `NSFetchedResultsController` that observes the `DataStack` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { return createFRC( fromContext: self.mainContext, @@ -62,7 +62,7 @@ public extension DataStack { - returns: an `NSFetchedResultsController` that observes the `DataStack` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { return createFRC( fromContext: self.mainContext, @@ -81,7 +81,7 @@ public extension DataStack { - returns: an `NSFetchedResultsController` that observes the `DataStack` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { return createFRC( fromContext: self.mainContext, @@ -100,7 +100,7 @@ public extension DataStack { - returns: an `NSFetchedResultsController` that observes the `DataStack` */ @nonobjc - public func createFetchedResultsController(forDataStack dataStack: DataStack, _ from: From, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { + public func createFetchedResultsController(forDataStack dataStack: DataStack, _ from: From, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { return createFRC( fromContext: self.mainContext, @@ -127,7 +127,7 @@ public extension UnsafeDataTransaction { - returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { return createFRC( fromContext: self.context, @@ -147,7 +147,7 @@ public extension UnsafeDataTransaction { - returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { return createFRC( fromContext: self.context, @@ -166,7 +166,7 @@ public extension UnsafeDataTransaction { - returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { return createFRC( fromContext: self.context, @@ -185,7 +185,7 @@ public extension UnsafeDataTransaction { - returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { return createFRC( fromContext: self.context, @@ -201,7 +201,7 @@ public extension UnsafeDataTransaction { // MARK: - Private @available(OSX 10.12, *) -fileprivate func createFRC(fromContext context: NSManagedObjectContext, from: From, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController { +fileprivate func createFRC(fromContext context: NSManagedObjectContext, from: From, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController { let controller = CoreStoreFetchedResultsController( context: context, @@ -214,7 +214,7 @@ fileprivate func createFRC(fromContext context: NSManagedObj CoreStore.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(cs_typeName(NSFetchedResultsController.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." + "An \(cs_typeName(NSFetchedResultsController.self)) requires a sort information. Specify from a \(cs_typeName(OrderBy.self)) clause or any custom \(cs_typeName(FetchClause.self)) that provides a sort descriptor." ) } ) diff --git a/Sources/NSManagedObjectContext+Querying.swift b/Sources/NSManagedObjectContext+Querying.swift index 11e58e6..c9a2d31 100644 --- a/Sources/NSManagedObjectContext+Querying.swift +++ b/Sources/NSManagedObjectContext+Querying.swift @@ -34,7 +34,7 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { // MARK: FetchableSource @nonobjc - public func fetchExisting(_ object: T) -> T? { + public func fetchExisting(_ object: D) -> D? { let rawObject = object.cs_toRaw() if rawObject.objectID.isTemporaryID { @@ -62,7 +62,7 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return object } - return T.cs_fromRaw(object: existingRawObject) + return D.cs_fromRaw(object: existingRawObject) } catch { @@ -75,12 +75,12 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { } @nonobjc - public func fetchExisting(_ objectID: NSManagedObjectID) -> T? { + public func fetchExisting(_ objectID: NSManagedObjectID) -> D? { do { let existingObject = try self.existingObject(with: objectID) - return T.cs_fromRaw(object: existingObject) + return D.cs_fromRaw(object: existingObject) } catch _ { @@ -89,25 +89,25 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { } @nonobjc - public func fetchExisting(_ objects: S) -> [T] where S.Iterator.Element == T { + public func fetchExisting(_ objects: S) -> [D] where S.Iterator.Element == D { return objects.flatMap({ self.fetchExisting($0.cs_id()) }) } @nonobjc - public func fetchExisting(_ objectIDs: S) -> [T] where S.Iterator.Element == NSManagedObjectID { + public func fetchExisting(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID { return objectIDs.flatMap({ self.fetchExisting($0) }) } @nonobjc - public func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> T? { + public func fetchOne(_ from: From, _ fetchClauses: FetchClause...) -> D? { return self.fetchOne(from, fetchClauses) } @nonobjc - public func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> T? { + public func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> D? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) @@ -123,14 +123,21 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.fetchOne(fetchRequest.dynamicCast()).flatMap(from.entityClass.cs_fromRaw) } + // TODO: docs @nonobjc - public func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [T]? { + public func fetchOne(_ clauseChain: B) -> B.ObjectType? { + + return self.fetchOne(clauseChain.from, clauseChain.fetchClauses) + } + + @nonobjc + public func fetchAll(_ from: From, _ fetchClauses: FetchClause...) -> [D]? { return self.fetchAll(from, fetchClauses) } @nonobjc - public func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [T]? { + public func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [D]? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) @@ -147,14 +154,21 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.fetchAll(fetchRequest.dynamicCast())?.map(entityClass.cs_fromRaw) } + // TODO: docs @nonobjc - public func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? { + public func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { + + return self.fetchAll(clauseChain.from, clauseChain.fetchClauses) + } + + @nonobjc + public func fetchCount(_ from: From, _ fetchClauses: FetchClause...) -> Int? { return self.fetchCount(from, fetchClauses) } @nonobjc - public func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? { + public func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) @@ -167,14 +181,21 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.fetchCount(fetchRequest.dynamicCast()) } + // TODO: docs @nonobjc - public func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { + public func fetchCount(_ clauseChain: B) -> Int? { + + return self.fetchCount(clauseChain.from, clauseChain.fetchClauses) + } + + @nonobjc + public func fetchObjectID(_ from: From, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { return self.fetchObjectID(from, fetchClauses) } @nonobjc - public func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { + public func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) @@ -190,14 +211,21 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.fetchObjectID(fetchRequest.dynamicCast()) } + // TODO: docs @nonobjc - public func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { + public func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? { + + return self.fetchObjectID(clauseChain.from, clauseChain.fetchClauses) + } + + @nonobjc + public func fetchObjectIDs(_ from: From, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { return self.fetchObjectIDs(from, fetchClauses) } @nonobjc - public func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { + public func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) @@ -213,6 +241,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.fetchObjectIDs(fetchRequest.dynamicCast()) } + // TODO: docs + @nonobjc + public func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? { + + return self.fetchObjectIDs(clauseChain.from, clauseChain.fetchClauses) + } + @nonobjc internal func fetchObjectIDs(_ fetchRequest: NSFetchRequest) -> [NSManagedObjectID]? { @@ -244,13 +279,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { // MARK: QueryableSource @nonobjc - public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { return self.queryValue(from, selectClause, queryClauses) } @nonobjc - public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) @@ -269,13 +304,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { } @nonobjc - public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { return self.queryAttributes(from, selectClause, queryClauses) } @nonobjc - public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) @@ -305,13 +340,7 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { // MARK: Deleting @nonobjc - internal func deleteAll(_ from: From, _ deleteClauses: DeleteClause...) -> Int? { - - return self.deleteAll(from, deleteClauses) - } - - @nonobjc - internal func deleteAll(_ from: From, _ deleteClauses: [DeleteClause]) -> Int? { + internal func deleteAll(_ from: From, _ deleteClauses: [FetchClause]) -> Int? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) @@ -347,9 +376,9 @@ internal extension NSManagedObjectContext { // MARK: Fetching @nonobjc - internal func fetchOne(_ fetchRequest: NSFetchRequest) -> T? { + internal func fetchOne(_ fetchRequest: NSFetchRequest) -> D? { - var fetchResults: [T]? + var fetchResults: [D]? var fetchError: Error? self.performAndWait { @@ -374,9 +403,9 @@ internal extension NSManagedObjectContext { } @nonobjc - internal func fetchAll(_ fetchRequest: NSFetchRequest) -> [T]? { + internal func fetchAll(_ fetchRequest: NSFetchRequest) -> [D]? { - var fetchResults: [T]? + var fetchResults: [D]? var fetchError: Error? self.performAndWait { @@ -555,7 +584,7 @@ internal extension NSManagedObjectContext { // MARK: Deleting @nonobjc - internal func deleteAll(_ fetchRequest: NSFetchRequest) -> Int? { + internal func deleteAll(_ fetchRequest: NSFetchRequest) -> Int? { var numberOfDeletedObjects: Int? var fetchError: Error? diff --git a/Sources/ObjectMonitor.swift b/Sources/ObjectMonitor.swift index b2d8db1..79102c7 100644 --- a/Sources/ObjectMonitor.swift +++ b/Sources/ObjectMonitor.swift @@ -273,7 +273,7 @@ public final class ObjectMonitor: Equatable { context: context, fetchRequest: fetchRequest.dynamicCast(), from: From([objectID.persistentStore?.configurationName]), - applyFetchClauses: Where("SELF", isEqualTo: objectID).applyToFetchRequest + applyFetchClauses: Where("SELF", isEqualTo: objectID).applyToFetchRequest ) let fetchedResultsControllerDelegate = FetchedResultsControllerDelegate() diff --git a/Sources/OrderBy.swift b/Sources/OrderBy.swift index 71f5e3e..33611e7 100644 --- a/Sources/OrderBy.swift +++ b/Sources/OrderBy.swift @@ -32,31 +32,12 @@ import CoreData public typealias KeyPathString = String -// MARK: - SortKey - -/** - The `SortKey` is passed to the `OrderBy` clause to indicate the sort keys and their sort direction. - */ -public enum SortKey { - - /** - Indicates that the `KeyPathString` should be sorted in ascending order - */ - case ascending(KeyPathString) - - /** - Indicates that the `KeyPathString` should be sorted in descending order - */ - case descending(KeyPathString) -} - - // MARK: - OrderBy /** The `OrderBy` clause specifies the sort order for results for a fetch or a query. */ -public struct OrderBy: FetchClause, QueryClause, DeleteClause, Hashable { +public struct OrderBy: OrderByClause, FetchClause, QueryClause, DeleteClause, Hashable { /** Combines two `OrderBy` sort descriptors together @@ -74,11 +55,6 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause, Hashable { left = left + right } - /** - The list of sort descriptors - */ - public let sortDescriptors: [NSSortDescriptor] - /** Initializes a `OrderBy` clause with an empty list of sort descriptors */ @@ -112,21 +88,9 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause, Hashable { - parameter sortKey: a series of `SortKey`s */ - public init(_ sortKey: [SortKey]) { + public init(_ sortKeys: [SortKey]) { - self.init( - sortKey.map { sortKey -> NSSortDescriptor in - - switch sortKey { - - case .ascending(let keyPath): - return NSSortDescriptor(key: keyPath, ascending: true) - - case .descending(let keyPath): - return NSSortDescriptor(key: keyPath, ascending: false) - } - } - ) + self.init(sortKeys.map({ $0.descriptor })) } /** @@ -141,6 +105,13 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause, Hashable { } + // MARK: OrderByClause + + public typealias ObjectType = D + + public let sortDescriptors: [NSSortDescriptor] + + // MARK: FetchClause, QueryClause, DeleteClause public func applyToFetchRequest(_ fetchRequest: NSFetchRequest) { @@ -171,17 +142,154 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause, Hashable { return (self.sortDescriptors as NSArray).hashValue } + + + // MARK: - SortKey + + /** + The `SortKey` is passed to the `OrderBy` clause to indicate the sort keys and their sort direction. + */ + public struct SortKey { + + // MARK: Raw Key Paths + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ keyPath: KeyPathString) -> SortKey { + + return SortKey(descriptor: .init(key: keyPath, ascending: true)) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ keyPath: KeyPathString) -> SortKey { + + return SortKey(descriptor: .init(key: keyPath, ascending: false)) + } + + + // MARK: NSManagedObject Key Paths + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ keyPath: KeyPath) -> SortKey where D: NSManagedObject { + + return .ascending(keyPath._kvcKeyPathString!) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ keyPath: KeyPath) -> SortKey where D: NSManagedObject { + + return .descending(keyPath._kvcKeyPathString!) + } + + + // MARK: CoreStoreObject Key Paths + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ attribute: KeyPath) -> SortKey where A: ValueContainer.Required { + + return .ascending(D.meta[keyPath: attribute].keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ attribute: KeyPath) -> SortKey where A: ValueContainer.Optional { + + return .ascending(D.meta[keyPath: attribute].keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ attribute: KeyPath) -> SortKey where A: TransformableContainer.Required { + + return .ascending(D.meta[keyPath: attribute].keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ attribute: KeyPath) -> SortKey where A: TransformableContainer.Optional { + + return .ascending(D.meta[keyPath: attribute].keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ attribute: KeyPath) -> SortKey where A: ValueContainer.Required { + + return .descending(D.meta[keyPath: attribute].keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ attribute: KeyPath) -> SortKey where A: ValueContainer.Optional { + + return .descending(D.meta[keyPath: attribute].keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ attribute: KeyPath) -> SortKey where A: TransformableContainer.Required { + + return .descending(D.meta[keyPath: attribute].keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ attribute: KeyPath) -> SortKey where A: TransformableContainer.Optional { + + return .descending(D.meta[keyPath: attribute].keyPath) + } + + + // MARK: Private + + fileprivate let descriptor: NSSortDescriptor + } } -// MARK: - Sequence where Element == OrderBy +// MARK: - OrderByClause -public extension Sequence where Iterator.Element == OrderBy { +/** + Abstracts the `OrderBy` clause for protocol utilities. + */ +public protocol OrderByClause { + + /** + The `DynamicObject` type associated with the clause + */ + associatedtype ObjectType: DynamicObject + + /** + The `NSSortDescriptor` array for the fetch or query + */ + var sortDescriptors: [NSSortDescriptor] { get } +} + + +// MARK: - Sequence where Iterator.Element: OrderByClause + +public extension Sequence where Iterator.Element: OrderByClause { /** Combines multiple `OrderBy` predicates together */ - public func combined() -> OrderBy { + public func combined() -> OrderBy { return OrderBy(self.flatMap({ $0.sortDescriptors })) } diff --git a/Sources/QueryableSource.swift b/Sources/QueryableSource.swift index 4b40a42..a844827 100644 --- a/Sources/QueryableSource.swift +++ b/Sources/QueryableSource.swift @@ -44,7 +44,7 @@ public protocol QueryableSource: class { - parameter 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. */ - func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? + func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? /** Queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. @@ -56,7 +56,7 @@ public protocol QueryableSource: class { - parameter 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. */ - func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? + func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? /** 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. @@ -68,7 +68,7 @@ public protocol QueryableSource: class { - parameter 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. */ - func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? + func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? /** 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. @@ -80,7 +80,7 @@ public protocol QueryableSource: class { - parameter 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. */ - func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? + func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? /** The internal `NSManagedObjectContext` managed by this `QueryableSource`. Using this context directly should typically be avoided, and is provided by CoreStore only for extremely specialized cases. diff --git a/Sources/SynchronousDataTransaction.swift b/Sources/SynchronousDataTransaction.swift index 7f5d3d8..8176dfd 100644 --- a/Sources/SynchronousDataTransaction.swift +++ b/Sources/SynchronousDataTransaction.swift @@ -55,7 +55,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { - parameter into: the `Into` clause indicating the destination `NSManagedObject` or `CoreStoreObject` entity type and the destination configuration - returns: a new `NSManagedObject` or `CoreStoreObject` instance of the specified entity type. */ - public override func create(_ into: Into) -> T { + public override func create(_ into: Into) -> D { CoreStore.assert( !self.isCommitted, @@ -71,7 +71,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { - parameter object: the `NSManagedObject` or `CoreStoreObject` to be edited - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`. */ - public override func edit(_ object: T?) -> T? { + public override func edit(_ object: D?) -> D? { CoreStore.assert( !self.isCommitted, @@ -88,7 +88,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { - parameter objectID: the `NSManagedObjectID` for the object to be edited - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`. */ - public override func edit(_ into: Into, _ objectID: NSManagedObjectID) -> T? { + public override func edit(_ into: Into, _ objectID: NSManagedObjectID) -> D? { CoreStore.assert( !self.isCommitted, @@ -103,7 +103,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { - parameter object: the `NSManagedObject` or `CoreStoreObject` type to be deleted */ - public override func delete(_ object: T?) { + public override func delete(_ object: D?) { CoreStore.assert( !self.isCommitted, @@ -120,7 +120,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { - parameter object2: another `DynamicObject` to be deleted - parameter objects: other `DynamicObject`s to be deleted */ - public override func delete(_ object1: T?, _ object2: T?, _ objects: T?...) { + public override func delete(_ object1: D?, _ object2: D?, _ objects: D?...) { CoreStore.assert( !self.isCommitted, diff --git a/Sources/UnsafeDataTransaction+Observing.swift b/Sources/UnsafeDataTransaction+Observing.swift index 02cbd03..181f28b 100644 --- a/Sources/UnsafeDataTransaction+Observing.swift +++ b/Sources/UnsafeDataTransaction+Observing.swift @@ -38,7 +38,7 @@ public extension UnsafeDataTransaction { - parameter object: the `DynamicObject` to observe changes from - returns: a `ObjectMonitor` that monitors changes to `object` */ - public func monitorObject(_ object: T) -> ObjectMonitor { + public func monitorObject(_ object: D) -> ObjectMonitor { return ObjectMonitor( unsafeTransaction: self, @@ -53,7 +53,7 @@ public extension UnsafeDataTransaction { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorList(_ from: From, _ fetchClauses: FetchClause...) -> ListMonitor { + public func monitorList(_ from: From, _ fetchClauses: FetchClause...) -> ListMonitor { return self.monitorList(from, fetchClauses) } @@ -65,10 +65,10 @@ public extension UnsafeDataTransaction { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorList(_ from: From, _ fetchClauses: [FetchClause]) -> ListMonitor { + public func monitorList(_ from: From, _ fetchClauses: [FetchClause]) -> ListMonitor { CoreStore.assert( - fetchClauses.filter { $0 is OrderBy }.count > 0, + fetchClauses.filter { $0 is OrderBy }.count > 0, "A ListMonitor requires an OrderBy clause." ) @@ -90,7 +90,7 @@ public extension UnsafeDataTransaction { - parameter from: a `From` clause indicating the entity type - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: FetchClause...) { + public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: FetchClause...) { self.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses) } @@ -102,10 +102,10 @@ public extension UnsafeDataTransaction { - parameter from: a `From` clause indicating the entity type - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: [FetchClause]) { + public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ fetchClauses: [FetchClause]) { CoreStore.assert( - fetchClauses.filter { $0 is OrderBy }.count > 0, + fetchClauses.filter { $0 is OrderBy }.count > 0, "A ListMonitor requires an OrderBy clause." ) @@ -129,7 +129,7 @@ public extension UnsafeDataTransaction { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { + public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { return self.monitorSectionedList(from, sectionBy, fetchClauses) } @@ -142,10 +142,10 @@ public extension UnsafeDataTransaction { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { + public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { CoreStore.assert( - fetchClauses.filter { $0 is OrderBy }.count > 0, + fetchClauses.filter { $0 is OrderBy }.count > 0, "A ListMonitor requires an OrderBy clause." ) @@ -168,7 +168,7 @@ public extension UnsafeDataTransaction { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } @@ -181,10 +181,10 @@ public extension UnsafeDataTransaction { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { CoreStore.assert( - fetchClauses.filter { $0 is OrderBy }.count > 0, + fetchClauses.filter { $0 is OrderBy }.count > 0, "A ListMonitor requires an OrderBy clause." ) diff --git a/Sources/Where+NSManagedObject.swift b/Sources/Where+NSManagedObject.swift new file mode 100644 index 0000000..ef73499 --- /dev/null +++ b/Sources/Where+NSManagedObject.swift @@ -0,0 +1,220 @@ +// +// Where+NSManagedObject.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - KeyPath where Root: NSManagedObject, Value: QueryableAttributeType & Equatable + +public extension KeyPath where Root: NSManagedObject, Value: QueryableAttributeType & Equatable { + + public static func == (_ keyPath: KeyPath, _ value: Value) -> Where { + + return Where(keyPath._kvcKeyPathString!, isEqualTo: value) + } + + public static func != (_ keyPath: KeyPath, _ value: Value) -> Where { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) + } + + public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == Value { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) + } +} + +// MARK: - KeyPath where Root: NSManagedObject, Value: Optional + +public extension KeyPath where Root: NSManagedObject { + + public static func == (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + + return Where(keyPath._kvcKeyPathString!, isEqualTo: value) + } + + public static func != (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) + } + + public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where Value == Optional, S.Iterator.Element == V { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) + } +} + + +// MARK: - KeyPath where Root: NSManagedObject, Value: QueryableAttributeType + +public extension KeyPath where Root: NSManagedObject, Value: QueryableAttributeType { + + public static func < (_ keyPath: KeyPath, _ value: Value) -> Where { + + return Where("%K < %@", keyPath._kvcKeyPathString!, value) + } + + public static func > (_ keyPath: KeyPath, _ value: Value) -> Where { + + return Where("%K > %@", keyPath._kvcKeyPathString!, value) + } + + public static func <= (_ keyPath: KeyPath, _ value: Value) -> Where { + + return Where("%K <= %@", keyPath._kvcKeyPathString!, value) + } + + public static func >= (_ keyPath: KeyPath, _ value: Value) -> Where { + + return Where("%K >= %@", keyPath._kvcKeyPathString!, value) + } +} + + +// MARK: - KeyPath where Root: NSManagedObject, Value: Optional + +public extension KeyPath where Root: NSManagedObject { + + public static func < (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + + if let value = value { + + return Where("%K < %@", keyPath._kvcKeyPathString!, value) + } + else { + + return Where("%K < nil", keyPath._kvcKeyPathString!) + } + } + + public static func > (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + + if let value = value { + + return Where("%K > %@", keyPath._kvcKeyPathString!, value) + } + else { + + return Where("%K > nil", keyPath._kvcKeyPathString!) + } + } + + public static func <= (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + + if let value = value { + + return Where("%K <= %@", keyPath._kvcKeyPathString!, value) + } + else { + + return Where("%K <= nil", keyPath._kvcKeyPathString!) + } + } + + public static func >= (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + + if let value = value { + + return Where("%K >= %@", keyPath._kvcKeyPathString!, value) + } + else { + + return Where("%K >= nil", keyPath._kvcKeyPathString!) + } + } +} + + +// MARK: - KeyPath where Root: NSManagedObject, Value: NSManagedObject + +public extension KeyPath where Root: NSManagedObject, Value: NSManagedObject { + + public static func == (_ keyPath: KeyPath, _ object: Value) -> Where { + + return Where(keyPath._kvcKeyPathString!, isEqualTo: object) + } + + public static func != (_ keyPath: KeyPath, _ object: Value) -> Where { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: object) + } + + public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == Value { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) + } + + public static func == (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { + + return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) + } + + public static func != (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) + } + + public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == NSManagedObjectID { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) + } +} + + +// MARK: - KeyPath where Root: NSManagedObject, Value: Optional + +public extension KeyPath where Root: NSManagedObject { + + public static func == (_ keyPath: KeyPath, _ object: Value) -> Where where Value == Optional { + + return Where(keyPath._kvcKeyPathString!, isEqualTo: object) + } + + public static func != (_ keyPath: KeyPath, _ object: Value) -> Where where Value == Optional { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: object) + } + + public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where Value == Optional, S.Iterator.Element == V { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) + } + + public static func == (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where where Value == Optional { + + return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) + } + + public static func != (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where where Value == Optional { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) + } + + public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where Value == Optional, S.Iterator.Element == NSManagedObjectID { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) + } +} diff --git a/Sources/Where.swift b/Sources/Where.swift index 65cea6a..b8b6fdd 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -32,7 +32,7 @@ import CoreData /** The `Where` clause specifies the conditions for a fetch or a query. */ -public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { +public struct Where: WhereClause, FetchClause, QueryClause, DeleteClause, Hashable { /** Combines two `Where` predicates together using `AND` operator @@ -126,11 +126,6 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { return Where(NSCompoundPredicate(type: .not, subpredicates: [clause.predicate])) } - /** - The `NSPredicate` for the fetch or query - */ - public let predicate: NSPredicate - /** Initializes a `Where` clause with a predicate that always evaluates to `true` */ @@ -188,7 +183,7 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { - parameter keyPath: the keyPath to compare with - parameter value: the arguments for the `==` operator */ - public init(_ keyPath: KeyPathString, isEqualTo value: T?) { + public init(_ keyPath: KeyPathString, isEqualTo value: U?) { switch value { @@ -207,12 +202,11 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { - parameter keyPath: the keyPath to compare with - parameter object: the arguments for the `==` operator */ - public init(_ keyPath: KeyPathString, isEqualTo object: T?) { + public init(_ keyPath: KeyPathString, isEqualTo object: D?) { switch object { - case nil, - is NSNull: + case nil: self.init(NSPredicate(format: "\(keyPath) == nil")) case let object?: @@ -220,6 +214,17 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { } } + /** + Initializes a `Where` clause that compares equality + + - parameter keyPath: the keyPath to compare with + - parameter objectID: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPathString, isEqualTo objectID: NSManagedObjectID) { + + self.init(NSPredicate(format: "\(keyPath) == %@", argumentArray: [objectID])) + } + /** Initializes a `Where` clause that compares membership @@ -242,6 +247,17 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { self.init(NSPredicate(format: "\(keyPath) IN %@", list.map({ $0.cs_id() }) as NSArray)) } + /** + Initializes a `Where` clause that compares membership + + - parameter keyPath: the keyPath to compare with + - parameter list: the sequence to check membership of + */ + public init(_ keyPath: KeyPathString, isMemberOf list: S) where S.Iterator.Element: NSManagedObjectID { + + self.init(NSPredicate(format: "\(keyPath) IN %@", list.map({ $0 }) as NSArray)) + } + /** Initializes a `Where` clause with an `NSPredicate` @@ -253,6 +269,13 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { } + // MARK: WhereClause + + public typealias ObjectType = D + + public let predicate: NSPredicate + + // MARK: FetchClause, QueryClause, DeleteClause public func applyToFetchRequest(_ fetchRequest: NSFetchRequest) { @@ -286,14 +309,33 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { } -// MARK: - Sequence where Element == Where +// MARK: - WhereClause -public extension Sequence where Iterator.Element == Where { +/** + Abstracts the `Where` clause for protocol utilities. + */ +public protocol WhereClause { + + /** + The `DynamicObject` type associated with the clause + */ + associatedtype ObjectType: DynamicObject + + /** + The `NSPredicate` for the fetch or query + */ + var predicate: NSPredicate { get } +} + + +// MARK: - Sequence where Iterator.Element: WhereClause + +public extension Sequence where Iterator.Element: WhereClause { /** Combines multiple `Where` predicates together using `AND` operator */ - public func combinedByAnd() -> Where { + public func combinedByAnd() -> Where { return Where(NSCompoundPredicate(type: .and, subpredicates: self.map({ $0.predicate }))) } @@ -301,7 +343,7 @@ public extension Sequence where Iterator.Element == Where { /** Combines multiple `Where` predicates together using `OR` operator */ - public func combinedByOr() -> Where { + public func combinedByOr() -> Where { return Where(NSCompoundPredicate(type: .or, subpredicates: self.map({ $0.predicate }))) } From fcd4be90112b9d9064314ccbe8297097246323b6 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 10 Jul 2017 08:23:59 +0900 Subject: [PATCH 07/56] WIP: chained queries for section monitors --- CoreStore.xcodeproj/project.pbxproj | 2 +- Sources/BaseDataTransaction+Querying.swift | 20 ++++++++++++++++++++ Sources/ChainedClauseBuilder.swift | 14 +++++++------- Sources/DataStack+Querying.swift | 20 ++++++++++++++++++++ Sources/QueryableSource.swift | 6 ++++++ 5 files changed, 54 insertions(+), 8 deletions(-) diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index cd4304e..d2c7b42 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -1392,9 +1392,9 @@ B5E84F061AFF847B0064E85B /* DataStack+Querying.swift */, B5E84F071AFF847B0064E85B /* CoreStore+Querying.swift */, B596BBB51DD5BC67001DCDD9 /* FetchableSource.swift */, - B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */, B596BBBA1DD5C39F001DCDD9 /* QueryableSource.swift */, B549F65D1E569C7400FBAB2D /* QueryableAttributeType.swift */, + B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */, B5A1DAC61F111BBE003CF369 /* KeyPath Utilities */, B5E84F0A1AFF847B0064E85B /* Protocol Clauses */, B5E84EFF1AFF847B0064E85B /* Concrete Clauses */, diff --git a/Sources/BaseDataTransaction+Querying.swift b/Sources/BaseDataTransaction+Querying.swift index 8cc55b1..a5f0b11 100644 --- a/Sources/BaseDataTransaction+Querying.swift +++ b/Sources/BaseDataTransaction+Querying.swift @@ -382,6 +382,16 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.queryValue(from, selectClause, queryClauses) } + // TODO: docs + public func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType { + + CoreStore.assert( + self.isRunningInAllowedQueue(), + "Attempted to query from a \(cs_typeName(self)) outside its designated queue." + ) + return self.context.queryValue(clauseChain.from, clauseChain.select, clauseChain.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. @@ -420,6 +430,16 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.queryAttributes(from, selectClause, queryClauses) } + // TODO: docs + public func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary { + + CoreStore.assert( + self.isRunningInAllowedQueue(), + "Attempted to query from a \(cs_typeName(self)) outside its designated queue." + ) + return self.context.queryAttributes(clauseChain.from, clauseChain.select, clauseChain.queryClauses) + } + // MARK: FetchableSource, QueryableSource diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index df13948..24f0584 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -43,7 +43,7 @@ public protocol QueryChainableBuilderType { var from: From { get set } var select: Select { get set } var groupBy: GroupBy { get set } - var fetchClauses: [FetchClause] { get set } + var queryClauses: [QueryClause] { get set } } public protocol SectionMonitorBuilderType { @@ -81,7 +81,7 @@ public struct QueryChainBuilder: QueryCha public var from: From public var select: Select public var groupBy: GroupBy - public var fetchClauses: [FetchClause] = [] + public var queryClauses: [QueryClause] = [] } @@ -112,7 +112,7 @@ public extension From { from: self, select: .init(selectTerms), groupBy: .init(), - fetchClauses: [] + queryClauses: [] ) } @@ -226,7 +226,7 @@ public extension QueryChainBuilder { from: self.from, select: self.select, groupBy: .init(keyPaths), - fetchClauses: self.fetchClauses + queryClauses: self.queryClauses ) } @@ -255,7 +255,7 @@ public extension QueryChainBuilder { return self.queryChain(appending: Tweak(fetchRequest)) } - public func appending(_ clause: FetchClause) -> QueryChainBuilder { + public func appending(_ clause: QueryClause) -> QueryChainBuilder { return self.queryChain(appending: clause) } @@ -263,13 +263,13 @@ public extension QueryChainBuilder { // MARK: Private - private func queryChain(appending clause: FetchClause) -> QueryChainBuilder { + private func queryChain(appending clause: QueryClause) -> QueryChainBuilder { return .init( from: self.from, select: self.select, groupBy: self.groupBy, - fetchClauses: self.fetchClauses + [clause] + queryClauses: self.queryClauses + [clause] ) } } diff --git a/Sources/DataStack+Querying.swift b/Sources/DataStack+Querying.swift index e600784..a0f527c 100644 --- a/Sources/DataStack+Querying.swift +++ b/Sources/DataStack+Querying.swift @@ -328,6 +328,16 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.queryValue(from, selectClause, queryClauses) } + // TODO: docs + public func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType { + + CoreStore.assert( + Thread.isMainThread, + "Attempted to query from a \(cs_typeName(self)) outside the main thread." + ) + return self.mainContext.queryValue(clauseChain.from, clauseChain.select, clauseChain.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. @@ -366,6 +376,16 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.queryAttributes(from, selectClause, queryClauses) } + // TODO: docs + public func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary { + + CoreStore.assert( + Thread.isMainThread, + "Attempted to query from a \(cs_typeName(self)) outside the main thread." + ) + return self.mainContext.queryAttributes(clauseChain.from, clauseChain.select, clauseChain.queryClauses) + } + // MARK: FetchableSource, QueryableSource diff --git a/Sources/QueryableSource.swift b/Sources/QueryableSource.swift index a844827..cf3da07 100644 --- a/Sources/QueryableSource.swift +++ b/Sources/QueryableSource.swift @@ -57,6 +57,9 @@ public protocol QueryableSource: class { - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select` parameter. */ func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? + + // TODO: docs + func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType /** 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. @@ -82,6 +85,9 @@ public protocol QueryableSource: class { */ func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? + // TODO: docs + func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary + /** The internal `NSManagedObjectContext` managed by this `QueryableSource`. Using this context directly should typically be avoided, and is provided by CoreStore only for extremely specialized cases. */ From 4a5bc6450b31abc75ac66caf86659b47acbdea8c Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Tue, 8 Aug 2017 07:36:52 +0900 Subject: [PATCH 08/56] complete query utilities --- CoreStoreTests/GroupByTests.swift | 8 +- CoreStoreTests/WhereTests.swift | 4 +- Sources/AttributeProtocol.swift | 2 +- Sources/CSGroupBy.swift | 18 +- Sources/ChainedClauseBuilder.swift | 18 +- Sources/CoreStoreObject+Querying.swift | 52 --- Sources/DataStack+Querying.swift | 2 +- Sources/GroupBy.swift | 33 +- Sources/NSManagedObjectContext+Querying.swift | 11 + Sources/RelationshipProtocol.swift | 4 +- Sources/Transformable.swift | 12 +- Sources/Value.swift | 2 +- Sources/Where+NSManagedObject.swift | 389 +++++++++++------- 13 files changed, 325 insertions(+), 230 deletions(-) diff --git a/CoreStoreTests/GroupByTests.swift b/CoreStoreTests/GroupByTests.swift index cf1e583..5f57f26 100644 --- a/CoreStoreTests/GroupByTests.swift +++ b/CoreStoreTests/GroupByTests.swift @@ -38,14 +38,14 @@ final class GroupByTests: BaseTestCase { do { - let groupBy = GroupBy() + let groupBy = GroupBy() XCTAssertEqual(groupBy, GroupBy([] as [String])) XCTAssertNotEqual(groupBy, GroupBy("key")) XCTAssertTrue(groupBy.keyPaths.isEmpty) } do { - let groupBy = GroupBy("key1") + let groupBy = GroupBy("key1") XCTAssertEqual(groupBy, GroupBy("key1")) XCTAssertEqual(groupBy, GroupBy(["key1"])) XCTAssertNotEqual(groupBy, GroupBy("key2")) @@ -53,7 +53,7 @@ final class GroupByTests: BaseTestCase { } do { - let groupBy = GroupBy("key1", "key2") + let groupBy = GroupBy("key1", "key2") XCTAssertEqual(groupBy, GroupBy("key1", "key2")) XCTAssertEqual(groupBy, GroupBy(["key1", "key2"])) XCTAssertNotEqual(groupBy, GroupBy("key2", "key1")) @@ -66,7 +66,7 @@ final class GroupByTests: BaseTestCase { self.prepareStack { (dataStack) in - let groupBy = GroupBy(#keyPath(TestEntity1.testString)) + let groupBy = GroupBy(#keyPath(TestEntity1.testString)) let request = CoreStoreFetchRequest() _ = From().applyToFetchRequest(request, context: dataStack.mainContext) diff --git a/CoreStoreTests/WhereTests.swift b/CoreStoreTests/WhereTests.swift index 4722b37..af9d9f0 100644 --- a/CoreStoreTests/WhereTests.swift +++ b/CoreStoreTests/WhereTests.swift @@ -31,12 +31,12 @@ import CoreStore // MARK: - XCTAssertAllEqual -private func XCTAssertAllEqual(_ whereClauses: Where...) { +private func XCTAssertAllEqual(_ whereClauses: Where...) { XCTAssertAllEqual(whereClauses) } -private func XCTAssertAllEqual(_ whereClauses: [Where]) { +private func XCTAssertAllEqual(_ whereClauses: [Where]) { for i in whereClauses.indices { diff --git a/Sources/AttributeProtocol.swift b/Sources/AttributeProtocol.swift index 9d6c017..4bc91bb 100644 --- a/Sources/AttributeProtocol.swift +++ b/Sources/AttributeProtocol.swift @@ -33,7 +33,7 @@ internal protocol AttributeProtocol: class { static var attributeType: NSAttributeType { get } - var keyPath: RawKeyPath { get } + var keyPath: KeyPathString { get } var isOptional: Bool { get } var isIndexed: Bool { get } var isTransient: Bool { get } diff --git a/Sources/CSGroupBy.swift b/Sources/CSGroupBy.swift index 9f79679..f5f97c2 100644 --- a/Sources/CSGroupBy.swift +++ b/Sources/CSGroupBy.swift @@ -35,7 +35,7 @@ import CoreData - SeeAlso: `GroupBy` */ @objc -public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType { +public final class CSGroupBy: NSObject, CSQueryClause { /** The list of key path strings to group results with @@ -102,11 +102,11 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType { // MARK: CoreStoreObjectiveCType - public let bridgeToSwift: GroupBy + public let bridgeToSwift: GroupBy - public init(_ swiftValue: GroupBy) { + public init(_ swiftValue: GroupBy) { - self.bridgeToSwift = swiftValue + self.bridgeToSwift = swiftValue.downcast() super.init() } } @@ -114,7 +114,7 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType { // MARK: - GroupBy -extension GroupBy: CoreStoreSwiftType { +extension GroupBy where D: NSManagedObject { // MARK: CoreStoreSwiftType @@ -122,4 +122,12 @@ extension GroupBy: CoreStoreSwiftType { return CSGroupBy(self) } + + + // MARK: FilePrivate + + fileprivate func downcast() -> GroupBy { + + return GroupBy(self.keyPaths) + } } diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index 24f0584..432cdbb 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -42,7 +42,6 @@ public protocol QueryChainableBuilderType { var from: From { get set } var select: Select { get set } - var groupBy: GroupBy { get set } var queryClauses: [QueryClause] { get set } } @@ -80,7 +79,6 @@ public struct QueryChainBuilder: QueryCha public var from: From public var select: Select - public var groupBy: GroupBy public var queryClauses: [QueryClause] = [] } @@ -111,7 +109,6 @@ public extension From { return .init( from: self, select: .init(selectTerms), - groupBy: .init(), queryClauses: [] ) } @@ -215,19 +212,19 @@ public extension FetchChainBuilder { public extension QueryChainBuilder { + public func groupBy(_ clause: GroupBy) -> QueryChainBuilder { + + return self.queryChain(appending: clause) + } + public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder { - return self.groupBy([keyPath] + keyPaths) + return self.groupBy(GroupBy([keyPath] + keyPaths)) } public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder { - return .init( - from: self.from, - select: self.select, - groupBy: .init(keyPaths), - queryClauses: self.queryClauses - ) + return self.queryChain(appending: GroupBy(keyPaths)) } public func `where`(_ clause: Where) -> QueryChainBuilder { @@ -268,7 +265,6 @@ public extension QueryChainBuilder { return .init( from: self.from, select: self.select, - groupBy: self.groupBy, queryClauses: self.queryClauses + [clause] ) } diff --git a/Sources/CoreStoreObject+Querying.swift b/Sources/CoreStoreObject+Querying.swift index 5f8c1ef..c0f9566 100644 --- a/Sources/CoreStoreObject+Querying.swift +++ b/Sources/CoreStoreObject+Querying.swift @@ -26,58 +26,6 @@ import CoreData import Foundation -//public extension From where D: NSManagedObject { -// -//// public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { -//// -//// return self.select(resultType, [selectTerm] + selectTerms) -//// } -//// -//// public func select(_ resultType: R.Type, _ selectTerms: [SelectTerm]) -> QueryChainBuilder { -//// -//// return .init( -//// from: self, -//// select: .init(selectTerms), -//// groupBy: .init(), -//// fetchClauses: [] -//// ) -//// } -// -// public func sectionBy(_ sectionKeyPath: KeyPath) -> SectionMonitorChainBuilder { -// -// return self.sectionBy(sectionKeyPath._kvcKeyPathString!, { $0 }) -// } -// -// public func sectionBy(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { -// -// return self.sectionBy(sectionKeyPath._kvcKeyPathString!, sectionIndexTransformer) -// } -// -// public func `where`(_ keyPath: KeyPath, isEqualTo value: Void?) -> FetchChainBuilder { -// -// return self.where(keyPath._kvcKeyPathString!, isEqualTo: value) -// } -// -// public func `where`(_ keyPath: KeyPath, isEqualTo value: U?) -> FetchChainBuilder { -// -// return self.where(keyPath._kvcKeyPathString!, isEqualTo: value) -// } -// -// public func `where`(_ keyPath: KeyPath, isEqualTo object: D?) -> FetchChainBuilder { -// -// return self.where(keyPath._kvcKeyPathString!, isEqualTo: object) -// } -// -// public func `where`(_ keyPath: KeyPath, isMemberOf list: S) -> FetchChainBuilder where S.Iterator.Element: QueryableAttributeType { -// -// return self.where(keyPath._kvcKeyPathString!, isMemberOf: list) -// } -// -// public func `where`(_ keyPath: KeyPath, isMemberOf list: S) -> FetchChainBuilder where S.Iterator.Element == D { -// -// return self.where(keyPath._kvcKeyPathString!, isMemberOf: list) -// } -//} // MARK: - DynamicObject diff --git a/Sources/DataStack+Querying.swift b/Sources/DataStack+Querying.swift index a0f527c..5f09687 100644 --- a/Sources/DataStack+Querying.swift +++ b/Sources/DataStack+Querying.swift @@ -153,7 +153,7 @@ extension DataStack: FetchableSource, QueryableSource { // TODO: docs public func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { - + CoreStore.assert( Thread.isMainThread, "Attempted to fetch from a \(cs_typeName(self)) outside the main thread." diff --git a/Sources/GroupBy.swift b/Sources/GroupBy.swift index 0c6153f..1ee748b 100644 --- a/Sources/GroupBy.swift +++ b/Sources/GroupBy.swift @@ -32,12 +32,7 @@ import CoreData /** The `GroupBy` clause specifies that the result of a query be grouped accoording to the specified key path. */ -public struct GroupBy: QueryClause, Hashable { - - /** - The list of key path strings to group results with - */ - public let keyPaths: [KeyPathString] +public struct GroupBy: GroupByClause, QueryClause, Hashable { /** Initializes a `GroupBy` clause with an empty list of key path strings @@ -69,6 +64,13 @@ public struct GroupBy: QueryClause, Hashable { } + // MARK: WhereClause + + public typealias ObjectType = D + + public let keyPaths: [KeyPathString] + + // MARK: QueryClause public func applyToFetchRequest(_ fetchRequest: NSFetchRequest) { @@ -100,3 +102,22 @@ public struct GroupBy: QueryClause, Hashable { return (self.keyPaths as NSArray).hashValue } } + + +// MARK: - GroupByClause + +/** + Abstracts the `GroupBy` clause for protocol utilities. + */ +public protocol GroupByClause { + + /** + The `DynamicObject` type associated with the clause + */ + associatedtype ObjectType: DynamicObject + + /** + The list of key path strings to group results with + */ + var keyPaths: [KeyPathString] { get } +} diff --git a/Sources/NSManagedObjectContext+Querying.swift b/Sources/NSManagedObjectContext+Querying.swift index c9a2d31..a48304e 100644 --- a/Sources/NSManagedObjectContext+Querying.swift +++ b/Sources/NSManagedObjectContext+Querying.swift @@ -303,6 +303,12 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.queryValue(selectTerms, fetchRequest: fetchRequest) } + @nonobjc + public func queryValue(_ clauseChain: B) -> B.ResultType? where B : QueryChainableBuilderType, B.ResultType : QueryableAttributeType { + + return self.queryValue(clauseChain.from, clauseChain.select, clauseChain.queryClauses) + } + @nonobjc public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { @@ -327,6 +333,11 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.queryAttributes(fetchRequest) } + public func queryAttributes(_ clauseChain: B) -> [[String : Any]]? where B : QueryChainableBuilderType, B.ResultType == NSDictionary { + + return self.queryAttributes(clauseChain.from, clauseChain.select, clauseChain.queryClauses) + } + // MARK: FetchableSource, QueryableSource diff --git a/Sources/RelationshipProtocol.swift b/Sources/RelationshipProtocol.swift index de1ff88..aa401ea 100644 --- a/Sources/RelationshipProtocol.swift +++ b/Sources/RelationshipProtocol.swift @@ -31,11 +31,11 @@ import CoreData internal protocol RelationshipProtocol: class { - var keyPath: RawKeyPath { get } + var keyPath: KeyPathString { get } var isToMany: Bool { get } var isOrdered: Bool { get } var deleteRule: NSDeleteRule { get } - var inverse: (type: CoreStoreObject.Type, keyPath: () -> RawKeyPath?) { get } + var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPathString?) { get } var affectedByKeyPaths: () -> Set { get } weak var parentObject: CoreStoreObject? { get set } var versionHashModifier: () -> String? { get } diff --git a/Sources/Transformable.swift b/Sources/Transformable.swift index 167cc78..435692f 100644 --- a/Sources/Transformable.swift +++ b/Sources/Transformable.swift @@ -116,7 +116,7 @@ public enum TransformableContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, initial: @autoclosure @escaping () -> V, isIndexed: Bool = false, isTransient: Bool = false, @@ -197,7 +197,7 @@ public enum TransformableContainer { return .transformableAttributeType } - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isOptional = false internal let isIndexed: Bool @@ -261,7 +261,7 @@ public enum TransformableContainer { @available(*, deprecated: 3.2, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)") public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, `default`: @autoclosure @escaping () -> V, isIndexed: Bool = false, isTransient: Bool = false, @@ -339,7 +339,7 @@ public enum TransformableContainer { - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. */ public init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, initial: @autoclosure @escaping () -> V? = nil, isIndexed: Bool = false, isTransient: Bool = false, @@ -420,7 +420,7 @@ public enum TransformableContainer { return .transformableAttributeType } - public let keyPath: RawKeyPath + public let keyPath: KeyPathString internal let isOptional = true internal let isIndexed: Bool @@ -484,7 +484,7 @@ public enum TransformableContainer { @available(*, deprecated: 3.2, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)") public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, `default`: @autoclosure @escaping () -> V? = nil, isIndexed: Bool = false, isTransient: Bool = false, diff --git a/Sources/Value.swift b/Sources/Value.swift index 72296fb..5f0c0b8 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -258,7 +258,7 @@ public enum ValueContainer { @available(*, deprecated: 3.2, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)") public convenience init( - _ keyPath: RawKeyPath, + _ keyPath: KeyPathString, `default`: @autoclosure @escaping () -> V, isIndexed: Bool = false, isTransient: Bool = false, diff --git a/Sources/Where+NSManagedObject.swift b/Sources/Where+NSManagedObject.swift index ef73499..ff0234b 100644 --- a/Sources/Where+NSManagedObject.swift +++ b/Sources/Where+NSManagedObject.swift @@ -29,192 +29,303 @@ import Foundation // MARK: - KeyPath where Root: NSManagedObject, Value: QueryableAttributeType & Equatable -public extension KeyPath where Root: NSManagedObject, Value: QueryableAttributeType & Equatable { +public func == (_ keyPath: KeyPath, _ value: V) -> Where { - public static func == (_ keyPath: KeyPath, _ value: Value) -> Where { - - return Where(keyPath._kvcKeyPathString!, isEqualTo: value) - } - - public static func != (_ keyPath: KeyPath, _ value: Value) -> Where { - - return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) - } - - public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == Value { - - return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) - } + return Where(keyPath._kvcKeyPathString!, isEqualTo: value) } +public func != (_ keyPath: KeyPath, _ value: V) -> Where { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == V { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) +} + + // MARK: - KeyPath where Root: NSManagedObject, Value: Optional -public extension KeyPath where Root: NSManagedObject { +public func == (_ keyPath: KeyPath>, _ value: V?) -> Where { - public static func == (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { - - return Where(keyPath._kvcKeyPathString!, isEqualTo: value) - } + return Where(keyPath._kvcKeyPathString!, isEqualTo: value) +} + +public func != (_ keyPath: KeyPath>, _ value: V?) -> Where { - public static func != (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { - - return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) - } + return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath>) -> Where where S.Iterator.Element == V { - public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where Value == Optional, S.Iterator.Element == V { - - return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) - } + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) } -// MARK: - KeyPath where Root: NSManagedObject, Value: QueryableAttributeType +// MARK: - KeyPath where Root: NSManagedObject, Value: QueryableAttributeType & Comparable -public extension KeyPath where Root: NSManagedObject, Value: QueryableAttributeType { +public func < (_ keyPath: KeyPath, _ value: V) -> Where { - public static func < (_ keyPath: KeyPath, _ value: Value) -> Where { - - return Where("%K < %@", keyPath._kvcKeyPathString!, value) - } + return Where("%K < %@", keyPath._kvcKeyPathString!, value) +} + +public func > (_ keyPath: KeyPath, _ value: V) -> Where { - public static func > (_ keyPath: KeyPath, _ value: Value) -> Where { - - return Where("%K > %@", keyPath._kvcKeyPathString!, value) - } + return Where("%K > %@", keyPath._kvcKeyPathString!, value) +} + +public func <= (_ keyPath: KeyPath, _ value: V) -> Where { - public static func <= (_ keyPath: KeyPath, _ value: Value) -> Where { - - return Where("%K <= %@", keyPath._kvcKeyPathString!, value) - } + return Where("%K <= %@", keyPath._kvcKeyPathString!, value) +} + +public func >= (_ keyPath: KeyPath, _ value: V) -> Where { - public static func >= (_ keyPath: KeyPath, _ value: Value) -> Where { - - return Where("%K >= %@", keyPath._kvcKeyPathString!, value) - } + return Where("%K >= %@", keyPath._kvcKeyPathString!, value) } -// MARK: - KeyPath where Root: NSManagedObject, Value: Optional +// MARK: - KeyPath where Root: NSManagedObject, Value: Optional -public extension KeyPath where Root: NSManagedObject { +public func < (_ keyPath: KeyPath>, _ value: V?) -> Where { - public static func < (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + if let value = value { - if let value = value { - - return Where("%K < %@", keyPath._kvcKeyPathString!, value) - } - else { - - return Where("%K < nil", keyPath._kvcKeyPathString!) - } + return Where("%K < %@", keyPath._kvcKeyPathString!, value) } - - public static func > (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + else { - if let value = value { - - return Where("%K > %@", keyPath._kvcKeyPathString!, value) - } - else { - - return Where("%K > nil", keyPath._kvcKeyPathString!) - } + return Where("%K < nil", keyPath._kvcKeyPathString!) } +} + +public func > (_ keyPath: KeyPath>, _ value: V?) -> Where { - public static func <= (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + if let value = value { - if let value = value { - - return Where("%K <= %@", keyPath._kvcKeyPathString!, value) - } - else { - - return Where("%K <= nil", keyPath._kvcKeyPathString!) - } + return Where("%K > %@", keyPath._kvcKeyPathString!, value) } - - public static func >= (_ keyPath: KeyPath, _ value: Value) -> Where where Value == Optional { + else { - if let value = value { - - return Where("%K >= %@", keyPath._kvcKeyPathString!, value) - } - else { - - return Where("%K >= nil", keyPath._kvcKeyPathString!) - } + return Where("%K > nil", keyPath._kvcKeyPathString!) + } +} + +public func <= (_ keyPath: KeyPath>, _ value: V?) -> Where { + + if let value = value { + + return Where("%K <= %@", keyPath._kvcKeyPathString!, value) + } + else { + + return Where("%K <= nil", keyPath._kvcKeyPathString!) + } +} + +public func >= (_ keyPath: KeyPath>, _ value: V?) -> Where { + + if let value = value { + + return Where("%K >= %@", keyPath._kvcKeyPathString!, value) + } + else { + + return Where("%K >= nil", keyPath._kvcKeyPathString!) } } // MARK: - KeyPath where Root: NSManagedObject, Value: NSManagedObject -public extension KeyPath where Root: NSManagedObject, Value: NSManagedObject { +public func == (_ keyPath: KeyPath, _ object: D) -> Where { - public static func == (_ keyPath: KeyPath, _ object: Value) -> Where { - - return Where(keyPath._kvcKeyPathString!, isEqualTo: object) - } + return Where(keyPath._kvcKeyPathString!, isEqualTo: object) +} + +public func != (_ keyPath: KeyPath, _ object: D) -> Where { - public static func != (_ keyPath: KeyPath, _ object: Value) -> Where { - - return !Where(keyPath._kvcKeyPathString!, isEqualTo: object) - } + return !Where(keyPath._kvcKeyPathString!, isEqualTo: object) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == D { - public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == Value { - - return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) - } + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) +} + +public func == (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { - public static func == (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { - - return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) - } + return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) +} + +public func != (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { - public static func != (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { - - return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) - } + return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == NSManagedObjectID { - public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == NSManagedObjectID { - - return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) - } + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) } // MARK: - KeyPath where Root: NSManagedObject, Value: Optional -public extension KeyPath where Root: NSManagedObject { +public func == (_ keyPath: KeyPath>, _ object: D?) -> Where { - public static func == (_ keyPath: KeyPath, _ object: Value) -> Where where Value == Optional { - - return Where(keyPath._kvcKeyPathString!, isEqualTo: object) + return Where(keyPath._kvcKeyPathString!, isEqualTo: object) +} + +public func != (_ keyPath: KeyPath>, _ object: D?) -> Where { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: object) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath>) -> Where where S.Iterator.Element == D { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) +} + +public func == (_ keyPath: KeyPath>, _ objectID: NSManagedObjectID?) -> Where { + + return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) +} + +public func != (_ keyPath: KeyPath>, _ objectID: NSManagedObjectID?) -> Where { + + return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath>) -> Where where S.Iterator.Element == NSManagedObjectID { + + return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) +} + + +// MARK: - KeyPath where Root: CoreStoreObject, Value: ValueContainer.Required + +public func == (_ keyPath: KeyPath.Required>, _ value: V) -> Where { + + return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) +} + +public func != (_ keyPath: KeyPath.Required>, _ value: V) -> Where { + + return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath.Required>) -> Where where S.Iterator.Element == V { + + return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) +} + + +// MARK: - KeyPath where Root: CoreStoreObject, Value: ValueContainer.Optional + +public func == (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { + + return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) +} + +public func != (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { + + return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath.Optional>) -> Where where S.Iterator.Element == V { + + return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) +} + + +// MARK: - KeyPath where Root: CoreStoreObject, Value: ValueContainer.Required + +public func < (_ keyPath: KeyPath.Required>, _ value: V) -> Where { + + return Where("%K < %@", O.meta[keyPath: keyPath].keyPath, value) +} + +public func > (_ keyPath: KeyPath.Required>, _ value: V) -> Where { + + return Where("%K > %@", O.meta[keyPath: keyPath].keyPath, value) +} + +public func <= (_ keyPath: KeyPath.Required>, _ value: V) -> Where { + + return Where("%K <= %@", O.meta[keyPath: keyPath].keyPath, value) +} + +public func >= (_ keyPath: KeyPath.Required>, _ value: V) -> Where { + + return Where("%K >= %@", O.meta[keyPath: keyPath].keyPath, value) +} + + +// MARK: - KeyPath where Root: CoreStoreObject, Value: ValueContainer.Optional + +public func < (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { + + if let value = value { + + return Where("%K < %@", O.meta[keyPath: keyPath].keyPath, value) } - - public static func != (_ keyPath: KeyPath, _ object: Value) -> Where where Value == Optional { - - return !Where(keyPath._kvcKeyPathString!, isEqualTo: object) - } - - public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where Value == Optional, S.Iterator.Element == V { - - return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) - } - - public static func == (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where where Value == Optional { - - return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) - } - - public static func != (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where where Value == Optional { - - return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) - } - - public static func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where Value == Optional, S.Iterator.Element == NSManagedObjectID { - - return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) + else { + + return Where("%K < nil", O.meta[keyPath: keyPath].keyPath) } } + +public func > (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { + + if let value = value { + + return Where("%K > %@", O.meta[keyPath: keyPath].keyPath, value) + } + else { + + return Where("%K > nil", O.meta[keyPath: keyPath].keyPath) + } +} + +public func <= (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { + + if let value = value { + + return Where("%K <= %@", O.meta[keyPath: keyPath].keyPath, value) + } + else { + + return Where("%K <= nil", O.meta[keyPath: keyPath].keyPath) + } +} + +public func >= (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { + + if let value = value { + + return Where("%K >= %@", O.meta[keyPath: keyPath].keyPath, value) + } + else { + + return Where("%K >= nil", O.meta[keyPath: keyPath].keyPath) + } +} + + +// MARK: - KeyPath where Root: CoreStoreObject, Value: RelationshipContainer.ToOne + +public func == (_ keyPath: KeyPath.ToOne>, _ object: D) -> Where { + + return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) +} + +public func != (_ keyPath: KeyPath.ToOne>, _ object: D) -> Where { + + return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) +} + +public func ~= (_ sequence: S, _ keyPath: KeyPath.ToOne>) -> Where where S.Iterator.Element == D { + + return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) +} From f7aaf4fb2a6f71d8edc2092071dec49b0a7a51b2 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Tue, 8 Aug 2017 08:02:05 +0900 Subject: [PATCH 09/56] Fix compile errors for Xcode 9 beta 5 --- Sources/CoreStore+CustomDebugStringConvertible.swift | 9 --------- Sources/CoreStoreSchema.swift | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Sources/CoreStore+CustomDebugStringConvertible.swift b/Sources/CoreStore+CustomDebugStringConvertible.swift index ca39d52..db99e03 100644 --- a/Sources/CoreStore+CustomDebugStringConvertible.swift +++ b/Sources/CoreStore+CustomDebugStringConvertible.swift @@ -1093,15 +1093,6 @@ fileprivate extension String { return String(repeating: " ", count: level * 4) } - fileprivate func trimSwiftModuleName() -> String { - - if self.hasPrefix("Swift.") { - - return self.substring(from: "Swift.".endIndex) - } - return self - } - fileprivate mutating func indent(_ level: Int) { self = self.replacingOccurrences(of: "\n", with: "\n\(String.indention(level))") diff --git a/Sources/CoreStoreSchema.swift b/Sources/CoreStoreSchema.swift index 633f3cb..cbd0c98 100644 --- a/Sources/CoreStoreSchema.swift +++ b/Sources/CoreStoreSchema.swift @@ -538,7 +538,7 @@ public final class CoreStoreSchema: DynamicSchema { } else { - let newMethod = class_getClassMethod(managedObjectClass, newSelector) + let newMethod = class_getClassMethod(managedObjectClass, newSelector)! method_exchangeImplementations(origMethod, newMethod) } } From 37fbedd79967efff20edd7e19a847ead2e8397bd Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 9 Aug 2017 08:11:41 +0900 Subject: [PATCH 10/56] WIP: keypath utilities for all raw clauses --- Sources/Where.swift | 110 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/Sources/Where.swift b/Sources/Where.swift index b8b6fdd..1daad7e 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -328,6 +328,116 @@ public protocol WhereClause { } +// MARK: - Where where D: NSManagedObject + +public extension Where where D: NSManagedObject { + + /** + Initializes a `Where` clause that compares equality to `nil` + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath, isEqualTo value: Void?) { + + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == nil")) + } + + /** + Initializes a `Where` clause that compares equality to `nil` + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath, isEqualTo value: Void?) { + + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == nil")) + } + + /** + Initializes a `Where` clause that compares equality + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath, isEqualTo value: V?) { + + switch value { + + case nil, + is NSNull: + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == nil")) + + case let value?: + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == %@", argumentArray: [value.cs_toQueryableNativeType()])) + } + } + + /** + Initializes a `Where` clause that compares equality + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath, isEqualTo value: O?) { + + switch value { + + case nil, + is NSNull: + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == nil")) + + case let value?: + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == %@", argumentArray: [value.cs_id()])) + } + } + + /** + Initializes a `Where` clause that compares equality + + - parameter keyPath: the keyPath to compare with + - parameter objectID: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath, isEqualTo objectID: NSManagedObjectID) { + + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == %@", argumentArray: [objectID])) + } + + /** + Initializes a `Where` clause that compares membership + + - parameter keyPath: the keyPath to compare with + - parameter list: the sequence to check membership of + */ + public init(_ keyPath: KeyPath, isMemberOf list: S) where S.Iterator.Element == V { + + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) IN %@", list.map({ $0.cs_toQueryableNativeType() }) as NSArray)) + } + + /** + Initializes a `Where` clause that compares membership + + - parameter keyPath: the keyPath to compare with + - parameter list: the sequence to check membership of + */ + public init(_ keyPath: KeyPath, isMemberOf list: S) where S.Iterator.Element == O { + + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) IN %@", list.map({ $0.cs_id() }) as NSArray)) + } + + /** + Initializes a `Where` clause that compares membership + + - parameter keyPath: the keyPath to compare with + - parameter list: the sequence to check membership of + */ + public init(_ keyPath: KeyPath, isMemberOf list: S) where S.Iterator.Element: NSManagedObjectID { + + self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) IN %@", list.map({ $0 }) as NSArray)) + } +} + + // MARK: - Sequence where Iterator.Element: WhereClause public extension Sequence where Iterator.Element: WhereClause { From f62137fb581af2f81f52684a2913c316281c8675 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Thu, 10 Aug 2017 00:10:08 +0900 Subject: [PATCH 11/56] rename file --- CoreStore.xcodeproj/project.pbxproj | 20 +++++++++---------- ...gedObject.swift => KeyPath+Querying.swift} | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) rename Sources/{Where+NSManagedObject.swift => KeyPath+Querying.swift} (99%) diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 800d8ef..18dad5c 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -489,10 +489,10 @@ B59FA0B01CCBACA7007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; }; B59FA0B11CCBACA7007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; }; B59FA0B21CCBACA8007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; }; - B5A1DAC81F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */; }; - B5A1DAC91F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */; }; - B5A1DACA1F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */; }; - B5A1DACB1F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */; }; + B5A1DAC81F111BFA003CF369 /* KeyPath+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */; }; + B5A1DAC91F111BFA003CF369 /* KeyPath+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */; }; + B5A1DACA1F111BFA003CF369 /* KeyPath+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */; }; + B5A1DACB1F111BFA003CF369 /* KeyPath+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */; }; B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A261201B64BFDB006EB6D3 /* MigrationType.swift */; }; B5A5F2661CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; }; B5A5F2681CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; }; @@ -846,7 +846,7 @@ B596BBBA1DD5C39F001DCDD9 /* QueryableSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryableSource.swift; sourceTree = ""; }; B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStoreCoordinator+Setup.swift"; sourceTree = ""; }; B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ICloudStore.swift; sourceTree = ""; }; - B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Where+NSManagedObject.swift"; sourceTree = ""; }; + B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyPath+Querying.swift"; sourceTree = ""; }; B5A261201B64BFDB006EB6D3 /* MigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationType.swift; sourceTree = ""; }; B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSelect.swift; sourceTree = ""; }; B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionLock.swift; sourceTree = ""; }; @@ -1283,7 +1283,7 @@ B5A1DAC61F111BBE003CF369 /* KeyPath Utilities */ = { isa = PBXGroup; children = ( - B5A1DAC71F111BFA003CF369 /* Where+NSManagedObject.swift */, + B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */, B5D339EB1E9495E500C880DE /* CoreStoreObject+Querying.swift */, ); name = "KeyPath Utilities"; @@ -1901,7 +1901,7 @@ B501FDE71CA8D20500BE22EF /* CSListObserver.swift in Sources */, B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B501FDE21CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, - B5A1DAC81F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */, + B5A1DAC81F111BFA003CF369 /* KeyPath+Querying.swift in Sources */, 2F291E2719C6D3CF007AF63F /* CoreStore.swift in Sources */, B5ECDC111CA816E500C7F112 /* CSTweak.swift in Sources */, B56923C41EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, @@ -2092,7 +2092,7 @@ B501FDE91CA8D20500BE22EF /* CSListObserver.swift in Sources */, B5E41EC11EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B501FDE41CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, - B5A1DAC91F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */, + B5A1DAC91F111BFA003CF369 /* KeyPath+Querying.swift in Sources */, B5FE4DA31C8481E100FA6A91 /* StorageInterface.swift in Sources */, B5ECDC131CA816E500C7F112 /* CSTweak.swift in Sources */, B56923C51EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, @@ -2283,7 +2283,7 @@ B559CD4D1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, B5E41EC31EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B5ECDBE91CA6BEA300C7F112 /* CSClauseTypes.swift in Sources */, - B5A1DACB1F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */, + B5A1DACB1F111BFA003CF369 /* KeyPath+Querying.swift in Sources */, B52DD1B81BE1F94000949AFE /* DataStack+Migration.swift in Sources */, B5ECDC091CA8138100C7F112 /* CSOrderBy.swift in Sources */, B56923C71EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, @@ -2474,7 +2474,7 @@ B501FDE51CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, B5E41EC21EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B5ECDC141CA816E500C7F112 /* CSTweak.swift in Sources */, - B5A1DACA1F111BFA003CF369 /* Where+NSManagedObject.swift in Sources */, + B5A1DACA1F111BFA003CF369 /* KeyPath+Querying.swift in Sources */, B56321AE1BD6521C006C9394 /* NotificationObserver.swift in Sources */, B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */, B56923C61EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, diff --git a/Sources/Where+NSManagedObject.swift b/Sources/KeyPath+Querying.swift similarity index 99% rename from Sources/Where+NSManagedObject.swift rename to Sources/KeyPath+Querying.swift index ff0234b..e06d9e4 100644 --- a/Sources/Where+NSManagedObject.swift +++ b/Sources/KeyPath+Querying.swift @@ -1,5 +1,5 @@ // -// Where+NSManagedObject.swift +// KeyPath+Querying.swift // CoreStore // // Copyright © 2017 John Rommel Estropia From f5e1643ef796490d6136f87194ef9e611f8e374f Mon Sep 17 00:00:00 2001 From: John Estropia Date: Tue, 4 Jul 2017 12:21:46 +0900 Subject: [PATCH 12/56] minor cleanup --- Sources/Where.swift | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Sources/Where.swift b/Sources/Where.swift index 1daad7e..5cd0294 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -50,13 +50,11 @@ public struct Where: WhereClause, FetchClause, QueryClause, De */ public static func && (left: Where, right: Where?) -> Where { - if right != nil { - return left && right! - } - else { + if let right = right { - return left + return left && right } + return left } /** @@ -67,13 +65,11 @@ public struct Where: WhereClause, FetchClause, QueryClause, De */ public static func && (left: Where?, right: Where) -> Where { - if left != nil { + if let left = left { + return left && right } - else { - - return right - } + return right } /** @@ -92,13 +88,11 @@ public struct Where: WhereClause, FetchClause, QueryClause, De */ public static func || (left: Where, right: Where?) -> Where { - if right != nil { - return left || right! - } - else { + if let right = right { - return left + return left || right } + return left } /** @@ -109,13 +103,11 @@ public struct Where: WhereClause, FetchClause, QueryClause, De */ public static func || (left: Where?, right: Where) -> Where { - if left != nil { + if let left = left { + return left || right } - else { - - return right - } + return right } /** From 0f405a50aa2ed524477a01cdfc2e09456fd5d002 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Thu, 10 Aug 2017 00:42:29 +0900 Subject: [PATCH 13/56] WIP: KeyPath utilities for all clauses --- Sources/Where.swift | 148 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 124 insertions(+), 24 deletions(-) diff --git a/Sources/Where.swift b/Sources/Where.swift index 5cd0294..e1ed743 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -332,7 +332,7 @@ public extension Where where D: NSManagedObject { */ public init(_ keyPath: KeyPath, isEqualTo value: Void?) { - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == nil")) + self.init(keyPath._kvcKeyPathString!, isEqualTo: value) } /** @@ -343,7 +343,7 @@ public extension Where where D: NSManagedObject { */ public init(_ keyPath: KeyPath, isEqualTo value: Void?) { - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == nil")) + self.init(keyPath._kvcKeyPathString!, isEqualTo: value) } /** @@ -354,15 +354,7 @@ public extension Where where D: NSManagedObject { */ public init(_ keyPath: KeyPath, isEqualTo value: V?) { - switch value { - - case nil, - is NSNull: - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == nil")) - - case let value?: - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == %@", argumentArray: [value.cs_toQueryableNativeType()])) - } + self.init(keyPath._kvcKeyPathString!, isEqualTo: value) } /** @@ -373,15 +365,7 @@ public extension Where where D: NSManagedObject { */ public init(_ keyPath: KeyPath, isEqualTo value: O?) { - switch value { - - case nil, - is NSNull: - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == nil")) - - case let value?: - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == %@", argumentArray: [value.cs_id()])) - } + self.init(keyPath._kvcKeyPathString!, isEqualTo: value) } /** @@ -392,7 +376,7 @@ public extension Where where D: NSManagedObject { */ public init(_ keyPath: KeyPath, isEqualTo objectID: NSManagedObjectID) { - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) == %@", argumentArray: [objectID])) + self.init(keyPath._kvcKeyPathString!, isEqualTo: objectID) } /** @@ -403,7 +387,7 @@ public extension Where where D: NSManagedObject { */ public init(_ keyPath: KeyPath, isMemberOf list: S) where S.Iterator.Element == V { - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) IN %@", list.map({ $0.cs_toQueryableNativeType() }) as NSArray)) + self.init(keyPath._kvcKeyPathString!, isMemberOf: list) } /** @@ -414,7 +398,7 @@ public extension Where where D: NSManagedObject { */ public init(_ keyPath: KeyPath, isMemberOf list: S) where S.Iterator.Element == O { - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) IN %@", list.map({ $0.cs_id() }) as NSArray)) + self.init(keyPath._kvcKeyPathString!, isMemberOf: list) } /** @@ -425,7 +409,123 @@ public extension Where where D: NSManagedObject { */ public init(_ keyPath: KeyPath, isMemberOf list: S) where S.Iterator.Element: NSManagedObjectID { - self.init(NSPredicate(format: "\(keyPath._kvcKeyPathString!) IN %@", list.map({ $0 }) as NSArray)) + self.init(keyPath._kvcKeyPathString!, isMemberOf: list) + } +} + + +// MARK: - Where where D: CoreStoreObject + +public extension Where where D: CoreStoreObject { + + /** + Initializes a `Where` clause that compares equality to `nil` + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath.Optional>, isEqualTo value: Void?) { + + self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: value) + } + + /** + Initializes a `Where` clause that compares equality to `nil` + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath.ToOne>, isEqualTo value: Void?) { + + self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: value) + } + + /** + Initializes a `Where` clause that compares equality + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath.Required>, isEqualTo value: V?) { + + self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: value) + } + + /** + Initializes a `Where` clause that compares equality + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath.Optional>, isEqualTo value: V?) { + + self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: value) + } + + /** + Initializes a `Where` clause that compares equality + + - parameter keyPath: the keyPath to compare with + - parameter value: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath.ToOne>, isEqualTo value: O?) { + + self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: value) + } + + /** + Initializes a `Where` clause that compares equality + + - parameter keyPath: the keyPath to compare with + - parameter objectID: the arguments for the `==` operator + */ + public init(_ keyPath: KeyPath.ToOne>, isEqualTo objectID: NSManagedObjectID) { + + self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: objectID) + } + + /** + Initializes a `Where` clause that compares membership + + - parameter keyPath: the keyPath to compare with + - parameter list: the sequence to check membership of + */ + public init(_ keyPath: KeyPath.Required>, isMemberOf list: S) where S.Iterator.Element == V { + + self.init(D.meta[keyPath: keyPath].keyPath, isMemberOf: list) + } + + /** + Initializes a `Where` clause that compares membership + + - parameter keyPath: the keyPath to compare with + - parameter list: the sequence to check membership of + */ + public init(_ keyPath: KeyPath.Optional>, isMemberOf list: S) where S.Iterator.Element == V { + + self.init(D.meta[keyPath: keyPath].keyPath, isMemberOf: list) + } + + /** + Initializes a `Where` clause that compares membership + + - parameter keyPath: the keyPath to compare with + - parameter list: the sequence to check membership of + */ + public init(_ keyPath: KeyPath.ToOne>, isMemberOf list: S) where S.Iterator.Element == O { + + self.init(D.meta[keyPath: keyPath].keyPath, isMemberOf: list) + } + + /** + Initializes a `Where` clause that compares membership + + - parameter keyPath: the keyPath to compare with + - parameter list: the sequence to check membership of + */ + public init(_ keyPath: KeyPath.ToOne>, isMemberOf list: S) where S.Iterator.Element: NSManagedObjectID { + + self.init(D.meta[keyPath: keyPath].keyPath, isMemberOf: list) } } From c89bc3c22712f660864ff22c469c7987c4749b0d Mon Sep 17 00:00:00 2001 From: Sid Mani Date: Wed, 16 Aug 2017 00:36:06 -0700 Subject: [PATCH 14/56] Corrected type-checking in fetchExisting to preserve input type --- Sources/NSManagedObjectContext+Querying.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/NSManagedObjectContext+Querying.swift b/Sources/NSManagedObjectContext+Querying.swift index 11e58e6..f857a1a 100644 --- a/Sources/NSManagedObjectContext+Querying.swift +++ b/Sources/NSManagedObjectContext+Querying.swift @@ -62,7 +62,9 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return object } - return T.cs_fromRaw(object: existingRawObject) + + let objectDynamicType = type(of: object) + return objectDynamicType.cs_fromRaw(object: existingRawObject) } catch { From da170c7e51c45ca44f1af5dd9e28170c5df64398 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Wed, 23 Aug 2017 18:40:06 +0900 Subject: [PATCH 15/56] Fixed Xcode 9 beta 6 errors --- Sources/XcodeDataModelSchema.swift | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/XcodeDataModelSchema.swift b/Sources/XcodeDataModelSchema.swift index 19a9819..e4db7ff 100644 --- a/Sources/XcodeDataModelSchema.swift +++ b/Sources/XcodeDataModelSchema.swift @@ -145,7 +145,7 @@ public final class XcodeDataModelSchema: DynamicSchema { public required init(modelName: ModelVersion, modelVersionFileURL: URL) { CoreStore.assert( - FileManager.default.fileExists(atPath: modelVersionFileURL.path), + NSManagedObjectModel(contentsOf: modelVersionFileURL) != nil, "Could not find the \"\(modelName).mom\" version file for the model at URL \"\(modelVersionFileURL)\"." ) @@ -164,13 +164,12 @@ public final class XcodeDataModelSchema: DynamicSchema { return cachedRawModel } - CoreStore.assert( - FileManager.default.fileExists(atPath: self.modelVersionFileURL.path), - "Could not find the \"\(self.modelVersion).mom\" version file for the model at URL \"\(self.modelVersionFileURL)\"." - ) - let rawModel = NSManagedObjectModel(contentsOf: self.modelVersionFileURL) - self.cachedRawModel = rawModel - return rawModel + if let rawModel = NSManagedObjectModel(contentsOf: self.modelVersionFileURL) { + + self.cachedRawModel = rawModel + return rawModel + } + CoreStore.abort("Could not create an \(cs_typeName(NSManagedObjectModel.self)) from the model at URL \"\(self.modelVersionFileURL)\".") } From cd405e038ebb1808da2a2b5044bb826c43d7d05d Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Thu, 31 Aug 2017 23:00:17 +0900 Subject: [PATCH 16/56] updated demo app --- .swift-version | 2 +- ...etchingAndQueryingDemoViewController.swift | 43 ++++++++++++------- .../ListObserverDemoViewController.swift | 2 +- .../CustomLoggerViewController.swift | 4 +- .../MigrationsDemoViewController.swift | 5 ++- 5 files changed, 36 insertions(+), 20 deletions(-) diff --git a/.swift-version b/.swift-version index 8c50098..5186d07 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.1 +4.0 diff --git a/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift index 013c529..f1d7d4d 100644 --- a/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift @@ -165,8 +165,8 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo fetch: { () -> [TimeZone] in return Static.timeZonesStack.fetchAll( - From(), - OrderBy(.ascending(#keyPath(TimeZone.name))) + From() + .orderBy(.ascending(\.name)) )! } ), @@ -175,9 +175,13 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo fetch: { () -> [TimeZone] in return Static.timeZonesStack.fetchAll( - From(), - Where("%K BEGINSWITH[c] %@", #keyPath(TimeZone.name), "Asia"), - OrderBy(.ascending(#keyPath(TimeZone.secondsFromGMT))) + From() + .where( + format: "%K BEGINSWITH[c] %@", + #keyPath(TimeZone.name), + "Asia" + ) + .orderBy(.ascending(\.secondsFromGMT)) )! } ), @@ -186,10 +190,15 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo fetch: { () -> [TimeZone] in return Static.timeZonesStack.fetchAll( - From(), - Where("%K BEGINSWITH[c] %@", #keyPath(TimeZone.name), "America") - || Where("%K BEGINSWITH[c] %@", #keyPath(TimeZone.name), "Europe"), - OrderBy(.ascending(#keyPath(TimeZone.secondsFromGMT))) + From() + .where( + format: "%K BEGINSWITH[c] %@ OR %K BEGINSWITH[c] %@", + #keyPath(TimeZone.name), + "America", + #keyPath(TimeZone.name), + "Europe" + ) + .orderBy(.ascending(\.secondsFromGMT)) )! } ), @@ -198,9 +207,13 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo fetch: { () -> [TimeZone] in return Static.timeZonesStack.fetchAll( - From(), - !Where("%K BEGINSWITH[c] %@", #keyPath(TimeZone.name), "America"), - OrderBy(.ascending(#keyPath(TimeZone.secondsFromGMT))) + From() + .where( + format: "%K BEGINSWITH[c] %@", + #keyPath(TimeZone.name), + "America" + ) + .orderBy(.ascending(\.secondsFromGMT)) )! } ), @@ -209,9 +222,9 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo fetch: { () -> [TimeZone] in return Static.timeZonesStack.fetchAll( - From(), - Where("hasDaylightSavingTime", isEqualTo: true), - OrderBy(.ascending(#keyPath(TimeZone.name))) + From() + .where(\.hasDaylightSavingTime == true) + .orderBy(.ascending(\.name)) )! } ) diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift index de745c9..85632f2 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift @@ -28,7 +28,7 @@ struct ColorsDemo { } } - func whereClause() -> Where { + func whereClause() -> Where { switch self { diff --git a/CoreStoreDemo/CoreStoreDemo/Loggers Demo/CustomLoggerViewController.swift b/CoreStoreDemo/CoreStoreDemo/Loggers Demo/CustomLoggerViewController.swift index 5bf30fd..9a309ed 100644 --- a/CoreStoreDemo/CoreStoreDemo/Loggers Demo/CustomLoggerViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Loggers Demo/CustomLoggerViewController.swift @@ -99,8 +99,8 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger { case 0?: let request = NSFetchRequest() - Where(true).applyToFetchRequest(request) - Where(false).applyToFetchRequest(request) + Where(true).applyToFetchRequest(request) + Where(false).applyToFetchRequest(request) case 1?: _ = try? dataStack.addStorageAndWait( diff --git a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift index c4329f2..baffca5 100644 --- a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift @@ -369,7 +369,10 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD )! self._dataStack = dataStack - let listMonitor = dataStack.monitorList(From(model.entityType), OrderBy(.descending("dna"))) + let listMonitor = dataStack.monitorList( + From(model.entityType) + .orderBy(.descending(#keyPath(OrganismV1.dna))) + ) listMonitor.addObserver(self) self._listMonitor = listMonitor From 78e43a37a5981a88c49dc5cc0b24c5ec69088b1a Mon Sep 17 00:00:00 2001 From: John Estropia Date: Tue, 19 Sep 2017 15:54:15 +0900 Subject: [PATCH 17/56] fix OSX compiler errors --- Sources/ChainedClauseBuilder.swift | 5 +++++ Sources/DynamicObject.swift | 7 +------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index 432cdbb..7974877 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -45,6 +45,7 @@ public protocol QueryChainableBuilderType { var queryClauses: [QueryClause] { get set } } +@available(OSX 10.12, *) public protocol SectionMonitorBuilderType { associatedtype ObjectType: DynamicObject @@ -85,6 +86,7 @@ public struct QueryChainBuilder: QueryCha // MARK: - SectionMonitorChainBuilder +@available(OSX 10.12, *) public struct SectionMonitorChainBuilder: SectionMonitorBuilderType { // MARK: SectionMonitorBuilderType @@ -113,11 +115,13 @@ public extension From { ) } + @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString) -> SectionMonitorChainBuilder { return self.sectionBy(sectionKeyPath, { $0 }) } + @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return .init( @@ -270,6 +274,7 @@ public extension QueryChainBuilder { } } +@available(OSX 10.12, *) public extension SectionMonitorChainBuilder { public func `where`(_ clause: Where) -> SectionMonitorChainBuilder { diff --git a/Sources/DynamicObject.swift b/Sources/DynamicObject.swift index 664d013..a79d68f 100644 --- a/Sources/DynamicObject.swift +++ b/Sources/DynamicObject.swift @@ -78,12 +78,7 @@ extension NSManagedObject: DynamicObject { public class func cs_fromRaw(object: NSManagedObject) -> Self { - @inline(__always) - func forceCast(_ value: Any) -> D { - - return value as! D - } - return forceCast(object) + return unsafeDowncast(object, to: self) } public static func cs_matches(object: NSManagedObject) -> Bool { From 2c5fa63f40ac1857ba134eafcebefb3eaf371bd5 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Tue, 19 Sep 2017 15:55:51 +0900 Subject: [PATCH 18/56] revert --- Sources/DynamicObject.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/DynamicObject.swift b/Sources/DynamicObject.swift index a79d68f..664d013 100644 --- a/Sources/DynamicObject.swift +++ b/Sources/DynamicObject.swift @@ -78,7 +78,12 @@ extension NSManagedObject: DynamicObject { public class func cs_fromRaw(object: NSManagedObject) -> Self { - return unsafeDowncast(object, to: self) + @inline(__always) + func forceCast(_ value: Any) -> D { + + return value as! D + } + return forceCast(object) } public static func cs_matches(object: NSManagedObject) -> Bool { From fd1ce208638bf2c6bd6110f4a3ca8c5a1f57bb5f Mon Sep 17 00:00:00 2001 From: John Estropia Date: Tue, 19 Sep 2017 16:04:26 +0900 Subject: [PATCH 19/56] set swift version --- CoreStore.xcodeproj/project.pbxproj | 6 ++++-- CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 18dad5c..a0b9b84 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -2627,6 +2627,7 @@ SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -2680,6 +2681,7 @@ SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; @@ -2955,7 +2957,7 @@ SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 4; }; name = Debug; @@ -2979,7 +2981,7 @@ SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 4; }; name = Release; diff --git a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj index c92f2aa..186cbd5 100644 --- a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj +++ b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj @@ -408,6 +408,7 @@ SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -447,6 +448,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; VALIDATE_PRODUCT = YES; }; name = Release; From 0d23ce1598d448242aff564872b71beb3cafcb84 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Tue, 19 Sep 2017 16:22:59 +0900 Subject: [PATCH 20/56] fix Demo app compiler errors --- ...etchingAndQueryingDemoViewController.swift | 45 ++++++++++++------- .../MigrationsDemoViewController.swift | 4 +- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift index f1d7d4d..cfee272 100644 --- a/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift @@ -230,15 +230,15 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo ) ] - private let queryingItems = [ + private let queryingItems: [(title: String, query: () -> Any)] = [ ( title: "Number of Time Zones", query: { () -> Any in return Static.timeZonesStack.queryValue( - From(), - Select(.count(#keyPath(TimeZone.name))) - )! + From() + .select(NSNumber.self, .count(#keyPath(TimeZone.name))) + )! as Any } ), ( @@ -246,10 +246,10 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo query: { () -> Any in return Static.timeZonesStack.queryValue( - From(), - Select(#keyPath(TimeZone.abbreviation)), - Where("%K ENDSWITH[c] %@", #keyPath(TimeZone.name), "Tokyo") - )! + From() + .select(String.self, .attribute(#keyPath(TimeZone.abbreviation))) + .where(format: "%K ENDSWITH[c] %@", #keyPath(TimeZone.name), "Tokyo") + )! as Any } ), ( @@ -257,9 +257,13 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo query: { () -> Any in return Static.timeZonesStack.queryAttributes( - From(), - Select(#keyPath(TimeZone.name), #keyPath(TimeZone.abbreviation)), - OrderBy(.ascending(#keyPath(TimeZone.name))) + From() + .select( + NSDictionary.self, + .attribute(#keyPath(TimeZone.name)), + .attribute(#keyPath(TimeZone.abbreviation)) + ) + .orderBy(.ascending(#keyPath(TimeZone.name))) )! } ), @@ -268,10 +272,17 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo query: { () -> Any in return Static.timeZonesStack.queryAttributes( - From(), - Select(.count(#keyPath(TimeZone.abbreviation)), #keyPath(TimeZone.abbreviation)), - GroupBy(#keyPath(TimeZone.abbreviation)), - OrderBy(.ascending(#keyPath(TimeZone.secondsFromGMT)), .ascending(#keyPath(TimeZone.name))) + From() + .select( + NSDictionary.self, + .count(#keyPath(TimeZone.abbreviation)), + .attribute(#keyPath(TimeZone.abbreviation)) + ) + .groupBy(#keyPath(TimeZone.abbreviation)) + .orderBy( + .ascending(#keyPath(TimeZone.secondsFromGMT)), + .ascending(#keyPath(TimeZone.name)) + ) )! } ), @@ -285,8 +296,8 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo .count(#keyPath(TimeZone.hasDaylightSavingTime), as: "numberOfCountries"), #keyPath(TimeZone.hasDaylightSavingTime) ), - GroupBy(#keyPath(TimeZone.hasDaylightSavingTime)), - OrderBy(.descending(#keyPath(TimeZone.hasDaylightSavingTime))) + GroupBy(#keyPath(TimeZone.hasDaylightSavingTime)), + OrderBy(.descending(#keyPath(TimeZone.hasDaylightSavingTime))) )! } ) diff --git a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift index baffca5..9227773 100644 --- a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift @@ -370,8 +370,8 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD self._dataStack = dataStack let listMonitor = dataStack.monitorList( - From(model.entityType) - .orderBy(.descending(#keyPath(OrganismV1.dna))) + From(model.entityType), + OrderBy(.descending(#keyPath(OrganismV1.dna))) ) listMonitor.addObserver(self) self._listMonitor = listMonitor From 3d427c29c4abe20f99e0e9aca91f5a1c01cb62dc Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 20 Sep 2017 00:09:57 +0900 Subject: [PATCH 21/56] added required settings --- CoreStore.xcodeproj/project.pbxproj | 14 +++++++++++++- .../xcshareddata/xcschemes/CoreStore OSX.xcscheme | 4 +++- .../xcshareddata/xcschemes/CoreStore iOS.xcscheme | 4 +++- .../xcshareddata/xcschemes/CoreStore tvOS.xcscheme | 4 +++- .../xcschemes/CoreStore watchOS.xcscheme | 4 +++- .../CoreStoreDemo.xcodeproj/project.pbxproj | 14 +++++++++++++- .../xcshareddata/xcschemes/CoreStoreDemo.xcscheme | 4 +++- 7 files changed, 41 insertions(+), 7 deletions(-) diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index a0b9b84..0946a04 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -1714,7 +1714,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "John Rommel Estropia"; TargetAttributes = { 2F03A52F19C5C6DA005002A5 = { @@ -2584,7 +2584,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -2592,7 +2594,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -2645,7 +2651,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -2653,7 +2661,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/CoreStore.xcodeproj/xcshareddata/xcschemes/CoreStore OSX.xcscheme b/CoreStore.xcodeproj/xcshareddata/xcschemes/CoreStore OSX.xcscheme index abd4547..0fdbcb1 100644 --- a/CoreStore.xcodeproj/xcshareddata/xcschemes/CoreStore OSX.xcscheme +++ b/CoreStore.xcodeproj/xcshareddata/xcschemes/CoreStore OSX.xcscheme @@ -1,6 +1,6 @@ @@ -36,6 +37,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj index 186cbd5..d0deb97 100644 --- a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj +++ b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj @@ -265,7 +265,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "John Rommel Estropia"; TargetAttributes = { B54AAD481AF4D26E00848AE0 = { @@ -371,14 +371,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -420,14 +426,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/CoreStoreDemo/CoreStoreDemo.xcodeproj/xcshareddata/xcschemes/CoreStoreDemo.xcscheme b/CoreStoreDemo/CoreStoreDemo.xcodeproj/xcshareddata/xcschemes/CoreStoreDemo.xcscheme index 783aed7..e9e5323 100644 --- a/CoreStoreDemo/CoreStoreDemo.xcodeproj/xcshareddata/xcschemes/CoreStoreDemo.xcscheme +++ b/CoreStoreDemo/CoreStoreDemo.xcodeproj/xcshareddata/xcschemes/CoreStoreDemo.xcscheme @@ -1,6 +1,6 @@ @@ -45,6 +46,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" From e45d67252c9001818a35ea57ee5f88b1ef6ca454 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 20 Sep 2017 00:24:03 +0900 Subject: [PATCH 22/56] CoreStore querying utilities --- Sources/CoreStore+Querying.swift | 42 ++++++++++++++++++++++++++++++++ Sources/CoreStoreBridge.h | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Sources/CoreStore+Querying.swift b/Sources/CoreStore+Querying.swift index 3d403ef..9e275bb 100644 --- a/Sources/CoreStore+Querying.swift +++ b/Sources/CoreStore+Querying.swift @@ -99,6 +99,12 @@ public extension CoreStore { return self.defaultStack.fetchOne(from, fetchClauses) } + // TODO: docs + public static func fetchOne(_ clauseChain: B) -> B.ObjectType? { + + return self.defaultStack.fetchOne(clauseChain) + } + /** Using the `defaultStack`, fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -123,6 +129,12 @@ public extension CoreStore { return self.defaultStack.fetchAll(from, fetchClauses) } + // TODO: docs + public static func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { + + return self.defaultStack.fetchAll(clauseChain) + } + /** Using the `defaultStack`, fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -147,6 +159,12 @@ public extension CoreStore { return self.defaultStack.fetchCount(from, fetchClauses) } + // TODO: docs + public static func fetchCount(_ clauseChain: B) -> Int? { + + return self.defaultStack.fetchCount(clauseChain) + } + /** Using the `defaultStack`, fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -171,6 +189,12 @@ public extension CoreStore { return self.defaultStack.fetchObjectID(from, fetchClauses) } + // TODO: docs + public static func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? { + + return self.defaultStack.fetchObjectID(clauseChain) + } + /** Using the `defaultStack`, fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses. @@ -195,6 +219,12 @@ public extension CoreStore { return self.defaultStack.fetchObjectIDs(from, fetchClauses) } + // TODO: docs + public static func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? { + + return self.defaultStack.fetchObjectIDs(clauseChain) + } + /** 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. @@ -225,6 +255,12 @@ public extension CoreStore { return self.defaultStack.queryValue(from, selectClause, queryClauses) } + // TODO: docs + public static func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType { + + return self.defaultStack.queryValue(clauseChain) + } + /** 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. @@ -254,4 +290,10 @@ public extension CoreStore { return self.defaultStack.queryAttributes(from, selectClause, queryClauses) } + + // TODO: docs + public static func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary { + + return self.defaultStack.queryAttributes(clauseChain) + } } diff --git a/Sources/CoreStoreBridge.h b/Sources/CoreStoreBridge.h index 43dfc6d..5762ce2 100644 --- a/Sources/CoreStoreBridge.h +++ b/Sources/CoreStoreBridge.h @@ -477,7 +477,7 @@ CSSelect *_Nonnull CSSelectData(CSSelectTerm *_Nonnull selectTerm) CORESTORE_RET a CSSelect clause for querying an NSManagedObjectID value */ CORESTORE_EXTERN -CSSelect *_Nonnull CSSelectObjectID() CORESTORE_RETURNS_RETAINED; +CSSelect *_Nonnull CSSelectObjectID(void) CORESTORE_RETURNS_RETAINED; #pragma mark CSTweak From 3e082d5ed4e4f46924650f6beaa5ba5bc5e7d11e Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 20 Sep 2017 00:45:38 +0900 Subject: [PATCH 23/56] queryBuilders for list monitors --- Sources/CoreStore+Observing.swift | 37 +++++++++++++++++++ Sources/DataStack+Observing.swift | 37 +++++++++++++++++++ Sources/UnsafeDataTransaction+Observing.swift | 37 +++++++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/Sources/CoreStore+Observing.swift b/Sources/CoreStore+Observing.swift index d785255..546f740 100644 --- a/Sources/CoreStore+Observing.swift +++ b/Sources/CoreStore+Observing.swift @@ -67,6 +67,12 @@ public extension CoreStore { return self.defaultStack.monitorList(from, fetchClauses) } + // TODO: docs + public static func monitorList(_ clauseChain: B) -> ListMonitor { + + return self.defaultStack.monitorList(clauseChain.from, clauseChain.fetchClauses) + } + /** Using the `defaultStack`, asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. @@ -91,6 +97,16 @@ public extension CoreStore { self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses) } + // TODO: docs + public static func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { + + self.defaultStack.monitorList( + createAsynchronously: createAsynchronously, + clauseChain.from, + clauseChain.fetchClauses + ) + } + /** Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. @@ -117,6 +133,16 @@ public extension CoreStore { return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) } + // TODO: docs + public static func monitorSectionedList(_ clauseChain: B) -> ListMonitor { + + return self.defaultStack.monitorSectionedList( + clauseChain.from, + clauseChain.sectionBy, + clauseChain.fetchClauses + ) + } + /** Using the `defaultStack`, asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. @@ -142,4 +168,15 @@ public extension CoreStore { self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } + + // TODO: docs + public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { + + self.defaultStack.monitorSectionedList( + createAsynchronously: createAsynchronously, + clauseChain.from, + clauseChain.sectionBy, + clauseChain.fetchClauses + ) + } } diff --git a/Sources/DataStack+Observing.swift b/Sources/DataStack+Observing.swift index 7777e82..1c08c18 100644 --- a/Sources/DataStack+Observing.swift +++ b/Sources/DataStack+Observing.swift @@ -88,6 +88,12 @@ public extension DataStack { ) } + // TODO: docs + public func monitorList(_ clauseChain: B) -> ListMonitor { + + return self.monitorList(clauseChain.from, clauseChain.fetchClauses) + } + /** Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. @@ -130,6 +136,16 @@ public extension DataStack { ) } + // TODO: docs + public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { + + self.monitorList( + createAsynchronously: createAsynchronously, + clauseChain.from, + clauseChain.fetchClauses + ) + } + /** Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. @@ -174,6 +190,16 @@ public extension DataStack { ) } + // TODO: docs + public func monitorSectionedList(_ clauseChain: B) -> ListMonitor { + + return self.monitorSectionedList( + clauseChain.from, + clauseChain.sectionBy, + clauseChain.fetchClauses + ) + } + /** Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. @@ -218,4 +244,15 @@ public extension DataStack { createAsynchronously: createAsynchronously ) } + + // TODO: docs + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { + + self.monitorSectionedList( + createAsynchronously: createAsynchronously, + clauseChain.from, + clauseChain.sectionBy, + clauseChain.fetchClauses + ) + } } diff --git a/Sources/UnsafeDataTransaction+Observing.swift b/Sources/UnsafeDataTransaction+Observing.swift index 181f28b..9a9b861 100644 --- a/Sources/UnsafeDataTransaction+Observing.swift +++ b/Sources/UnsafeDataTransaction+Observing.swift @@ -83,6 +83,12 @@ public extension UnsafeDataTransaction { ) } + // TODO: docs + public func monitorList(_ clauseChain: B) -> ListMonitor { + + return self.monitorList(clauseChain.from, clauseChain.fetchClauses) + } + /** Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. @@ -121,6 +127,16 @@ public extension UnsafeDataTransaction { ) } + // TODO: docs + public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { + + self.monitorList( + createAsynchronously: createAsynchronously, + clauseChain.from, + clauseChain.fetchClauses + ) + } + /** Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. @@ -160,6 +176,16 @@ public extension UnsafeDataTransaction { ) } + // TODO: docs + public func monitorSectionedList(_ clauseChain: B) -> ListMonitor { + + return self.monitorSectionedList( + clauseChain.from, + clauseChain.sectionBy, + clauseChain.fetchClauses + ) + } + /** Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. @@ -199,4 +225,15 @@ public extension UnsafeDataTransaction { createAsynchronously: createAsynchronously ) } + + // TODO: docs + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { + + self.monitorSectionedList( + createAsynchronously: createAsynchronously, + clauseChain.from, + clauseChain.sectionBy, + clauseChain.fetchClauses + ) + } } From 1bfb7451c3d7b7fbf086cf96e9c7262c1d74f25d Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Thu, 21 Sep 2017 07:56:02 +0900 Subject: [PATCH 24/56] keyPath utilities for Select queries --- CoreStoreTests/QueryTests.swift | 242 ++++---- CoreStoreTests/SelectTests.swift | 38 +- Sources/BaseDataTransaction+Querying.swift | 8 +- Sources/CSSelect.swift | 152 ++++- Sources/ChainedClauseBuilder.swift | 130 ++++- Sources/CoreStore+Querying.swift | 8 +- Sources/DataStack+Querying.swift | 8 +- Sources/GroupBy.swift | 31 + .../NSManagedObjectContext+ObjectiveC.swift | 7 +- Sources/NSManagedObjectContext+Querying.swift | 29 +- Sources/QueryableSource.swift | 8 +- Sources/Select.swift | 538 ++++++++++++++++-- 12 files changed, 938 insertions(+), 261 deletions(-) diff --git a/CoreStoreTests/QueryTests.swift b/CoreStoreTests/QueryTests.swift index a82b963..a848c46 100644 --- a/CoreStoreTests/QueryTests.swift +++ b/CoreStoreTests/QueryTests.swift @@ -49,7 +49,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testBoolean)), + Select(#keyPath(TestEntity1.testBoolean)), queryClauses ) XCTAssertNotNil(value) @@ -59,7 +59,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testNumber)), + Select(#keyPath(TestEntity1.testNumber)), queryClauses ) XCTAssertNotNil(value) @@ -69,7 +69,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testNumber)), + Select(#keyPath(TestEntity1.testNumber)), queryClauses ) XCTAssertNotNil(value) @@ -79,7 +79,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testNumber)), + Select(#keyPath(TestEntity1.testNumber)), queryClauses ) XCTAssertNotNil(value) @@ -89,7 +89,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testNumber)), + Select(#keyPath(TestEntity1.testNumber)), queryClauses ) XCTAssertNotNil(value) @@ -99,7 +99,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testNumber)), + Select(#keyPath(TestEntity1.testNumber)), queryClauses ) XCTAssertNotNil(value) @@ -109,7 +109,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testNumber)), + Select(#keyPath(TestEntity1.testNumber)), queryClauses ) XCTAssertNotNil(value) @@ -119,7 +119,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testNumber)), + Select(#keyPath(TestEntity1.testNumber)), queryClauses ) XCTAssertNotNil(value) @@ -129,7 +129,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testNumber)), + Select(#keyPath(TestEntity1.testNumber)), queryClauses ) XCTAssertNotNil(value) @@ -139,7 +139,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testDecimal)), + Select(#keyPath(TestEntity1.testDecimal)), queryClauses ) XCTAssertNotNil(value) @@ -149,7 +149,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testString)), + Select(#keyPath(TestEntity1.testString)), queryClauses ) XCTAssertNotNil(value) @@ -159,7 +159,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testString)), + Select(#keyPath(TestEntity1.testString)), queryClauses ) XCTAssertNotNil(value) @@ -169,7 +169,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testData)), + Select(#keyPath(TestEntity1.testData)), queryClauses ) XCTAssertNotNil(value) @@ -179,7 +179,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testData)), + Select(#keyPath(TestEntity1.testData)), queryClauses ) XCTAssertNotNil(value) @@ -189,7 +189,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testDate)), + Select(#keyPath(TestEntity1.testDate)), queryClauses ) XCTAssertNotNil(value) @@ -199,7 +199,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testDate)), + Select(#keyPath(TestEntity1.testDate)), queryClauses ) XCTAssertNotNil(value) @@ -209,7 +209,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testDate)), + Select(#keyPath(TestEntity1.testDate)), queryClauses ) XCTAssertNil(value) @@ -234,7 +234,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testBoolean))), + Select(.average(#keyPath(TestEntity1.testBoolean))), queryClauses ) XCTAssertNotNil(value) @@ -244,7 +244,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testNumber))), + Select(.average(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -254,7 +254,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testNumber))), + Select(.average(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -264,7 +264,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testNumber))), + Select(.average(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -274,7 +274,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testNumber))), + Select(.average(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -284,7 +284,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testNumber))), + Select(.average(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -294,7 +294,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testNumber))), + Select(.average(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -304,7 +304,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testNumber))), + Select(.average(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -314,7 +314,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testNumber))), + Select(.average(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -324,7 +324,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testDecimal))), + Select(.average(#keyPath(TestEntity1.testDecimal))), queryClauses ) XCTAssertNotNil(value) @@ -334,7 +334,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testString))), + Select(.average(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNil(value) @@ -343,7 +343,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testString))), + Select(.average(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNil(value) @@ -352,7 +352,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testData))), + Select(.average(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNil(value) @@ -361,7 +361,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testData))), + Select(.average(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNil(value) @@ -370,7 +370,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testDate))), + Select(.average(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNil(value) @@ -379,7 +379,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.average(#keyPath(TestEntity1.testDate))), + Select(.average(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNil(value) @@ -388,7 +388,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(#keyPath(TestEntity1.testEntityID)), + Select(#keyPath(TestEntity1.testEntityID)), queryClauses ) XCTAssertNil(value) @@ -412,7 +412,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testBoolean))), + Select(.count(#keyPath(TestEntity1.testBoolean))), queryClauses ) XCTAssertNotNil(value) @@ -422,7 +422,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testNumber))), + Select(.count(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -432,7 +432,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testNumber))), + Select(.count(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -442,7 +442,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testNumber))), + Select(.count(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -452,7 +452,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testNumber))), + Select(.count(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -462,7 +462,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testNumber))), + Select(.count(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -472,7 +472,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testNumber))), + Select(.count(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -482,7 +482,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testNumber))), + Select(.count(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -492,7 +492,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testNumber))), + Select(.count(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -502,7 +502,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testDecimal))), + Select(.count(#keyPath(TestEntity1.testDecimal))), queryClauses ) XCTAssertNil(value) @@ -511,7 +511,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testString))), + Select(.count(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNil(value) @@ -520,7 +520,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testString))), + Select(.count(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNil(value) @@ -529,7 +529,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testData))), + Select(.count(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNil(value) @@ -538,7 +538,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testData))), + Select(.count(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNil(value) @@ -547,7 +547,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testDate))), + Select(.count(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNil(value) @@ -556,7 +556,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testDate))), + Select(.count(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNil(value) @@ -565,7 +565,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.count(#keyPath(TestEntity1.testEntityID))), + Select(.count(#keyPath(TestEntity1.testEntityID))), queryClauses ) XCTAssertNil(value) @@ -589,7 +589,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testBoolean))), + Select(.maximum(#keyPath(TestEntity1.testBoolean))), queryClauses ) XCTAssertNotNil(value) @@ -599,7 +599,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testNumber))), + Select(.maximum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -609,7 +609,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testNumber))), + Select(.maximum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -619,7 +619,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testNumber))), + Select(.maximum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -629,7 +629,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testNumber))), + Select(.maximum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -639,7 +639,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testNumber))), + Select(.maximum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -649,7 +649,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testNumber))), + Select(.maximum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -659,7 +659,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testNumber))), + Select(.maximum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -669,7 +669,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testNumber))), + Select(.maximum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -679,7 +679,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testDecimal))), + Select(.maximum(#keyPath(TestEntity1.testDecimal))), queryClauses ) XCTAssertNotNil(value) @@ -689,7 +689,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testString))), + Select(.maximum(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNotNil(value) @@ -699,7 +699,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testString))), + Select(.maximum(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNotNil(value) @@ -709,7 +709,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testData))), + Select(.maximum(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNotNil(value) @@ -719,7 +719,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testData))), + Select(.maximum(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNotNil(value) @@ -729,7 +729,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testDate))), + Select(.maximum(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNotNil(value) @@ -739,7 +739,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testDate))), + Select(.maximum(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNotNil(value) @@ -749,7 +749,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.maximum(#keyPath(TestEntity1.testEntityID))), + Select(.maximum(#keyPath(TestEntity1.testEntityID))), queryClauses ) XCTAssertNil(value) @@ -773,7 +773,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testBoolean))), + Select(.minimum(#keyPath(TestEntity1.testBoolean))), queryClauses ) XCTAssertNotNil(value) @@ -783,7 +783,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testNumber))), + Select(.minimum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -793,7 +793,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testNumber))), + Select(.minimum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -803,7 +803,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testNumber))), + Select(.minimum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -813,7 +813,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testNumber))), + Select(.minimum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -823,7 +823,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testNumber))), + Select(.minimum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -833,7 +833,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testNumber))), + Select(.minimum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -843,7 +843,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testNumber))), + Select(.minimum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -853,7 +853,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testNumber))), + Select(.minimum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -863,7 +863,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testDecimal))), + Select(.minimum(#keyPath(TestEntity1.testDecimal))), queryClauses ) XCTAssertNotNil(value) @@ -873,7 +873,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testString))), + Select(.minimum(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNotNil(value) @@ -883,7 +883,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testString))), + Select(.minimum(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNotNil(value) @@ -893,7 +893,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testData))), + Select(.minimum(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNotNil(value) @@ -903,7 +903,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testData))), + Select(.minimum(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNotNil(value) @@ -913,7 +913,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testDate))), + Select(.minimum(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNotNil(value) @@ -923,7 +923,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testDate))), + Select(.minimum(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNotNil(value) @@ -933,7 +933,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.minimum(#keyPath(TestEntity1.testEntityID))), + Select(.minimum(#keyPath(TestEntity1.testEntityID))), queryClauses ) XCTAssertNil(value) @@ -957,7 +957,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testBoolean))), + Select(.sum(#keyPath(TestEntity1.testBoolean))), queryClauses ) XCTAssertNotNil(value) @@ -967,7 +967,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testNumber))), + Select(.sum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -977,7 +977,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testNumber))), + Select(.sum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -987,7 +987,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testNumber))), + Select(.sum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -997,7 +997,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testNumber))), + Select(.sum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -1007,7 +1007,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testNumber))), + Select(.sum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -1017,7 +1017,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testNumber))), + Select(.sum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -1027,7 +1027,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testNumber))), + Select(.sum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -1037,7 +1037,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testNumber))), + Select(.sum(#keyPath(TestEntity1.testNumber))), queryClauses ) XCTAssertNotNil(value) @@ -1047,7 +1047,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testDecimal))), + Select(.sum(#keyPath(TestEntity1.testDecimal))), queryClauses ) XCTAssertNotNil(value) @@ -1057,7 +1057,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testString))), + Select(.sum(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNil(value) @@ -1066,7 +1066,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testString))), + Select(.sum(#keyPath(TestEntity1.testString))), queryClauses ) XCTAssertNil(value) @@ -1075,7 +1075,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testData))), + Select(.sum(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNil(value) @@ -1084,7 +1084,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testData))), + Select(.sum(#keyPath(TestEntity1.testData))), queryClauses ) XCTAssertNil(value) @@ -1093,7 +1093,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testDate))), + Select(.sum(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNil(value) @@ -1102,7 +1102,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testDate))), + Select(.sum(#keyPath(TestEntity1.testDate))), queryClauses ) XCTAssertNil(value) @@ -1111,7 +1111,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.sum(#keyPath(TestEntity1.testEntityID))), + Select(.sum(#keyPath(TestEntity1.testEntityID))), queryClauses ) XCTAssertNil(value) @@ -1135,7 +1135,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1144,7 +1144,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1153,7 +1153,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1162,7 +1162,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1171,7 +1171,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1180,7 +1180,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1189,7 +1189,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1198,7 +1198,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1207,7 +1207,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1216,7 +1216,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1225,7 +1225,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1234,7 +1234,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1243,7 +1243,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1252,7 +1252,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1261,7 +1261,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1270,7 +1270,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNil(value) @@ -1279,7 +1279,7 @@ class QueryTests: BaseTestDataTestCase { let value = stack.queryValue( from, - Select(.objectID()), + Select(.objectID()), queryClauses ) XCTAssertNotNil(value) @@ -1304,7 +1304,7 @@ class QueryTests: BaseTestDataTestCase { let values = stack.queryAttributes( from, - Select( + Select( #keyPath(TestEntity1.testBoolean), #keyPath(TestEntity1.testNumber), #keyPath(TestEntity1.testDecimal), @@ -1355,7 +1355,7 @@ class QueryTests: BaseTestDataTestCase { let values = stack.queryAttributes( from, - Select( + Select( .sum(#keyPath(TestEntity1.testBoolean)), .count(#keyPath(TestEntity1.testNumber)), .maximum(#keyPath(TestEntity1.testNumber)), diff --git a/CoreStoreTests/SelectTests.swift b/CoreStoreTests/SelectTests.swift index 0c10154..a2067e0 100644 --- a/CoreStoreTests/SelectTests.swift +++ b/CoreStoreTests/SelectTests.swift @@ -38,7 +38,7 @@ final class SelectTests: XCTestCase { do { - let term: SelectTerm = "attribute" + let term: SelectTerm = "attribute" XCTAssertEqual(term, SelectTerm.attribute("attribute")) XCTAssertNotEqual(term, SelectTerm.attribute("attribute2")) XCTAssertNotEqual(term, SelectTerm.average("attribute")) @@ -58,7 +58,7 @@ final class SelectTests: XCTestCase { } do { - let term = SelectTerm.attribute("attribute") + let term = SelectTerm.attribute("attribute") XCTAssertNotEqual(term, SelectTerm.attribute("attribute2")) XCTAssertNotEqual(term, SelectTerm.average("attribute")) XCTAssertNotEqual(term, SelectTerm.count("attribute")) @@ -82,7 +82,7 @@ final class SelectTests: XCTestCase { do { - let term = SelectTerm.average("attribute") + let term = SelectTerm.average("attribute") XCTAssertEqual(term, SelectTerm.average("attribute")) XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.average("attribute2")) @@ -106,7 +106,7 @@ final class SelectTests: XCTestCase { } do { - let term = SelectTerm.average("attribute", as: "alias") + let term = SelectTerm.average("attribute", as: "alias") XCTAssertEqual(term, SelectTerm.average("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.average("attribute2")) @@ -135,7 +135,7 @@ final class SelectTests: XCTestCase { do { - let term = SelectTerm.count("attribute") + let term = SelectTerm.count("attribute") XCTAssertEqual(term, SelectTerm.count("attribute")) XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.count("attribute2")) @@ -159,7 +159,7 @@ final class SelectTests: XCTestCase { } do { - let term = SelectTerm.count("attribute", as: "alias") + let term = SelectTerm.count("attribute", as: "alias") XCTAssertEqual(term, SelectTerm.count("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.count("attribute2")) @@ -188,7 +188,7 @@ final class SelectTests: XCTestCase { do { - let term = SelectTerm.maximum("attribute") + let term = SelectTerm.maximum("attribute") XCTAssertEqual(term, SelectTerm.maximum("attribute")) XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.maximum("attribute2")) @@ -212,7 +212,7 @@ final class SelectTests: XCTestCase { } do { - let term = SelectTerm.maximum("attribute", as: "alias") + let term = SelectTerm.maximum("attribute", as: "alias") XCTAssertEqual(term, SelectTerm.maximum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.maximum("attribute2")) @@ -241,7 +241,7 @@ final class SelectTests: XCTestCase { do { - let term = SelectTerm.minimum("attribute") + let term = SelectTerm.minimum("attribute") XCTAssertEqual(term, SelectTerm.minimum("attribute")) XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.minimum("attribute2")) @@ -265,7 +265,7 @@ final class SelectTests: XCTestCase { } do { - let term = SelectTerm.minimum("attribute", as: "alias") + let term = SelectTerm.minimum("attribute", as: "alias") XCTAssertEqual(term, SelectTerm.minimum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.minimum("attribute2")) @@ -294,7 +294,7 @@ final class SelectTests: XCTestCase { do { - let term = SelectTerm.sum("attribute") + let term = SelectTerm.sum("attribute") XCTAssertEqual(term, SelectTerm.sum("attribute")) XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.sum("attribute2")) @@ -318,7 +318,7 @@ final class SelectTests: XCTestCase { } do { - let term = SelectTerm.sum("attribute", as: "alias") + let term = SelectTerm.sum("attribute", as: "alias") XCTAssertEqual(term, SelectTerm.sum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.sum("attribute2")) @@ -347,7 +347,7 @@ final class SelectTests: XCTestCase { do { - let term = SelectTerm.objectID() + let term = SelectTerm.objectID() XCTAssertEqual(term, SelectTerm.objectID()) XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias")) XCTAssertNotEqual(term, SelectTerm.attribute("attribute")) @@ -368,7 +368,7 @@ final class SelectTests: XCTestCase { } do { - let term = SelectTerm.objectID(as: "alias") + let term = SelectTerm.objectID(as: "alias") XCTAssertEqual(term, SelectTerm.objectID(as: "alias")) XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias2")) XCTAssertNotEqual(term, SelectTerm.objectID()) @@ -393,12 +393,12 @@ final class SelectTests: XCTestCase { @objc dynamic func test_ThatSelectClauses_ConfigureCorrectly() { - let term1 = SelectTerm.attribute("attribute1") - let term2 = SelectTerm.attribute("attribute2") - let term3 = SelectTerm.attribute("attribute3") + let term1 = SelectTerm.attribute("attribute1") + let term2 = SelectTerm.attribute("attribute2") + let term3 = SelectTerm.attribute("attribute3") do { - let select = Select(term1, term2, term3) + let select = Select(term1, term2, term3) XCTAssertEqual(select.selectTerms, [term1, term2, term3]) XCTAssertNotEqual(select.selectTerms, [term1, term3, term2]) XCTAssertNotEqual(select.selectTerms, [term2, term1, term3]) @@ -408,7 +408,7 @@ final class SelectTests: XCTestCase { } do { - let select = Select([term1, term2, term3]) + let select = Select([term1, term2, term3]) XCTAssertEqual(select.selectTerms, [term1, term2, term3]) XCTAssertNotEqual(select.selectTerms, [term1, term3, term2]) XCTAssertNotEqual(select.selectTerms, [term2, term1, term3]) diff --git a/Sources/BaseDataTransaction+Querying.swift b/Sources/BaseDataTransaction+Querying.swift index a5f0b11..0ece0ae 100644 --- a/Sources/BaseDataTransaction+Querying.swift +++ b/Sources/BaseDataTransaction+Querying.swift @@ -354,7 +354,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter 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? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -373,7 +373,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter 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? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -402,7 +402,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter 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...) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { CoreStore.assert( self.isRunningInAllowedQueue(), @@ -421,7 +421,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { - parameter 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]) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { CoreStore.assert( self.isRunningInAllowedQueue(), diff --git a/Sources/CSSelect.swift b/Sources/CSSelect.swift index c06b4f6..7312ff8 100644 --- a/Sources/CSSelect.swift +++ b/Sources/CSSelect.swift @@ -35,7 +35,7 @@ import CoreData - SeeAlso: `SelectTerm` */ @objc -public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { +public final class CSSelectTerm: NSObject { /** Provides a `CSSelectTerm` to a `CSSelect` clause for querying an entity attribute. @@ -175,11 +175,11 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { // MARK: CoreStoreObjectiveCType - public let bridgeToSwift: SelectTerm + public let bridgeToSwift: SelectTerm - public init(_ swiftValue: SelectTerm) { + public init(_ swiftValue: SelectTerm) { - self.bridgeToSwift = swiftValue + self.bridgeToSwift = swiftValue.downcast() super.init() } } @@ -187,7 +187,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { // MARK: - SelectTerm -extension SelectTerm: CoreStoreSwiftType { +extension SelectTerm where D: NSManagedObject { // MARK: CoreStoreSwiftType @@ -195,6 +195,24 @@ extension SelectTerm: CoreStoreSwiftType { return CSSelectTerm(self) } + + + // MARK: FilePrivate + + fileprivate func downcast() -> SelectTerm { + + switch self { + + case ._attribute(let keyPath): + return SelectTerm._attribute(keyPath) + + case ._aggregate(let function, let keyPath, let alias, let nativeType): + return SelectTerm._aggregate(function: function, keyPath: keyPath, alias: alias, nativeType: nativeType) + + case ._identity(let alias, let nativeType): + return SelectTerm._identity(alias: alias, nativeType: nativeType) + } + } } @@ -221,7 +239,7 @@ public final class CSSelect: NSObject { @objc public convenience init(numberTerm: CSSelectTerm) { - self.init(Select(numberTerm.bridgeToSwift)) + self.init(Select(numberTerm.bridgeToSwift)) } /** @@ -237,7 +255,7 @@ public final class CSSelect: NSObject { @objc public convenience init(decimalTerm: CSSelectTerm) { - self.init(Select(decimalTerm.bridgeToSwift)) + self.init(Select(decimalTerm.bridgeToSwift)) } /** @@ -253,7 +271,7 @@ public final class CSSelect: NSObject { @objc public convenience init(stringTerm: CSSelectTerm) { - self.init(Select(stringTerm.bridgeToSwift)) + self.init(Select(stringTerm.bridgeToSwift)) } /** @@ -269,7 +287,7 @@ public final class CSSelect: NSObject { @objc public convenience init(dateTerm: CSSelectTerm) { - self.init(Select(dateTerm.bridgeToSwift)) + self.init(Select(dateTerm.bridgeToSwift)) } /** @@ -285,7 +303,7 @@ public final class CSSelect: NSObject { @objc public convenience init(dataTerm: CSSelectTerm) { - self.init(Select(dataTerm.bridgeToSwift)) + self.init(Select(dataTerm.bridgeToSwift)) } /** @@ -300,7 +318,7 @@ public final class CSSelect: NSObject { @objc public convenience init(objectIDTerm: ()) { - self.init(Select(.objectID())) + self.init(Select(.objectID())) } /** @@ -316,7 +334,7 @@ public final class CSSelect: NSObject { @objc public static func dictionaryForTerm(_ term: CSSelectTerm) -> CSSelect { - return self.init(Select(term.bridgeToSwift)) + return self.init(Select(term.bridgeToSwift)) } /** @@ -335,7 +353,7 @@ public final class CSSelect: NSObject { @objc public static func dictionaryForTerms(_ terms: [CSSelectTerm]) -> CSSelect { - return self.init(Select(terms.map { $0.bridgeToSwift })) + return self.init(Select(terms.map { $0.bridgeToSwift })) } @@ -365,18 +383,18 @@ public final class CSSelect: NSObject { // MARK: CoreStoreObjectiveCType - public init(_ swiftValue: Select) { + public init(_ swiftValue: Select) { self.attributeType = T.cs_rawAttributeType - self.selectTerms = swiftValue.selectTerms + self.selectTerms = swiftValue.selectTerms.map({ $0.downcast() }) self.bridgeToSwift = swiftValue super.init() } - public init(_ swiftValue: Select) { + public init(_ swiftValue: Select) { self.attributeType = .undefinedAttributeType - self.selectTerms = swiftValue.selectTerms + self.selectTerms = swiftValue.selectTerms.map({ $0.downcast() }) self.bridgeToSwift = swiftValue super.init() } @@ -385,7 +403,95 @@ public final class CSSelect: NSObject { // MARK: Internal internal let attributeType: NSAttributeType - internal let selectTerms: [SelectTerm] + internal let selectTerms: [SelectTerm] + + + // MARK: Internal + + internal func applyToFetchRequest(_ fetchRequest: NSFetchRequest) { + + fetchRequest.includesPendingChanges = false + fetchRequest.resultType = .dictionaryResultType + + func attributeDescription(for keyPath: String, in entity: NSEntityDescription) -> NSAttributeDescription? { + + let components = keyPath.components(separatedBy: ".") + switch components.count { + + case 0: + return nil + + case 1: + return entity.attributesByName[components[0]] + + default: + guard let relationship = entity.relationshipsByName[components[0]], + let destinationEntity = relationship.destinationEntity else { + + return nil + } + return attributeDescription( + for: components.dropFirst().joined(separator: "."), + in: destinationEntity + ) + } + } + + var propertiesToFetch = [Any]() + for term in self.selectTerms { + + switch term { + + case ._attribute(let keyPath): + propertiesToFetch.append(keyPath) + + case ._aggregate(let function, let keyPath, let alias, let nativeType): + let entityDescription = fetchRequest.entity! + if let attributeDescription = attributeDescription(for: keyPath, in: entityDescription) { + + let expressionDescription = NSExpressionDescription() + expressionDescription.name = alias + if nativeType == .undefinedAttributeType { + + expressionDescription.expressionResultType = attributeDescription.attributeType + } + else { + + expressionDescription.expressionResultType = nativeType + } + expressionDescription.expression = NSExpression( + forFunction: function, + arguments: [NSExpression(forKeyPath: keyPath)] + ) + propertiesToFetch.append(expressionDescription) + } + else { + + CoreStore.log( + .warning, + message: "The key path \"\(keyPath)\" could not be resolved in entity \(cs_typeName(entityDescription.managedObjectClassName)) as an attribute and will be ignored by \(cs_typeName(self)) query clause." + ) + } + + case ._identity(let alias, let nativeType): + let expressionDescription = NSExpressionDescription() + expressionDescription.name = alias + if nativeType == .undefinedAttributeType { + + expressionDescription.expressionResultType = .objectIDAttributeType + } + else { + + expressionDescription.expressionResultType = nativeType + } + expressionDescription.expression = NSExpression.expressionForEvaluatedObject() + + propertiesToFetch.append(expressionDescription) + } + } + + fetchRequest.propertiesToFetch = propertiesToFetch + } // MARK: Private @@ -396,7 +502,7 @@ public final class CSSelect: NSObject { // MARK: - Select -extension Select: CoreStoreSwiftType { +extension Select where D: NSManagedObject { // MARK: CoreStoreSwiftType @@ -404,4 +510,12 @@ extension Select: CoreStoreSwiftType { return CSSelect(self) } + + + // MARK: FilePrivate + + fileprivate func downcast() -> Select { + + return Select(self.selectTerms.map({ $0.downcast() })) + } } diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index 7974877..7bcaae8 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -41,7 +41,7 @@ public protocol QueryChainableBuilderType { associatedtype ResultType: SelectResultType var from: From { get set } - var select: Select { get set } + var select: Select { get set } var queryClauses: [QueryClause] { get set } } @@ -79,7 +79,7 @@ public struct QueryChainBuilder: QueryCha public typealias ResultType = R public var from: From - public var select: Select + public var select: Select public var queryClauses: [QueryClause] = [] } @@ -101,12 +101,12 @@ public struct SectionMonitorChainBuilder: SectionMonitorBuilde public extension From { - public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { + public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { return self.select(resultType, [selectTerm] + selectTerms) } - public func select(_ resultType: R.Type, _ selectTerms: [SelectTerm]) -> QueryChainBuilder { + public func select(_ resultType: R.Type, _ selectTerms: [SelectTerm]) -> QueryChainBuilder { return .init( from: self, @@ -170,6 +170,97 @@ public extension From { } } +public extension From where D: NSManagedObject { + + public func select(_ keyPath: KeyPath) -> QueryChainBuilder { + + return self.select(R.self, [SelectTerm.attribute(keyPath)]) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath) -> SectionMonitorChainBuilder { + + return self.sectionBy(sectionKeyPath._kvcKeyPathString!, { $0 }) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { + + return self.sectionBy(sectionKeyPath._kvcKeyPathString!, sectionIndexTransformer) + } +} + +public extension From where D: CoreStoreObject { + + public func select(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { + + return self.select(R.self, [SelectTerm.attribute(keyPath)]) + } + + public func select(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { + + return self.select(R.self, [SelectTerm.attribute(keyPath)]) + } + + public func select(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { + + return self.select(R.self, [SelectTerm.attribute(keyPath)]) + } + + public func select(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { + + return self.select(R.self, [SelectTerm.attribute(keyPath)]) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath.Required>) -> SectionMonitorChainBuilder { + + return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath.Optional>) -> SectionMonitorChainBuilder { + + return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath.Required>) -> SectionMonitorChainBuilder { + + return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath.Optional>) -> SectionMonitorChainBuilder { + + return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath.Required>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { + + return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath.Optional>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { + + return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath.Required>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { + + return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) + } + + @available(OSX 10.12, *) + public func sectionBy(_ sectionKeyPath: KeyPath.Optional>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { + + return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) + } +} + public extension FetchChainBuilder { public func `where`(_ clause: Where) -> FetchChainBuilder { @@ -274,6 +365,37 @@ public extension QueryChainBuilder { } } +public extension QueryChainBuilder where D: NSManagedObject { + + public func groupBy(_ keyPath: KeyPath) -> QueryChainBuilder { + + return self.groupBy(GroupBy(keyPath)) + } +} + +public extension QueryChainBuilder where D: CoreStoreObject { + + public func groupBy(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { + + return self.groupBy(GroupBy(keyPath)) + } + + public func groupBy(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { + + return self.groupBy(GroupBy(keyPath)) + } + + public func groupBy(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { + + return self.groupBy(GroupBy(keyPath)) + } + + public func groupBy(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { + + return self.groupBy(GroupBy(keyPath)) + } +} + @available(OSX 10.12, *) public extension SectionMonitorChainBuilder { diff --git a/Sources/CoreStore+Querying.swift b/Sources/CoreStore+Querying.swift index 9e275bb..4a6e43f 100644 --- a/Sources/CoreStore+Querying.swift +++ b/Sources/CoreStore+Querying.swift @@ -235,7 +235,7 @@ public extension CoreStore { - parameter 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? { + public static func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { return self.defaultStack.queryValue(from, selectClause, queryClauses) } @@ -250,7 +250,7 @@ public extension CoreStore { - parameter 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? { + public static func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { return self.defaultStack.queryValue(from, selectClause, queryClauses) } @@ -271,7 +271,7 @@ public extension CoreStore { - parameter 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...) -> [[String: Any]]? { + public static func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { return self.defaultStack.queryAttributes(from, selectClause, queryClauses) } @@ -286,7 +286,7 @@ public extension CoreStore { - parameter 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]) -> [[String: Any]]? { + public static func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { return self.defaultStack.queryAttributes(from, selectClause, queryClauses) } diff --git a/Sources/DataStack+Querying.swift b/Sources/DataStack+Querying.swift index 5f09687..409644e 100644 --- a/Sources/DataStack+Querying.swift +++ b/Sources/DataStack+Querying.swift @@ -300,7 +300,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter 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? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { CoreStore.assert( Thread.isMainThread, @@ -319,7 +319,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter 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? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { CoreStore.assert( Thread.isMainThread, @@ -348,7 +348,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter 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...) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { CoreStore.assert( Thread.isMainThread, @@ -367,7 +367,7 @@ extension DataStack: FetchableSource, QueryableSource { - parameter 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]) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { CoreStore.assert( Thread.isMainThread, diff --git a/Sources/GroupBy.swift b/Sources/GroupBy.swift index 1ee748b..467fc62 100644 --- a/Sources/GroupBy.swift +++ b/Sources/GroupBy.swift @@ -103,6 +103,37 @@ public struct GroupBy: GroupByClause, QueryClause, Hashable { } } +public extension GroupBy where D: NSManagedObject { + + public init(_ keyPath: KeyPath) { + + self.init([keyPath._kvcKeyPathString!]) + } +} + +public extension GroupBy where D: CoreStoreObject { + + public init(_ keyPath: KeyPath.Required>) { + + self.init([D.meta[keyPath: keyPath].keyPath]) + } + + public init(_ keyPath: KeyPath.Optional>) { + + self.init([D.meta[keyPath: keyPath].keyPath]) + } + + public init(_ keyPath: KeyPath.Required>) { + + self.init([D.meta[keyPath: keyPath].keyPath]) + } + + public init(_ keyPath: KeyPath.Optional>) { + + self.init([D.meta[keyPath: keyPath].keyPath]) + } +} + // MARK: - GroupByClause diff --git a/Sources/NSManagedObjectContext+ObjectiveC.swift b/Sources/NSManagedObjectContext+ObjectiveC.swift index 91bd888..deef584 100644 --- a/Sources/NSManagedObjectContext+ObjectiveC.swift +++ b/Sources/NSManagedObjectContext+ObjectiveC.swift @@ -142,15 +142,14 @@ internal extension NSManagedObjectContext { fetchRequest.fetchLimit = 0 - let selectTerms = selectClause.selectTerms - selectTerms.applyToFetchRequest(fetchRequest, owner: selectClause) + selectClause.applyToFetchRequest(fetchRequest) queryClauses.forEach { $0.applyToFetchRequest(fetchRequest) } guard storeFound else { return nil } - return self.queryValue(selectTerms, fetchRequest: fetchRequest.dynamicCast()) + return self.queryValue(selectClause.selectTerms, fetchRequest: fetchRequest.dynamicCast()) } @nonobjc @@ -161,7 +160,7 @@ internal extension NSManagedObjectContext { fetchRequest.fetchLimit = 0 - selectClause.selectTerms.applyToFetchRequest(fetchRequest, owner: selectClause) + selectClause.applyToFetchRequest(fetchRequest) queryClauses.forEach { $0.applyToFetchRequest(fetchRequest) } guard storeFound else { diff --git a/Sources/NSManagedObjectContext+Querying.swift b/Sources/NSManagedObjectContext+Querying.swift index 69a268b..51f626b 100644 --- a/Sources/NSManagedObjectContext+Querying.swift +++ b/Sources/NSManagedObjectContext+Querying.swift @@ -279,51 +279,50 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { // MARK: QueryableSource @nonobjc - public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? { return self.queryValue(from, selectClause, queryClauses) } @nonobjc - public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { + public func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 - let selectTerms = selectClause.selectTerms - selectTerms.applyToFetchRequest(fetchRequest, owner: selectClause) + selectClause.applyToFetchRequest(fetchRequest) queryClauses.forEach { $0.applyToFetchRequest(fetchRequest) } guard storeFound else { return nil } - return self.queryValue(selectTerms, fetchRequest: fetchRequest) + return self.queryValue(selectClause.selectTerms, fetchRequest: fetchRequest) } @nonobjc - public func queryValue(_ clauseChain: B) -> B.ResultType? where B : QueryChainableBuilderType, B.ResultType : QueryableAttributeType { + public func queryValue(_ clauseChain: B) -> B.ResultType? where B: QueryChainableBuilderType, B.ResultType: QueryableAttributeType { return self.queryValue(clauseChain.from, clauseChain.select, clauseChain.queryClauses) } @nonobjc - public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? { return self.queryAttributes(from, selectClause, queryClauses) } @nonobjc - public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { + public func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? { let fetchRequest = CoreStoreFetchRequest() let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 - selectClause.selectTerms.applyToFetchRequest(fetchRequest, owner: selectClause) + selectClause.applyToFetchRequest(fetchRequest) queryClauses.forEach { $0.applyToFetchRequest(fetchRequest) } guard storeFound else { @@ -498,7 +497,7 @@ internal extension NSManagedObjectContext { // MARK: Querying @nonobjc - internal func queryValue(_ selectTerms: [SelectTerm], fetchRequest: NSFetchRequest) -> U? { + internal func queryValue(_ selectTerms: [SelectTerm], fetchRequest: NSFetchRequest) -> U? { var fetchResults: [Any]? var fetchError: Error? @@ -516,9 +515,9 @@ internal extension NSManagedObjectContext { if let fetchResults = fetchResults { if let rawResult = fetchResults.first as? NSDictionary, - let rawObject = rawResult[selectTerms.keyPathForFirstSelectTerm()] as? U.QueryableNativeType { + let rawObject = rawResult[selectTerms.first!.keyPathString] as? U.QueryableNativeType { - return Select.ReturnType.cs_fromQueryableNativeType(rawObject) + return Select.ReturnType.cs_fromQueryableNativeType(rawObject) } return nil } @@ -531,7 +530,7 @@ internal extension NSManagedObjectContext { } @nonobjc - internal func queryValue(_ selectTerms: [SelectTerm], fetchRequest: NSFetchRequest) -> Any? { + internal func queryValue(_ selectTerms: [SelectTerm], fetchRequest: NSFetchRequest) -> Any? { var fetchResults: [Any]? var fetchError: Error? @@ -549,7 +548,7 @@ internal extension NSManagedObjectContext { if let fetchResults = fetchResults { if let rawResult = fetchResults.first as? NSDictionary, - let rawObject = rawResult[selectTerms.keyPathForFirstSelectTerm()] { + let rawObject = rawResult[selectTerms.first!.keyPathString] { return rawObject } @@ -581,7 +580,7 @@ internal extension NSManagedObjectContext { } if let fetchResults = fetchResults { - return Select.ReturnType.cs_fromQueryResultsNativeType(fetchResults) + return NSDictionary.cs_fromQueryResultsNativeType(fetchResults) } CoreStore.log( diff --git a/Sources/QueryableSource.swift b/Sources/QueryableSource.swift index cf3da07..b8fb498 100644 --- a/Sources/QueryableSource.swift +++ b/Sources/QueryableSource.swift @@ -44,7 +44,7 @@ public protocol QueryableSource: class { - parameter 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. */ - func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? + func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> U? /** Queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. @@ -56,7 +56,7 @@ public protocol QueryableSource: class { - parameter 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. */ - func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? + func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? // TODO: docs func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType @@ -71,7 +71,7 @@ public protocol QueryableSource: class { - parameter 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. */ - func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? + func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: QueryClause...) -> [[String: Any]]? /** 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. @@ -83,7 +83,7 @@ public protocol QueryableSource: class { - parameter 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. */ - func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? + func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? // TODO: docs func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary diff --git a/Sources/Select.swift b/Sources/Select.swift index 1c5bc43..98a9786 100644 --- a/Sources/Select.swift +++ b/Sources/Select.swift @@ -52,7 +52,7 @@ public protocol SelectAttributesResultType: SelectResultType { /** The `SelectTerm` is passed to the `Select` clause to indicate the attributes/aggregate keys to be queried. */ -public enum SelectTerm: ExpressibleByStringLiteral, Hashable { +public enum SelectTerm: ExpressibleByStringLiteral, Hashable { /** 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: @@ -74,7 +74,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter keyPath: the attribute name - returns: a `SelectTerm` to a `Select` clause for querying an entity attribute */ - public static func attribute(_ keyPath: KeyPathString) -> SelectTerm { + public static func attribute(_ keyPath: KeyPathString) -> SelectTerm { return ._attribute(keyPath) } @@ -91,7 +91,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { + public static func average(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "average:", @@ -113,7 +113,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { + public static func count(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "count:", @@ -135,7 +135,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { + public static func maximum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "max:", @@ -157,7 +157,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { + public static func minimum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "min:", @@ -179,7 +179,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { + public static func sum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm { return ._aggregate( function: "sum:", @@ -202,7 +202,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter 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 "objecID" is used - returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute */ - public static func objectID(as alias: KeyPathString? = nil) -> SelectTerm { + public static func objectID(as alias: KeyPathString? = nil) -> SelectTerm { return ._identity( alias: alias ?? "objectID", @@ -231,7 +231,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { // MARK: Equatable - public static func == (lhs: SelectTerm, rhs: SelectTerm) -> Bool { + public static func == (lhs: SelectTerm, rhs: SelectTerm) -> Bool { switch (lhs, rhs) { @@ -248,7 +248,9 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { case (._identity(let alias1, let nativeType1), ._identity(let alias2, let nativeType2)): return alias1 == alias2 && nativeType1 == nativeType2 - default: + case (._attribute, _), + (._aggregate, _), + (._identity, _): return false } } @@ -277,6 +279,355 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { case _attribute(KeyPathString) case _aggregate(function: String, keyPath: KeyPathString, alias: String, nativeType: NSAttributeType) case _identity(alias: String, nativeType: NSAttributeType) + + internal var keyPathString: String { + + switch self { + + case ._attribute(let keyPath): return keyPath + case ._aggregate(_, _, let alias, _): return alias + case ._identity(let alias, _): return alias + } + } +} + +extension SelectTerm where D: NSManagedObject { + + /** + Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. + - parameter keyPath: the attribute name + - returns: a `SelectTerm` to a `Select` clause for querying an entity attribute + */ + public static func attribute(_ keyPath: KeyPath) -> SelectTerm { + + return self.attribute(keyPath._kvcKeyPathString!) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute. + - parameter keyPath: the attribute name + - parameter 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: KeyPathString? = nil) -> SelectTerm { + + return self.average(keyPath._kvcKeyPathString!, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for a count query. + - parameter keyPath: the attribute name + - parameter 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: KeyPathString? = nil) -> SelectTerm { + + return self.count(keyPath._kvcKeyPathString!, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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: KeyPathString? = nil) -> SelectTerm { + + return self.maximum(keyPath._kvcKeyPathString!, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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: KeyPathString? = nil) -> SelectTerm { + + return self.minimum(keyPath._kvcKeyPathString!, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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: KeyPathString? = nil) -> SelectTerm { + + return self.sum(keyPath._kvcKeyPathString!, as: alias) + } +} + +extension SelectTerm where D: CoreStoreObject { + + /** + Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. + - parameter keyPath: the attribute name + - returns: a `SelectTerm` to a `Select` clause for querying an entity attribute + */ + public static func attribute(_ keyPath: KeyPath.Required>) -> SelectTerm { + + return self.attribute(D.meta[keyPath: keyPath].keyPath) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. + - parameter keyPath: the attribute name + - returns: a `SelectTerm` to a `Select` clause for querying an entity attribute + */ + public static func attribute(_ keyPath: KeyPath.Optional>) -> SelectTerm { + + return self.attribute(D.meta[keyPath: keyPath].keyPath) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. + - parameter keyPath: the attribute name + - returns: a `SelectTerm` to a `Select` clause for querying an entity attribute + */ + public static func attribute(_ keyPath: KeyPath.Required>) -> SelectTerm { + + return self.attribute(D.meta[keyPath: keyPath].keyPath) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. + - parameter keyPath: the attribute name + - returns: a `SelectTerm` to a `Select` clause for querying an entity attribute + */ + public static func attribute(_ keyPath: KeyPath.Optional>) -> SelectTerm { + + return self.attribute(D.meta[keyPath: keyPath].keyPath) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.average(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.average(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.average(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.average(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for a count query. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.count(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for a count query. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.count(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for a count query. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.count(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for a count query. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.count(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.maximum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.maximum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.maximum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.maximum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.minimum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.minimum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.minimum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.minimum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.sum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.sum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Required>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.sum(D.meta[keyPath: keyPath].keyPath, as: alias) + } + + /** + Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute. + - parameter keyPath: the attribute name + - parameter 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.Optional>, as alias: KeyPathString? = nil) -> SelectTerm { + + return self.sum(D.meta[keyPath: keyPath].keyPath, as: alias) + } } @@ -308,12 +659,7 @@ public enum SelectTerm: ExpressibleByStringLiteral, Hashable { - parameter sortDescriptors: a series of `NSSortDescriptor`s */ -public struct Select: Hashable { - - /** - The `SelectResultType` type for the query's return value - */ - public typealias ReturnType = T +public struct Select: SelectClause, Hashable { /** Initializes a `Select` clause with a list of `SelectTerm`s @@ -321,7 +667,7 @@ public struct Select: Hashable { - parameter selectTerm: a `SelectTerm` - parameter selectTerms: a series of `SelectTerm`s */ - public init(_ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) { + public init(_ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) { self.selectTerms = [selectTerm] + selectTerms } @@ -331,7 +677,7 @@ public struct Select: Hashable { - parameter selectTerms: a series of `SelectTerm`s */ - public init(_ selectTerms: [SelectTerm]) { + public init(_ selectTerms: [SelectTerm]) { self.selectTerms = selectTerms } @@ -339,12 +685,20 @@ public struct Select: Hashable { // MARK: Equatable - public static func == (lhs: Select, rhs: Select) -> Bool { + public static func == (lhs: Select, rhs: Select) -> Bool { return lhs.selectTerms == rhs.selectTerms } + // MARK: SelectClause + + public typealias ObjectType = D + public typealias ReturnType = T + + public let selectTerms: [SelectTerm] + + // MARK: Hashable public var hashValue: Int { @@ -355,36 +709,7 @@ public struct Select: Hashable { // MARK: Internal - internal let selectTerms: [SelectTerm] -} - -public extension Select where T: NSManagedObjectID { - - public init() { - - self.init(.objectID()) - } -} - - -// MARK: - NSDictionary: SelectAttributesResultType - -extension NSDictionary: SelectAttributesResultType { - - // MARK: SelectAttributesResultType - - public static func cs_fromQueryResultsNativeType(_ result: [Any]) -> [[String : Any]] { - - return result as! [[String: Any]] - } -} - - -// MARK: - Internal - -internal extension Collection where Iterator.Element == SelectTerm { - - internal func applyToFetchRequest(_ fetchRequest: NSFetchRequest, owner: T) { + internal func applyToFetchRequest(_ fetchRequest: NSFetchRequest) { fetchRequest.includesPendingChanges = false fetchRequest.resultType = .dictionaryResultType @@ -414,7 +739,7 @@ internal extension Collection where Iterator.Element == SelectTerm { } var propertiesToFetch = [Any]() - for term in self { + for term in self.selectTerms { switch term { @@ -445,7 +770,7 @@ internal extension Collection where Iterator.Element == SelectTerm { CoreStore.log( .warning, - message: "The key path \"\(keyPath)\" could not be resolved in entity \(cs_typeName(entityDescription.managedObjectClassName)) as an attribute and will be ignored by \(cs_typeName(owner)) query clause." + message: "The key path \"\(keyPath)\" could not be resolved in entity \(cs_typeName(entityDescription.managedObjectClassName)) as an attribute and will be ignored by \(cs_typeName(self)) query clause." ) } @@ -468,19 +793,106 @@ internal extension Collection where Iterator.Element == SelectTerm { fetchRequest.propertiesToFetch = propertiesToFetch } +} + +public extension Select where T: NSManagedObjectID { - internal func keyPathForFirstSelectTerm() -> KeyPathString { + /** + Initializes a `Select` that queries for `NSManagedObjectID` results + */ + public init() { - switch self.first! { - - case ._attribute(let keyPath): - return keyPath - - case ._aggregate(_, _, let alias, _): - return alias - - case ._identity(let alias, _): - return alias - } + self.init(.objectID()) + } +} + +public extension Select where D: NSManagedObject { + + /** + Initializes a `Select` that queries the value of an attribute pertained by a keyPath + - parameter keyPath: the keyPath for the attribute + */ + public init(_ keyPath: KeyPath) { + + self.init(.attribute(keyPath)) + } +} + +public extension Select where D: CoreStoreObject, T: ImportableAttributeType { + + /** + Initializes a `Select` that queries the value of an attribute pertained by a keyPath + - parameter keyPath: the keyPath for the attribute + */ + public init(_ keyPath: KeyPath.Required>) { + + self.init(.attribute(keyPath)) + } + + /** + Initializes a `Select` that queries the value of an attribute pertained by a keyPath + - parameter keyPath: the keyPath for the attribute + */ + public init(_ keyPath: KeyPath.Optional>) { + + self.init(.attribute(keyPath)) + } +} + +public extension Select where D: CoreStoreObject, T: ImportableAttributeType & NSCoding & NSCopying { + + /** + Initializes a `Select` that queries the value of an attribute pertained by a keyPath + - parameter keyPath: the keyPath for the attribute + */ + public init(_ keyPath: KeyPath.Required>) { + + self.init(.attribute(keyPath)) + } + + /** + Initializes a `Select` that queries the value of an attribute pertained by a keyPath + - parameter keyPath: the keyPath for the attribute + */ + public init(_ keyPath: KeyPath.Optional>) { + + self.init(.attribute(keyPath)) + } +} + + +// MARK: - SelectClause + +/** + Abstracts the `Select` clause for protocol utilities. + */ +public protocol SelectClause { + + /** + The `DynamicObject` type associated with the clause + */ + associatedtype ObjectType: DynamicObject + + /** + The `SelectResultType` type associated with the clause + */ + associatedtype ReturnType: SelectResultType + + /** + The `SelectTerm`s for the query + */ + var selectTerms: [SelectTerm] { get } +} + + +// MARK: - NSDictionary: SelectAttributesResultType + +extension NSDictionary: SelectAttributesResultType { + + // MARK: SelectAttributesResultType + + public static func cs_fromQueryResultsNativeType(_ result: [Any]) -> [[String : Any]] { + + return result as! [[String: Any]] } } From 03bb7619dadb30c647ce83692d4c68d959b911a9 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 22 Sep 2017 00:08:01 +0900 Subject: [PATCH 25/56] added queryBuilder utilities for clause sequences --- Sources/ChainedClauseBuilder.swift | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index 7bcaae8..722ceb5 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -161,6 +161,11 @@ public extension From { return self.fetchChain(appending: clause) } + public func appending(_ clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + + return self.fetchChain(appending: clauses) + } + // MARK: Private @@ -168,6 +173,11 @@ public extension From { return .init(from: self, fetchClauses: [clause]) } + + private func fetchChain(appending clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + + return .init(from: self, fetchClauses: Array(clauses)) + } } public extension From where D: NSManagedObject { @@ -293,6 +303,11 @@ public extension FetchChainBuilder { return self.fetchChain(appending: clause) } + public func appending(_ clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + + return self.fetchChain(appending: clauses) + } + // MARK: Private @@ -303,6 +318,14 @@ public extension FetchChainBuilder { fetchClauses: self.fetchClauses + [clause] ) } + + private func fetchChain(appending clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + + return .init( + from: self.from, + fetchClauses: self.fetchClauses + Array(clauses) + ) + } } public extension QueryChainBuilder { @@ -352,6 +375,11 @@ public extension QueryChainBuilder { return self.queryChain(appending: clause) } + public func appending(_ clauses: S) -> QueryChainBuilder where S.Element: QueryClause { + + return self.queryChain(appending: clauses) + } + // MARK: Private @@ -363,6 +391,15 @@ public extension QueryChainBuilder { queryClauses: self.queryClauses + [clause] ) } + + private func queryChain(appending clauses: S) -> QueryChainBuilder where S.Element: QueryClause { + + return .init( + from: self.from, + select: self.select, + queryClauses: self.queryClauses + Array(clauses) + ) + } } public extension QueryChainBuilder where D: NSManagedObject { @@ -429,6 +466,11 @@ public extension SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause) } + public func appending(_ clauses: S) -> SectionMonitorChainBuilder where S.Element: FetchClause { + + return self.sectionMonitorChain(appending: clauses) + } + // MARK: Private @@ -440,4 +482,13 @@ public extension SectionMonitorChainBuilder { fetchClauses: self.fetchClauses + [clause] ) } + + private func sectionMonitorChain(appending clauses: S) -> SectionMonitorChainBuilder where S.Element: FetchClause { + + return .init( + from: self.from, + sectionBy: self.sectionBy, + fetchClauses: self.fetchClauses + Array(clauses) + ) + } } From 16225fc4c670deacac5307331ae96bd7fa4a5af9 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 22 Sep 2017 00:20:55 +0900 Subject: [PATCH 26/56] allow optionals in relationship keyPaths --- Sources/KeyPath+Querying.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/KeyPath+Querying.swift b/Sources/KeyPath+Querying.swift index e06d9e4..9893789 100644 --- a/Sources/KeyPath+Querying.swift +++ b/Sources/KeyPath+Querying.swift @@ -320,11 +320,21 @@ public func == (_ keyPath: KeyPath.ToOne>, return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +public func == (_ keyPath: KeyPath.ToOne>, _ object: D?) -> Where { + + return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) +} + public func != (_ keyPath: KeyPath.ToOne>, _ object: D) -> Where { return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +public func != (_ keyPath: KeyPath.ToOne>, _ object: D?) -> Where { + + return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) +} + public func ~= (_ sequence: S, _ keyPath: KeyPath.ToOne>) -> Where where S.Iterator.Element == D { return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) From 474f52ed2b3217954067d351561d6360d637a884 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 22 Sep 2017 00:30:02 +0900 Subject: [PATCH 27/56] alow nil comparison for relationship keypaths --- Sources/KeyPath+Querying.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/KeyPath+Querying.swift b/Sources/KeyPath+Querying.swift index 9893789..25b549c 100644 --- a/Sources/KeyPath+Querying.swift +++ b/Sources/KeyPath+Querying.swift @@ -325,6 +325,11 @@ public func == (_ keyPath: KeyPath.ToOne>, return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +public func == (_ keyPath: KeyPath.ToOne>, _ null: Void?) -> Where { + + return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: nil) +} + public func != (_ keyPath: KeyPath.ToOne>, _ object: D) -> Where { return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) @@ -335,6 +340,11 @@ public func != (_ keyPath: KeyPath.ToOne>, return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +public func != (_ keyPath: KeyPath.ToOne>, _ null: Void?) -> Where { + + return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: nil) +} + public func ~= (_ sequence: S, _ keyPath: KeyPath.ToOne>) -> Where where S.Iterator.Element == D { return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) From cc84b1f8bd4db0f1dbd399831e9378abacad75df Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 22 Sep 2017 01:00:03 +0900 Subject: [PATCH 28/56] minor --- Sources/ChainedClauseBuilder.swift | 8 ++++---- Sources/KeyPath+Querying.swift | 10 ---------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index 722ceb5..a6bea62 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -161,7 +161,7 @@ public extension From { return self.fetchChain(appending: clause) } - public func appending(_ clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + public func appending(sequence clauses: S) -> FetchChainBuilder where S.Element: FetchClause { return self.fetchChain(appending: clauses) } @@ -303,7 +303,7 @@ public extension FetchChainBuilder { return self.fetchChain(appending: clause) } - public func appending(_ clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + public func appending(sequence clauses: S) -> FetchChainBuilder where S.Element: FetchClause { return self.fetchChain(appending: clauses) } @@ -375,7 +375,7 @@ public extension QueryChainBuilder { return self.queryChain(appending: clause) } - public func appending(_ clauses: S) -> QueryChainBuilder where S.Element: QueryClause { + public func appending(sequence clauses: S) -> QueryChainBuilder where S.Element: QueryClause { return self.queryChain(appending: clauses) } @@ -466,7 +466,7 @@ public extension SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause) } - public func appending(_ clauses: S) -> SectionMonitorChainBuilder where S.Element: FetchClause { + public func appending(sequence clauses: S) -> SectionMonitorChainBuilder where S.Element: FetchClause { return self.sectionMonitorChain(appending: clauses) } diff --git a/Sources/KeyPath+Querying.swift b/Sources/KeyPath+Querying.swift index 25b549c..9893789 100644 --- a/Sources/KeyPath+Querying.swift +++ b/Sources/KeyPath+Querying.swift @@ -325,11 +325,6 @@ public func == (_ keyPath: KeyPath.ToOne>, return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } -public func == (_ keyPath: KeyPath.ToOne>, _ null: Void?) -> Where { - - return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: nil) -} - public func != (_ keyPath: KeyPath.ToOne>, _ object: D) -> Where { return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) @@ -340,11 +335,6 @@ public func != (_ keyPath: KeyPath.ToOne>, return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } -public func != (_ keyPath: KeyPath.ToOne>, _ null: Void?) -> Where { - - return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: nil) -} - public func ~= (_ sequence: S, _ keyPath: KeyPath.ToOne>) -> Where where S.Iterator.Element == D { return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) From 7beb3bec75e0d168a4e96979d6adbfbad0ccc36e Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 22 Sep 2017 08:02:13 +0900 Subject: [PATCH 29/56] rename methods --- Sources/ChainedClauseBuilder.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index a6bea62..30119d1 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -161,7 +161,7 @@ public extension From { return self.fetchChain(appending: clause) } - public func appending(sequence clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return self.fetchChain(appending: clauses) } @@ -174,7 +174,7 @@ public extension From { return .init(from: self, fetchClauses: [clause]) } - private func fetchChain(appending clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + private func fetchChain(appending clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return .init(from: self, fetchClauses: Array(clauses)) } @@ -303,7 +303,7 @@ public extension FetchChainBuilder { return self.fetchChain(appending: clause) } - public func appending(sequence clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return self.fetchChain(appending: clauses) } @@ -319,7 +319,7 @@ public extension FetchChainBuilder { ) } - private func fetchChain(appending clauses: S) -> FetchChainBuilder where S.Element: FetchClause { + private func fetchChain(appending clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return .init( from: self.from, @@ -375,7 +375,7 @@ public extension QueryChainBuilder { return self.queryChain(appending: clause) } - public func appending(sequence clauses: S) -> QueryChainBuilder where S.Element: QueryClause { + public func appending(contentsOf clauses: S) -> QueryChainBuilder where S.Element == QueryClause { return self.queryChain(appending: clauses) } @@ -392,7 +392,7 @@ public extension QueryChainBuilder { ) } - private func queryChain(appending clauses: S) -> QueryChainBuilder where S.Element: QueryClause { + private func queryChain(appending clauses: S) -> QueryChainBuilder where S.Element == QueryClause { return .init( from: self.from, @@ -466,7 +466,7 @@ public extension SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause) } - public func appending(sequence clauses: S) -> SectionMonitorChainBuilder where S.Element: FetchClause { + public func appending(contentsOf clauses: S) -> SectionMonitorChainBuilder where S.Element == FetchClause { return self.sectionMonitorChain(appending: clauses) } @@ -483,7 +483,7 @@ public extension SectionMonitorChainBuilder { ) } - private func sectionMonitorChain(appending clauses: S) -> SectionMonitorChainBuilder where S.Element: FetchClause { + private func sectionMonitorChain(appending clauses: S) -> SectionMonitorChainBuilder where S.Element == FetchClause { return .init( from: self.from, From 645034dde5b2dacd71b8adb2ad11cda4e42576c0 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 24 Sep 2017 10:38:17 +0900 Subject: [PATCH 30/56] keyPath utilities for SectionBy clauses --- ...etchingAndQueryingDemoViewController.swift | 40 +++--- .../MigrationsDemoViewController.swift | 4 +- CoreStoreTests/SectionByTests.swift | 4 +- Sources/CSSectionBy.swift | 22 +++- Sources/ChainedClauseBuilder.swift | 23 +++- Sources/CoreStore+Observing.swift | 8 +- .../CoreStoreFetchedResultsController.swift | 4 +- Sources/DataStack+Observing.swift | 8 +- Sources/ListMonitor.swift | 14 +- ...FetchedResultsController+Convenience.swift | 10 +- Sources/SectionBy.swift | 120 +++++++++++++++++- Sources/UnsafeDataTransaction+Observing.swift | 8 +- 12 files changed, 207 insertions(+), 58 deletions(-) diff --git a/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift index cfee272..d39a850 100644 --- a/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift @@ -237,7 +237,7 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo return Static.timeZonesStack.queryValue( From() - .select(NSNumber.self, .count(#keyPath(TimeZone.name))) + .select(NSNumber.self, .count(\.name)) )! as Any } ), @@ -247,7 +247,7 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo return Static.timeZonesStack.queryValue( From() - .select(String.self, .attribute(#keyPath(TimeZone.abbreviation))) + .select(String.self, .attribute(\.abbreviation)) .where(format: "%K ENDSWITH[c] %@", #keyPath(TimeZone.name), "Tokyo") )! as Any } @@ -260,10 +260,10 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo From() .select( NSDictionary.self, - .attribute(#keyPath(TimeZone.name)), - .attribute(#keyPath(TimeZone.abbreviation)) + .attribute(\.name), + .attribute(\.abbreviation) ) - .orderBy(.ascending(#keyPath(TimeZone.name))) + .orderBy(.ascending(\.name)) )! } ), @@ -275,14 +275,14 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo From() .select( NSDictionary.self, - .count(#keyPath(TimeZone.abbreviation)), - .attribute(#keyPath(TimeZone.abbreviation)) + .count(\.abbreviation), + .attribute(\.abbreviation) ) - .groupBy(#keyPath(TimeZone.abbreviation)) + .groupBy(\.abbreviation) .orderBy( - .ascending(#keyPath(TimeZone.secondsFromGMT)), - .ascending(#keyPath(TimeZone.name)) - ) + .ascending(\.secondsFromGMT), + .ascending(\.name) + ) )! } ), @@ -291,13 +291,17 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo query: { () -> Any in return Static.timeZonesStack.queryAttributes( - From(), - Select( - .count(#keyPath(TimeZone.hasDaylightSavingTime), as: "numberOfCountries"), - #keyPath(TimeZone.hasDaylightSavingTime) - ), - GroupBy(#keyPath(TimeZone.hasDaylightSavingTime)), - OrderBy(.descending(#keyPath(TimeZone.hasDaylightSavingTime))) + From() + .select( + NSDictionary.self, + .count(\.hasDaylightSavingTime, as: "numberOfCountries"), + .attribute(\.hasDaylightSavingTime) + ) + .groupBy(\.hasDaylightSavingTime) + .orderBy( + .descending(\.hasDaylightSavingTime), + .ascending(\.name) + ) )! } ) diff --git a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift index 9227773..56fa48f 100644 --- a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift @@ -287,8 +287,8 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD self.set(dataStack: dataStack, model: model, scrollToSelection: true) let count = dataStack.queryValue( - From(model.entityType), - Select(.count(#keyPath(OrganismV1.dna))))! + From(model.entityType) + .select(Int.self, .count(#keyPath(OrganismV1.dna))))! if count > 0 { self.setEnabled(true) diff --git a/CoreStoreTests/SectionByTests.swift b/CoreStoreTests/SectionByTests.swift index 29132e6..686d109 100644 --- a/CoreStoreTests/SectionByTests.swift +++ b/CoreStoreTests/SectionByTests.swift @@ -39,13 +39,13 @@ final class SectionByTests: XCTestCase { do { - let sectionBy = SectionBy("key") + let sectionBy = SectionBy("key") XCTAssertEqual(sectionBy.sectionKeyPath, "key") XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key") } do { - let sectionBy = SectionBy("key") { $0.flatMap { "\($0):suffix" } } + let sectionBy = SectionBy("key") { $0.flatMap { "\($0):suffix" } } XCTAssertEqual(sectionBy.sectionKeyPath, "key") XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key:suffix") XCTAssertNil(sectionBy.sectionIndexTransformer(nil)) diff --git a/Sources/CSSectionBy.swift b/Sources/CSSectionBy.swift index bccdffc..f8c2fc7 100644 --- a/Sources/CSSectionBy.swift +++ b/Sources/CSSectionBy.swift @@ -36,7 +36,7 @@ import CoreData */ @available(OSX 10.12, *) @objc -public final class CSSectionBy: NSObject, CoreStoreObjectiveCType { +public final class CSSectionBy: NSObject { /** Initializes a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections @@ -47,7 +47,7 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType { @objc public static func keyPath(_ sectionKeyPath: KeyPathString) -> CSSectionBy { - return self.init(SectionBy(sectionKeyPath)) + return self.init(SectionBy(sectionKeyPath)) } /** @@ -60,7 +60,7 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType { @objc public static func keyPath(_ sectionKeyPath: KeyPathString, sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> CSSectionBy { - return self.init(SectionBy(sectionKeyPath, sectionIndexTransformer)) + return self.init(SectionBy(sectionKeyPath, sectionIndexTransformer)) } @@ -74,11 +74,11 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType { // MARK: CoreStoreObjectiveCType - public let bridgeToSwift: SectionBy + public let bridgeToSwift: SectionBy - public init(_ swiftValue: SectionBy) { + public init(_ swiftValue: SectionBy) { - self.bridgeToSwift = swiftValue + self.bridgeToSwift = swiftValue.downcast() super.init() } } @@ -87,7 +87,7 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType { // MARK: - SectionBy @available(OSX 10.12, *) -extension SectionBy: CoreStoreSwiftType { +extension SectionBy { // MARK: CoreStoreSwiftType @@ -95,4 +95,12 @@ extension SectionBy: CoreStoreSwiftType { return CSSectionBy(self) } + + + // MARK: FilePrivate + + fileprivate func downcast() -> SectionBy { + + return SectionBy(self.sectionKeyPath, self.sectionIndexTransformer) + } } diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index 30119d1..1433d94 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -51,7 +51,7 @@ public protocol SectionMonitorBuilderType { associatedtype ObjectType: DynamicObject var from: From { get set } - var sectionBy: SectionBy { get set } + var sectionBy: SectionBy { get set } var fetchClauses: [FetchClause] { get set } } @@ -92,7 +92,7 @@ public struct SectionMonitorChainBuilder: SectionMonitorBuilde // MARK: SectionMonitorBuilderType public var from: From - public var sectionBy: SectionBy + public var sectionBy: SectionBy public var fetchClauses: [FetchClause] = [] } @@ -101,6 +101,15 @@ public struct SectionMonitorChainBuilder: SectionMonitorBuilde public extension From { + public func select(_ clause: Select) -> QueryChainBuilder { + + return .init( + from: self, + select: clause, + queryClauses: [] + ) + } + public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { return self.select(resultType, [selectTerm] + selectTerms) @@ -115,6 +124,16 @@ public extension From { ) } + @available(OSX 10.12, *) + public func sectionBy(_ clause: SectionBy) -> SectionMonitorChainBuilder { + + return .init( + from: self, + sectionBy: clause, + fetchClauses: [] + ) + } + @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString) -> SectionMonitorChainBuilder { diff --git a/Sources/CoreStore+Observing.swift b/Sources/CoreStore+Observing.swift index 546f740..01f5993 100644 --- a/Sources/CoreStore+Observing.swift +++ b/Sources/CoreStore+Observing.swift @@ -115,7 +115,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public static func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { + public static func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) } @@ -128,7 +128,7 @@ public extension CoreStore { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public static func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { + public static func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) } @@ -151,7 +151,7 @@ public extension CoreStore { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { + public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } @@ -164,7 +164,7 @@ public extension CoreStore { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { + public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } diff --git a/Sources/CoreStoreFetchedResultsController.swift b/Sources/CoreStoreFetchedResultsController.swift index b0c18cb..2230a22 100644 --- a/Sources/CoreStoreFetchedResultsController.swift +++ b/Sources/CoreStoreFetchedResultsController.swift @@ -35,7 +35,7 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll // MARK: Internal @nonobjc - internal convenience init(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { + internal convenience init(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { self.init( context: dataStack.mainContext, @@ -47,7 +47,7 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll } @nonobjc - internal init(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { + internal init(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { _ = from.applyToFetchRequest( fetchRequest, diff --git a/Sources/DataStack+Observing.swift b/Sources/DataStack+Observing.swift index 1c08c18..611130d 100644 --- a/Sources/DataStack+Observing.swift +++ b/Sources/DataStack+Observing.swift @@ -154,7 +154,7 @@ public extension DataStack { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { + public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { return self.monitorSectionedList(from, sectionBy, fetchClauses) } @@ -167,7 +167,7 @@ public extension DataStack { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { + public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { CoreStore.assert( Thread.isMainThread, @@ -208,7 +208,7 @@ public extension DataStack { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } @@ -221,7 +221,7 @@ public extension DataStack { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { CoreStore.assert( Thread.isMainThread, diff --git a/Sources/ListMonitor.swift b/Sources/ListMonitor.swift index 1492405..acf2019 100644 --- a/Sources/ListMonitor.swift +++ b/Sources/ListMonitor.swift @@ -626,7 +626,7 @@ public final class ListMonitor: Hashable { // MARK: Internal - internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { + internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { self.init( context: dataStack.mainContext, @@ -638,7 +638,7 @@ public final class ListMonitor: Hashable { ) } - internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void, createAsynchronously: @escaping (ListMonitor) -> Void) { + internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void, createAsynchronously: @escaping (ListMonitor) -> Void) { self.init( context: dataStack.mainContext, @@ -650,7 +650,7 @@ public final class ListMonitor: Hashable { ) } - internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { + internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) { self.init( context: unsafeTransaction.context, @@ -662,7 +662,7 @@ public final class ListMonitor: Hashable { ) } - internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void, createAsynchronously: @escaping (ListMonitor) -> Void) { + internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void, createAsynchronously: @escaping (ListMonitor) -> Void) { self.init( context: unsafeTransaction.context, @@ -1041,7 +1041,7 @@ public final class ListMonitor: Hashable { } } - private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) -> (controller: CoreStoreFetchedResultsController, delegate: FetchedResultsControllerDelegate) { + private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void) -> (controller: CoreStoreFetchedResultsController, delegate: FetchedResultsControllerDelegate) { let fetchRequest = CoreStoreFetchRequest() fetchRequest.fetchLimit = 0 @@ -1065,9 +1065,9 @@ public final class ListMonitor: Hashable { } private let from: From - private let sectionBy: SectionBy? + private let sectionBy: SectionBy? - private init(context: NSManagedObjectContext, transactionQueue: DispatchQueue, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void, createAsynchronously: ((ListMonitor) -> Void)?) { + private init(context: NSManagedObjectContext, transactionQueue: DispatchQueue, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest) -> Void, createAsynchronously: ((ListMonitor) -> Void)?) { self.isSectioned = (sectionBy != nil) self.from = from diff --git a/Sources/NSFetchedResultsController+Convenience.swift b/Sources/NSFetchedResultsController+Convenience.swift index 3d1c552..1bcb1fa 100644 --- a/Sources/NSFetchedResultsController+Convenience.swift +++ b/Sources/NSFetchedResultsController+Convenience.swift @@ -42,7 +42,7 @@ public extension DataStack { - returns: an `NSFetchedResultsController` that observes the `DataStack` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { return createFRC( fromContext: self.mainContext, @@ -62,7 +62,7 @@ public extension DataStack { - returns: an `NSFetchedResultsController` that observes the `DataStack` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { return createFRC( fromContext: self.mainContext, @@ -127,7 +127,7 @@ public extension UnsafeDataTransaction { - returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController { return createFRC( fromContext: self.context, @@ -147,7 +147,7 @@ public extension UnsafeDataTransaction { - returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction` */ @nonobjc - public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { + public func createFetchedResultsController(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController { return createFRC( fromContext: self.context, @@ -201,7 +201,7 @@ public extension UnsafeDataTransaction { // MARK: - Private @available(OSX 10.12, *) -fileprivate func createFRC(fromContext context: NSManagedObjectContext, from: From, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController { +fileprivate func createFRC(fromContext context: NSManagedObjectContext, from: From, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController { let controller = CoreStoreFetchedResultsController( context: context, diff --git a/Sources/SectionBy.swift b/Sources/SectionBy.swift index f9ac2b7..629fd2f 100644 --- a/Sources/SectionBy.swift +++ b/Sources/SectionBy.swift @@ -40,7 +40,7 @@ import CoreData ``` */ @available(OSX 10.12, *) -public struct SectionBy { +public struct SectionBy { /** Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections @@ -71,3 +71,121 @@ public struct SectionBy { internal let sectionKeyPath: KeyPathString internal let sectionIndexTransformer: (_ sectionName: String?) -> String? } + +@available(OSX 10.12, *) +public extension SectionBy where D: NSManagedObject { + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the key path to use to group the objects into sections + */ + public init(_ sectionKeyPath: KeyPath) { + + self.init(sectionKeyPath, { $0 }) + } + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the key path to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + */ + public init(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) { + + self.init(sectionKeyPath._kvcKeyPathString!, sectionIndexTransformer) + } +} + +@available(OSX 10.12, *) +public extension SectionBy where D: CoreStoreObject { + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the key path to use to group the objects into sections + */ + public init(_ sectionKeyPath: KeyPath.Required>) { + + self.init(sectionKeyPath, { $0 }) + } + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the key path to use to group the objects into sections + */ + public init(_ sectionKeyPath: KeyPath.Optional>) { + + self.init(sectionKeyPath, { $0 }) + } + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the key path to use to group the objects into sections + */ + public init(_ sectionKeyPath: KeyPath.Required>) { + + self.init(sectionKeyPath, { $0 }) + } + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the key path to use to group the objects into sections + */ + public init(_ sectionKeyPath: KeyPath.Optional>) { + + self.init(sectionKeyPath, { $0 }) + } + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the key path to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + */ + public init(_ sectionKeyPath: KeyPath.Required>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) { + + self.init(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) + } + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the key path to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + */ + public init(_ sectionKeyPath: KeyPath.Optional>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) { + + self.init(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) + } + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the key path to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + */ + public init(_ sectionKeyPath: KeyPath.Required>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) { + + self.init(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) + } + + /** + Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the key path to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + */ + public init(_ sectionKeyPath: KeyPath.Optional>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) { + + self.init(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) + } +} diff --git a/Sources/UnsafeDataTransaction+Observing.swift b/Sources/UnsafeDataTransaction+Observing.swift index 9a9b861..7ad6a67 100644 --- a/Sources/UnsafeDataTransaction+Observing.swift +++ b/Sources/UnsafeDataTransaction+Observing.swift @@ -145,7 +145,7 @@ public extension UnsafeDataTransaction { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { + public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor { return self.monitorSectionedList(from, sectionBy, fetchClauses) } @@ -158,7 +158,7 @@ public extension UnsafeDataTransaction { - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - returns: a `ListMonitor` instance that monitors changes to the list */ - public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { + public func monitorSectionedList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor { CoreStore.assert( fetchClauses.filter { $0 is OrderBy }.count > 0, @@ -194,7 +194,7 @@ public extension UnsafeDataTransaction { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } @@ -207,7 +207,7 @@ public extension UnsafeDataTransaction { - parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections. - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. */ - public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { + public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { CoreStore.assert( fetchClauses.filter { $0 is OrderBy }.count > 0, From 4ead3c34dd05bc0a61395972ad3e5a3c594dd7a7 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Tue, 26 Sep 2017 11:59:28 +0900 Subject: [PATCH 31/56] delete errant operator --- Sources/CoreStoreObject+Querying.swift | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Sources/CoreStoreObject+Querying.swift b/Sources/CoreStoreObject+Querying.swift index c0f9566..1d5800f 100644 --- a/Sources/CoreStoreObject+Querying.swift +++ b/Sources/CoreStoreObject+Querying.swift @@ -379,18 +379,6 @@ public extension RelationshipContainer.ToOne { return !Where(relationship.keyPath, isEqualTo: object) } - /** - Creates a `Where` clause from a `CoreStoreObject.Relationship` property. - ``` - let dog = CoreStore.fetchOne(From(), Dog.where { $0.master ~= me }) - ``` - */ - @inline(__always) - public static func ~= (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { - - return Where(relationship.keyPath, isEqualTo: object) - } - /** Creates a `Where` clause from a `CoreStoreObject.Relationship` property. ``` From 096e5493a6f2ea025d0da61c60e833282be92b34 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Fri, 29 Sep 2017 20:33:10 +0900 Subject: [PATCH 32/56] WIP: protocol cleanup --- CoreStore.xcodeproj/project.pbxproj | 30 ++- CoreStoreTests/DynamicModelTests.swift | 31 ++- CoreStoreTests/WhereTests.swift | 8 +- Sources/CoreStoreStrings.swift | 8 + Sources/GroupBy.swift | 2 +- Sources/OrderBy.swift | 5 - ...useTypes.swift => TypeErasedClauses.swift} | 23 ++- Sources/Where.swift | 181 ++++++++++-------- Sources/WhereClauseType.swift | 62 ++++++ 9 files changed, 248 insertions(+), 102 deletions(-) rename Sources/{ClauseTypes.swift => TypeErasedClauses.swift} (79%) create mode 100644 Sources/WhereClauseType.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 0946a04..c7c4c1e 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -34,7 +34,7 @@ 82BA18B51C4BBD3F00A0916E /* BaseDataTransaction+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EFE1AFF847B0064E85B /* BaseDataTransaction+Querying.swift */; }; 82BA18B61C4BBD3F00A0916E /* DataStack+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F061AFF847B0064E85B /* DataStack+Querying.swift */; }; 82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F071AFF847B0064E85B /* CoreStore+Querying.swift */; }; - 82BA18B81C4BBD4200A0916E /* ClauseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */; }; + 82BA18B81C4BBD4200A0916E /* TypeErasedClauses.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* TypeErasedClauses.swift */; }; 82BA18B91C4BBD4A00A0916E /* From.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F011AFF847B0064E85B /* From.swift */; }; 82BA18BA1C4BBD4A00A0916E /* Select.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F031AFF847B0064E85B /* Select.swift */; }; 82BA18BB1C4BBD4A00A0916E /* Where.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F051AFF847B0064E85B /* Where.swift */; }; @@ -184,7 +184,7 @@ B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EFE1AFF847B0064E85B /* BaseDataTransaction+Querying.swift */; }; B52DD1A81BE1F93200949AFE /* DataStack+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F061AFF847B0064E85B /* DataStack+Querying.swift */; }; B52DD1A91BE1F93200949AFE /* CoreStore+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F071AFF847B0064E85B /* CoreStore+Querying.swift */; }; - B52DD1AA1BE1F93500949AFE /* ClauseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */; }; + B52DD1AA1BE1F93500949AFE /* TypeErasedClauses.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* TypeErasedClauses.swift */; }; B52DD1AB1BE1F93900949AFE /* From.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F011AFF847B0064E85B /* From.swift */; }; B52DD1AC1BE1F93900949AFE /* Select.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F031AFF847B0064E85B /* Select.swift */; }; B52DD1AD1BE1F93900949AFE /* Where.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F051AFF847B0064E85B /* Where.swift */; }; @@ -366,7 +366,7 @@ B56321921BD65216006C9394 /* BaseDataTransaction+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EFE1AFF847B0064E85B /* BaseDataTransaction+Querying.swift */; }; B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F061AFF847B0064E85B /* DataStack+Querying.swift */; }; B56321941BD65216006C9394 /* CoreStore+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F071AFF847B0064E85B /* CoreStore+Querying.swift */; }; - B56321951BD65216006C9394 /* ClauseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */; }; + B56321951BD65216006C9394 /* TypeErasedClauses.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* TypeErasedClauses.swift */; }; B56321961BD65216006C9394 /* From.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F011AFF847B0064E85B /* From.swift */; }; B56321971BD65216006C9394 /* Select.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F031AFF847B0064E85B /* Select.swift */; }; B56321981BD65216006C9394 /* Where.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F051AFF847B0064E85B /* Where.swift */; }; @@ -516,6 +516,10 @@ B5C976E71C6E3A5A00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */; }; B5C976E81C6E3A5D00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */; }; B5C976E91C6E3A5E00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */; }; + B5CA2B081F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; + B5CA2B091F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; + B5CA2B0A1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; + B5CA2B0B1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; B5D1E22C19FA9FBC003B2874 /* CoreStoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */; }; B5D339B41E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; B5D339B51E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; @@ -638,7 +642,7 @@ B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */; }; B5E84F371AFF85470064E85B /* NSManagedObjectContext+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */; }; B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */; }; - B5E84F411AFF8CCD0064E85B /* ClauseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */; }; + B5E84F411AFF8CCD0064E85B /* TypeErasedClauses.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F401AFF8CCD0064E85B /* TypeErasedClauses.swift */; }; B5ECDBDF1CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5ECDBDE1CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift */; }; B5ECDBE11CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5ECDBDE1CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift */; }; B5ECDBE21CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5ECDBDE1CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift */; }; @@ -857,6 +861,7 @@ B5BDC9271C2024F2008147CD /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = SOURCE_ROOT; }; B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = ""; }; B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreFetchedResultsController.swift; sourceTree = ""; }; + B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhereClauseType.swift; sourceTree = ""; }; B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreError.swift; sourceTree = ""; }; B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicModelTests.swift; sourceTree = ""; }; B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreObject.swift; sourceTree = ""; }; @@ -923,7 +928,7 @@ B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Setup.swift"; sourceTree = ""; }; B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Transaction.swift"; sourceTree = ""; }; B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Querying.swift"; sourceTree = ""; }; - B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClauseTypes.swift; sourceTree = ""; }; + B5E84F401AFF8CCD0064E85B /* TypeErasedClauses.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeErasedClauses.swift; sourceTree = ""; }; B5ECDBDE1CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSBaseDataTransaction+Querying.swift"; sourceTree = ""; }; B5ECDBE41CA6BEA300C7F112 /* CSClauseTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSClauseTypes.swift; sourceTree = ""; }; B5ECDBEB1CA6BF2000C7F112 /* CSFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSFrom.swift; sourceTree = ""; }; @@ -1442,7 +1447,8 @@ B5E84F0A1AFF847B0064E85B /* Protocol Clauses */ = { isa = PBXGroup; children = ( - B5E84F401AFF8CCD0064E85B /* ClauseTypes.swift */, + B5E84F401AFF8CCD0064E85B /* TypeErasedClauses.swift */, + B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */, ); name = "Protocol Clauses"; sourceTree = ""; @@ -1829,6 +1835,7 @@ files = ( B5E84F221AFF84860064E85B /* ObjectMonitor.swift in Sources */, B5ECDBF91CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */, + B5CA2B081F7E5ACA004B1936 /* WhereClauseType.swift in Sources */, B5C976E71C6E3A5A00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */, B56923F51EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */, B5F1DA901B9AA991007C5CBB /* ImportableUniqueObject.swift in Sources */, @@ -1905,7 +1912,7 @@ 2F291E2719C6D3CF007AF63F /* CoreStore.swift in Sources */, B5ECDC111CA816E500C7F112 /* CSTweak.swift in Sources */, B56923C41EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, - B5E84F411AFF8CCD0064E85B /* ClauseTypes.swift in Sources */, + B5E84F411AFF8CCD0064E85B /* TypeErasedClauses.swift in Sources */, B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */, B52F74451E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */, B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */, @@ -2020,6 +2027,7 @@ files = ( 82BA18B61C4BBD3F00A0916E /* DataStack+Querying.swift in Sources */, B5ECDBFB1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */, + B5CA2B091F7E5ACA004B1936 /* WhereClauseType.swift in Sources */, B5C976E81C6E3A5D00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */, B56923F61EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */, 82BA18A21C4BBD1D00A0916E /* CoreStoreError.swift in Sources */, @@ -2151,7 +2159,7 @@ 82BA18A81C4BBD2900A0916E /* CoreStoreLogger.swift in Sources */, B549F65F1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B559CD451CAA8B6300E4D58B /* CSSetupResult.swift in Sources */, - 82BA18B81C4BBD4200A0916E /* ClauseTypes.swift in Sources */, + 82BA18B81C4BBD4200A0916E /* TypeErasedClauses.swift in Sources */, B5A991ED1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5ECDBEE1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, @@ -2211,6 +2219,7 @@ files = ( B5220E1E1D13080D009BC71E /* CSListMonitor.swift in Sources */, B5DBE2D01C9914A900B5CEFA /* CSCoreStore.swift in Sources */, + B5CA2B0B1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */, B5677D411CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */, B56923F81EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */, B52DD1BE1BE1F94300949AFE /* Progress+Convenience.swift in Sources */, @@ -2357,7 +2366,7 @@ B5AEFAB81C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, B598514B1C90289F00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, B5D339EA1E9493A500C880DE /* Entity.swift in Sources */, - B52DD1AA1BE1F93500949AFE /* ClauseTypes.swift in Sources */, + B52DD1AA1BE1F93500949AFE /* TypeErasedClauses.swift in Sources */, B53FBA021CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */, B51FE5AF1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */, ); @@ -2402,6 +2411,7 @@ files = ( B56321A91BD65219006C9394 /* Progress+Convenience.swift in Sources */, B5ECDBFC1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */, + B5CA2B0A1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */, B5C976E91C6E3A5E00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */, B56923F71EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */, B56321801BD65216006C9394 /* CoreStoreError.swift in Sources */, @@ -2550,7 +2560,7 @@ B5D339E91E9493A500C880DE /* Entity.swift in Sources */, B56321A41BD65216006C9394 /* CoreStore+Migration.swift in Sources */, B56321A01BD65216006C9394 /* ObjectObserver.swift in Sources */, - B56321951BD65216006C9394 /* ClauseTypes.swift in Sources */, + B56321951BD65216006C9394 /* TypeErasedClauses.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index 92b24fc..30e97cd 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -218,7 +218,7 @@ class DynamicModelTests: BaseTestDataTestCase { let p2 = Dog.where({ $0.nickname == "Spot" }) XCTAssertEqual(p2.predicate, NSPredicate(format: "%K == %@", "nickname", "Spot")) - let dog = transaction.fetchOne(From(), p2) + let dog = transaction.fetchOne(From().where(\.nickname == "Spot")) XCTAssertNotNil(dog) XCTAssertEqual(dog!.nickname.value, "Spot") XCTAssertEqual(dog!.species.value, "Dog") @@ -229,6 +229,35 @@ class DynamicModelTests: BaseTestDataTestCase { let p3 = Dog.where({ $0.age == 10 }) XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10)) + + _ = transaction.fetchAll( + From() + .where(\Animal.species == "Dog" && \.age == 10) + ) + _ = transaction.fetchAll( + From() + .where(\.age == 10 && \Animal.species == "Dog") + ) + _ = transaction.fetchAll( + From(), + Dog.where({ $0.age > 10 && $0.age <= 15 }) + ) + _ = transaction.fetchAll( + From(), + Dog.where({ $0.species == "Dog" && $0.age == 10 }) + ) + _ = transaction.fetchAll( + From(), + Dog.where({ $0.age == 10 && $0.species == "Dog" }) + ) + _ = transaction.fetchAll( + From(), + Dog.where({ $0.age > 10 && $0.age <= 15 }) + ) + _ = transaction.fetchAll( + From(), + (\Dog.age > 10 && \Dog.age <= 15) + ) }, success: { _ in diff --git a/CoreStoreTests/WhereTests.swift b/CoreStoreTests/WhereTests.swift index af9d9f0..11175f9 100644 --- a/CoreStoreTests/WhereTests.swift +++ b/CoreStoreTests/WhereTests.swift @@ -263,8 +263,8 @@ final class WhereTests: XCTestCase { let someWhere: Where? = Where("key4", isEqualTo: "value4") - let finalNoneWhere = andWhere && noneWhere - let finalSomeWhere = andWhere && someWhere + let finalNoneWhere = andWhere &&? noneWhere + let finalSomeWhere = andWhere &&? someWhere let unwrappedFinalSomeWhere = andWhere && someWhere! @@ -294,8 +294,8 @@ final class WhereTests: XCTestCase { let someWhere: Where? = Where("key4", isEqualTo: "value4") - let finalNoneWhere = orWhere && noneWhere - let finalSomeWhere = orWhere && someWhere + let finalNoneWhere = orWhere &&? noneWhere + let finalSomeWhere = orWhere &&? someWhere let unwrappedFinalSomeWhere = orWhere && someWhere! XCTAssertEqual(orWhere.predicate, finalNoneWhere.predicate) diff --git a/Sources/CoreStoreStrings.swift b/Sources/CoreStoreStrings.swift index 5fc467b..e4a8464 100644 --- a/Sources/CoreStoreStrings.swift +++ b/Sources/CoreStoreStrings.swift @@ -64,3 +64,11 @@ public typealias EntityName = String An `String` that pertains to a dynamically-accessable class name (usable with NSClassFromString(...)). */ public typealias ClassName = String + + +// MARK: - KeyPathString + +/** + An `String` that pertains to a attribute keyPaths. + */ +public typealias KeyPathString = String diff --git a/Sources/GroupBy.swift b/Sources/GroupBy.swift index 467fc62..88fdb5d 100644 --- a/Sources/GroupBy.swift +++ b/Sources/GroupBy.swift @@ -64,7 +64,7 @@ public struct GroupBy: GroupByClause, QueryClause, Hashable { } - // MARK: WhereClause + // MARK: GroupByClause public typealias ObjectType = D diff --git a/Sources/OrderBy.swift b/Sources/OrderBy.swift index 33611e7..f5199c4 100644 --- a/Sources/OrderBy.swift +++ b/Sources/OrderBy.swift @@ -27,11 +27,6 @@ import Foundation import CoreData -// MARK: - KeyPathString - -public typealias KeyPathString = String - - // MARK: - OrderBy /** diff --git a/Sources/ClauseTypes.swift b/Sources/TypeErasedClauses.swift similarity index 79% rename from Sources/ClauseTypes.swift rename to Sources/TypeErasedClauses.swift index aa92003..30154f8 100644 --- a/Sources/ClauseTypes.swift +++ b/Sources/TypeErasedClauses.swift @@ -1,5 +1,5 @@ // -// ClauseTypes.swift +// TypeErasedClauses.swift // CoreStore // // Copyright © 2014 John Rommel Estropia @@ -58,3 +58,24 @@ public protocol DeleteClause: FetchClause { func applyToFetchRequest(_ fetchRequest: NSFetchRequest) } + + +// MARK: - AnyWhereClause + +/** + Type-erased `Where` clause for protocol utilities. + */ +public protocol AnyWhereClause: QueryClause, DeleteClause { + + /** + The `NSPredicate` for the fetch or query + */ + var predicate: NSPredicate { get } + + /** + Initializes a `Where` clause with an `NSPredicate` + + - parameter predicate: the `NSPredicate` for the fetch or query + */ + init(_ predicate: NSPredicate) +} diff --git a/Sources/Where.swift b/Sources/Where.swift index e1ed743..96eb888 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -27,66 +27,72 @@ import Foundation import CoreData +infix operator &&? : LogicalConjunctionPrecedence +infix operator ||? : LogicalConjunctionPrecedence + + // MARK: - Where /** The `Where` clause specifies the conditions for a fetch or a query. */ -public struct Where: WhereClause, FetchClause, QueryClause, DeleteClause, Hashable { +public struct Where: WhereClauseType, FetchClause, QueryClause, DeleteClause, Hashable { /** Combines two `Where` predicates together using `AND` operator */ - public static func && (left: Where, right: Where) -> Where { + public static func && (left: Where, right: Where) -> Where { - return Where(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])) - } - - /** - Combines two `Where` predicates together using `AND` operator. - - parameter left: the left hand side `Where` clause - - parameter right: the right hand side `Where` clause - - returns: Return `left` unchanged if `right` is nil - */ - public static func && (left: Where, right: Where?) -> Where { - - if let right = right { - - return left && right - } - return left - } - - /** - Combines two `Where` predicates together using `AND` operator. - - parameter left: the left hand side `Where` clause - - parameter right: the right hand side `Where` clause - - returns: Returns `right` unchanged if `left` is nil - */ - public static func && (left: Where?, right: Where) -> Where { - - if let left = left { - - return left && right - } - return right + return Where(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])) } /** Combines two `Where` predicates together using `OR` operator */ - public static func || (left: Where, right: Where) -> Where { + public static func || (left: Where, right: Where) -> Where { - return Where(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate])) + return Where(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate])) + } + + /** + Inverts the predicate of a `Where` clause using `NOT` operator + */ + public static prefix func ! (clause: Where) -> Where { + + return Where(NSCompoundPredicate(type: .not, subpredicates: [clause.predicate])) + } + + /** + Combines two `Where` predicates together using `AND` operator. + - returns: `left` if `right` is `nil`, otherwise equivalent to `(left && right)` + */ + public static func &&? (left: Where, right: Where?) -> Where { + + if let right = right { + + return left && right + } + return left + } + + /** + Combines two `Where` predicates together using `AND` operator. + - returns: `right` if `left` is `nil`, otherwise equivalent to `(left && right)` + */ + public static func &&? (left: Where?, right: Where) -> Where { + + if let left = left { + + return left && right + } + return right } /** Combines two `Where` predicates together using `OR` operator. - - parameter left: the left hand side `Where` clause - - parameter right: the right hand side `Where` clause - - returns: Returns `left` unchanged if `right` is nil + - returns: `left` if `right` is `nil`, otherwise equivalent to `(left || right)` */ - public static func || (left: Where, right: Where?) -> Where { + public static func ||? (left: Where, right: Where?) -> Where { if let right = right { @@ -97,11 +103,9 @@ public struct Where: WhereClause, FetchClause, QueryClause, De /** Combines two `Where` predicates together using `OR` operator. - - parameter left: the left hand side `Where` clause - - parameter right: the right hand side `Where` clause - - returns: Return `right` unchanged if `left` is nil + - returns: `right` if `left` is `nil`, otherwise equivalent to `(left || right)` */ - public static func || (left: Where?, right: Where) -> Where { + public static func ||? (left: Where?, right: Where) -> Where { if let left = left { @@ -110,14 +114,6 @@ public struct Where: WhereClause, FetchClause, QueryClause, De return right } - /** - Inverts the predicate of a `Where` clause using `NOT` operator - */ - public static prefix func ! (clause: Where) -> Where { - - return Where(NSCompoundPredicate(type: .not, subpredicates: [clause.predicate])) - } - /** Initializes a `Where` clause with a predicate that always evaluates to `true` */ @@ -250,23 +246,21 @@ public struct Where: WhereClause, FetchClause, QueryClause, De self.init(NSPredicate(format: "\(keyPath) IN %@", list.map({ $0 }) as NSArray)) } - /** - Initializes a `Where` clause with an `NSPredicate` - - - parameter predicate: the `NSPredicate` for the fetch or query - */ + + // MARK: AnyWhereClause + + public let predicate: NSPredicate + public init(_ predicate: NSPredicate) { self.predicate = predicate } - // MARK: WhereClause + // MARK: WhereClauseType public typealias ObjectType = D - public let predicate: NSPredicate - // MARK: FetchClause, QueryClause, DeleteClause @@ -301,25 +295,6 @@ public struct Where: WhereClause, FetchClause, QueryClause, De } -// MARK: - WhereClause - -/** - Abstracts the `Where` clause for protocol utilities. - */ -public protocol WhereClause { - - /** - The `DynamicObject` type associated with the clause - */ - associatedtype ObjectType: DynamicObject - - /** - The `NSPredicate` for the fetch or query - */ - var predicate: NSPredicate { get } -} - - // MARK: - Where where D: NSManagedObject public extension Where where D: NSManagedObject { @@ -530,9 +505,9 @@ public extension Where where D: CoreStoreObject { } -// MARK: - Sequence where Iterator.Element: WhereClause +// MARK: - Sequence where Iterator.Element: WhereClauseType -public extension Sequence where Iterator.Element: WhereClause { +public extension Sequence where Iterator.Element: WhereClauseType { /** Combines multiple `Where` predicates together using `AND` operator @@ -550,3 +525,49 @@ public extension Sequence where Iterator.Element: WhereClause { return Where(NSCompoundPredicate(type: .or, subpredicates: self.map({ $0.predicate }))) } } + + +// MARK: - Deprecated + +public extension Where { + + @available(*, deprecated: 4.0, renamed: "&&?") + public static func && (left: Where, right: Where?) -> Where { + + if let right = right { + + return left && right + } + return left + } + + @available(*, deprecated: 4.0, renamed: "&&?") + public static func && (left: Where?, right: Where) -> Where { + + if let left = left { + + return left && right + } + return right + } + + @available(*, deprecated: 4.0, renamed: "||?") + public static func || (left: Where, right: Where?) -> Where { + + if let right = right { + + return left || right + } + return left + } + + @available(*, deprecated: 4.0, renamed: "||?") + public static func || (left: Where?, right: Where) -> Where { + + if let left = left { + + return left || right + } + return right + } +} diff --git a/Sources/WhereClauseType.swift b/Sources/WhereClauseType.swift new file mode 100644 index 0000000..38ece53 --- /dev/null +++ b/Sources/WhereClauseType.swift @@ -0,0 +1,62 @@ +// +// WhereClauseType.swift +// CoreStore +// +// Created by John Estropia on 2017/09/29. +// Copyright © 2017 John Rommel Estropia. All rights reserved. +// + +import Foundation + + +// MARK: - WhereClauseType + +/** + Abstracts the `Where` clause for protocol utilities. + */ +public protocol WhereClauseType: AnyWhereClause { + + /** + The `DynamicObject` type associated with the clause + */ + associatedtype ObjectType: DynamicObject +} + +public extension WhereClauseType { + + /** + Combines two `Where` predicates together using `AND` operator. + - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows AND'ing clauses of unrelated `DynamicObject` types. + */ + public static func && (left: Self, right: TWhere) -> Self { + + return Self.init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])) + } + + /** + Combines two `Where` predicates together using `AND` operator. + - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows AND'ing clauses of unrelated `DynamicObject` types. + */ + public static func && (left: TWhere, right: Self) -> Self { + + return Self.init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])) + } + + /** + Combines two `Where` predicates together using `OR` operator. + - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows OR'ing clauses of unrelated `DynamicObject` types. + */ + public static func || (left: Self, right: TWhere) -> Self { + + return Self.init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate])) + } + + /** + Combines two `Where` predicates together using `OR` operator. + - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows OR'ing clauses of unrelated `DynamicObject` types. + */ + public static func || (left: TWhere, right: Self) -> Self { + + return Self.init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate])) + } +} From 85ed815ec234dd8001b580bb5ab850c3a1bf8bf4 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 1 Oct 2017 00:45:21 +0900 Subject: [PATCH 33/56] WIP: more chain clause builder utilities --- Sources/ChainedClauseBuilder.swift | 27 +++++++++++++++++++++ Sources/WhereClauseType.swift | 39 ------------------------------ 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index 1433d94..b5c714a 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -221,6 +221,11 @@ public extension From where D: NSManagedObject { public extension From where D: CoreStoreObject { + public func `where`(_ clause: (D) -> Where) -> FetchChainBuilder { + + return self.fetchChain(appending: clause(D.meta)) + } + public func select(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) @@ -347,6 +352,14 @@ public extension FetchChainBuilder { } } +public extension FetchChainBuilder where D: CoreStoreObject { + + public func `where`(_ clause: (D) -> Where) -> FetchChainBuilder { + + return self.fetchChain(appending: clause(D.meta)) + } +} + public extension QueryChainBuilder { public func groupBy(_ clause: GroupBy) -> QueryChainBuilder { @@ -431,6 +444,11 @@ public extension QueryChainBuilder where D: NSManagedObject { public extension QueryChainBuilder where D: CoreStoreObject { + public func `where`(_ clause: (D) -> Where) -> QueryChainBuilder { + + return self.queryChain(appending: clause(D.meta)) + } + public func groupBy(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) @@ -511,3 +529,12 @@ public extension SectionMonitorChainBuilder { ) } } + +@available(OSX 10.12, *) +public extension SectionMonitorChainBuilder where D: CoreStoreObject { + + public func `where`(_ clause: (D) -> Where) -> SectionMonitorChainBuilder { + + return self.sectionMonitorChain(appending: clause(D.meta)) + } +} diff --git a/Sources/WhereClauseType.swift b/Sources/WhereClauseType.swift index 38ece53..6c28131 100644 --- a/Sources/WhereClauseType.swift +++ b/Sources/WhereClauseType.swift @@ -21,42 +21,3 @@ public protocol WhereClauseType: AnyWhereClause { */ associatedtype ObjectType: DynamicObject } - -public extension WhereClauseType { - - /** - Combines two `Where` predicates together using `AND` operator. - - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows AND'ing clauses of unrelated `DynamicObject` types. - */ - public static func && (left: Self, right: TWhere) -> Self { - - return Self.init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])) - } - - /** - Combines two `Where` predicates together using `AND` operator. - - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows AND'ing clauses of unrelated `DynamicObject` types. - */ - public static func && (left: TWhere, right: Self) -> Self { - - return Self.init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])) - } - - /** - Combines two `Where` predicates together using `OR` operator. - - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows OR'ing clauses of unrelated `DynamicObject` types. - */ - public static func || (left: Self, right: TWhere) -> Self { - - return Self.init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate])) - } - - /** - Combines two `Where` predicates together using `OR` operator. - - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows OR'ing clauses of unrelated `DynamicObject` types. - */ - public static func || (left: TWhere, right: Self) -> Self { - - return Self.init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate])) - } -} From 1d2eef789426ac22de86f85707c0adb46efbcab1 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 1 Oct 2017 01:01:10 +0900 Subject: [PATCH 34/56] revert --- Sources/WhereClauseType.swift | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Sources/WhereClauseType.swift b/Sources/WhereClauseType.swift index 6c28131..38ece53 100644 --- a/Sources/WhereClauseType.swift +++ b/Sources/WhereClauseType.swift @@ -21,3 +21,42 @@ public protocol WhereClauseType: AnyWhereClause { */ associatedtype ObjectType: DynamicObject } + +public extension WhereClauseType { + + /** + Combines two `Where` predicates together using `AND` operator. + - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows AND'ing clauses of unrelated `DynamicObject` types. + */ + public static func && (left: Self, right: TWhere) -> Self { + + return Self.init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])) + } + + /** + Combines two `Where` predicates together using `AND` operator. + - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows AND'ing clauses of unrelated `DynamicObject` types. + */ + public static func && (left: TWhere, right: Self) -> Self { + + return Self.init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])) + } + + /** + Combines two `Where` predicates together using `OR` operator. + - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows OR'ing clauses of unrelated `DynamicObject` types. + */ + public static func || (left: Self, right: TWhere) -> Self { + + return Self.init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate])) + } + + /** + Combines two `Where` predicates together using `OR` operator. + - Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows OR'ing clauses of unrelated `DynamicObject` types. + */ + public static func || (left: TWhere, right: Self) -> Self { + + return Self.init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate])) + } +} From 06635c9d2fb50ef2c5ab2b7d189edec3a2d5e139 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 2 Oct 2017 08:04:28 +0900 Subject: [PATCH 35/56] orderby utilities --- CoreStoreTests/DynamicModelTests.swift | 1 + Sources/OrderBy.swift | 67 ++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index 30e97cd..4d68ddb 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -237,6 +237,7 @@ class DynamicModelTests: BaseTestDataTestCase { _ = transaction.fetchAll( From() .where(\.age == 10 && \Animal.species == "Dog") + .orderBy(.ascending({ $0.species })) ) _ = transaction.fetchAll( From(), diff --git a/Sources/OrderBy.swift b/Sources/OrderBy.swift index f5199c4..247b66a 100644 --- a/Sources/OrderBy.swift +++ b/Sources/OrderBy.swift @@ -257,6 +257,73 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause } } +public extension OrderBy where D: CoreStoreObject { + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ attribute: (D) -> A) -> SortKey where A: ValueContainer.Required { + + return .ascending(attribute(D.meta).keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ attribute: (D) -> A) -> SortKey where A: ValueContainer.Optional { + + return .ascending(attribute(D.meta).keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ attribute: (D) -> A) -> SortKey where A: TransformableContainer.Required { + + return .ascending(attribute(D.meta).keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in ascending order + */ + public static func ascending(_ attribute: (D) -> A) -> SortKey where A: TransformableContainer.Optional { + + return .ascending(attribute(D.meta).keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ attribute: (D) -> A) -> SortKey where A: ValueContainer.Required { + + return .descending(attribute(D.meta).keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ attribute: (D) -> A) -> SortKey where A: ValueContainer.Optional { + + return .descending(attribute(D.meta).keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ attribute: (D) -> A) -> SortKey where A: TransformableContainer.Required { + + return .descending(attribute(D.meta).keyPath) + } + + /** + Indicates that the `KeyPathString` should be sorted in descending order + */ + public static func descending(_ attribute: (D) -> A) -> SortKey where A: TransformableContainer.Optional { + + return .descending(attribute(D.meta).keyPath) + } +} + // MARK: - OrderByClause From 9eaf85388c5b27b25aeae30297f91ee7554579bd Mon Sep 17 00:00:00 2001 From: John Estropia Date: Mon, 2 Oct 2017 10:33:59 +0900 Subject: [PATCH 36/56] relax generic type requirements for some Where utilities --- Sources/ChainedClauseBuilder.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index b5c714a..4403610 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -221,7 +221,7 @@ public extension From where D: NSManagedObject { public extension From where D: CoreStoreObject { - public func `where`(_ clause: (D) -> Where) -> FetchChainBuilder { + public func `where`(_ clause: (D) -> T) -> FetchChainBuilder { return self.fetchChain(appending: clause(D.meta)) } @@ -354,7 +354,7 @@ public extension FetchChainBuilder { public extension FetchChainBuilder where D: CoreStoreObject { - public func `where`(_ clause: (D) -> Where) -> FetchChainBuilder { + public func `where`(_ clause: (D) -> T) -> FetchChainBuilder { return self.fetchChain(appending: clause(D.meta)) } @@ -444,7 +444,7 @@ public extension QueryChainBuilder where D: NSManagedObject { public extension QueryChainBuilder where D: CoreStoreObject { - public func `where`(_ clause: (D) -> Where) -> QueryChainBuilder { + public func `where`(_ clause: (D) -> T) -> QueryChainBuilder { return self.queryChain(appending: clause(D.meta)) } @@ -533,7 +533,7 @@ public extension SectionMonitorChainBuilder { @available(OSX 10.12, *) public extension SectionMonitorChainBuilder where D: CoreStoreObject { - public func `where`(_ clause: (D) -> Where) -> SectionMonitorChainBuilder { + public func `where`(_ clause: (D) -> T) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause(D.meta)) } From 11743dfb5f36296ffce11839e3b73c038ab75834 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Mon, 2 Oct 2017 11:08:44 +0900 Subject: [PATCH 37/56] oops --- Sources/OrderBy.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OrderBy.swift b/Sources/OrderBy.swift index 247b66a..aed248c 100644 --- a/Sources/OrderBy.swift +++ b/Sources/OrderBy.swift @@ -257,7 +257,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause } } -public extension OrderBy where D: CoreStoreObject { +public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in ascending order From 780ff4e60b5b29c3a4e06f6793be941e3d2dcf8a Mon Sep 17 00:00:00 2001 From: John Estropia Date: Mon, 2 Oct 2017 11:10:28 +0900 Subject: [PATCH 38/56] fix compile errors --- Sources/OrderBy.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/OrderBy.swift b/Sources/OrderBy.swift index aed248c..39e92ee 100644 --- a/Sources/OrderBy.swift +++ b/Sources/OrderBy.swift @@ -262,7 +262,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: (D) -> A) -> SortKey where A: ValueContainer.Required { + public static func ascending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: ValueContainer.Required { return .ascending(attribute(D.meta).keyPath) } @@ -270,7 +270,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: (D) -> A) -> SortKey where A: ValueContainer.Optional { + public static func ascending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: ValueContainer.Optional { return .ascending(attribute(D.meta).keyPath) } @@ -278,7 +278,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: (D) -> A) -> SortKey where A: TransformableContainer.Required { + public static func ascending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: TransformableContainer.Required { return .ascending(attribute(D.meta).keyPath) } @@ -286,7 +286,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: (D) -> A) -> SortKey where A: TransformableContainer.Optional { + public static func ascending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: TransformableContainer.Optional { return .ascending(attribute(D.meta).keyPath) } @@ -294,7 +294,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: (D) -> A) -> SortKey where A: ValueContainer.Required { + public static func descending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: ValueContainer.Required { return .descending(attribute(D.meta).keyPath) } @@ -302,7 +302,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: (D) -> A) -> SortKey where A: ValueContainer.Optional { + public static func descending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: ValueContainer.Optional { return .descending(attribute(D.meta).keyPath) } @@ -310,7 +310,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: (D) -> A) -> SortKey where A: TransformableContainer.Required { + public static func descending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: TransformableContainer.Required { return .descending(attribute(D.meta).keyPath) } @@ -318,7 +318,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: (D) -> A) -> SortKey where A: TransformableContainer.Optional { + public static func descending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: TransformableContainer.Optional { return .descending(attribute(D.meta).keyPath) } From 97242d9726229d0a0afe94c69585c4f33855276c Mon Sep 17 00:00:00 2001 From: John Estropia Date: Mon, 2 Oct 2017 12:00:45 +0900 Subject: [PATCH 39/56] added type-erasers for CoreStoreObject property containers --- CoreStore.xcodeproj/project.pbxproj | 10 ++++ Sources/AnyCoreStoreKeyPath.swift | 83 +++++++++++++++++++++++++++++ Sources/OrderBy.swift | 56 ++----------------- 3 files changed, 97 insertions(+), 52 deletions(-) create mode 100644 Sources/AnyCoreStoreKeyPath.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index c7c4c1e..8967c1e 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -520,6 +520,10 @@ B5CA2B091F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; B5CA2B0A1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; B5CA2B0B1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; + B5CA2B121F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */; }; + B5CA2B131F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */; }; + B5CA2B141F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */; }; + B5CA2B151F81DBFF004B1936 /* AnyCoreStoreKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */; }; B5D1E22C19FA9FBC003B2874 /* CoreStoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */; }; B5D339B41E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; B5D339B51E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; @@ -862,6 +866,7 @@ B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = ""; }; B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreFetchedResultsController.swift; sourceTree = ""; }; B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhereClauseType.swift; sourceTree = ""; }; + B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCoreStoreKeyPath.swift; sourceTree = ""; }; B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreError.swift; sourceTree = ""; }; B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicModelTests.swift; sourceTree = ""; }; B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreObject.swift; sourceTree = ""; }; @@ -1290,6 +1295,7 @@ children = ( B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */, B5D339EB1E9495E500C880DE /* CoreStoreObject+Querying.swift */, + B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */, ); name = "KeyPath Utilities"; sourceTree = ""; @@ -1968,6 +1974,7 @@ B549F65E1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B5E84F211AFF84860064E85B /* CoreStore+Observing.swift in Sources */, B559CD431CAA8B6300E4D58B /* CSSetupResult.swift in Sources */, + B5CA2B121F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */, B5A991EC1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5FE4DA71C84FB4400FA6A91 /* InMemoryStore.swift in Sources */, B52F743D1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, @@ -2160,6 +2167,7 @@ B549F65F1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B559CD451CAA8B6300E4D58B /* CSSetupResult.swift in Sources */, 82BA18B81C4BBD4200A0916E /* TypeErasedClauses.swift in Sources */, + B5CA2B131F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */, B5A991ED1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5ECDBEE1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, @@ -2352,6 +2360,7 @@ B549F6611E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B52DD19B1BE1F92800949AFE /* CoreStoreLogger.swift in Sources */, B52DD1991BE1F92800949AFE /* DefaultLogger.swift in Sources */, + B5CA2B151F81DBFF004B1936 /* AnyCoreStoreKeyPath.swift in Sources */, B5A991EF1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5220E201D130813009BC71E /* CSObjectMonitor.swift in Sources */, B52F74401E9B8724005F3DAC /* DynamicSchema.swift in Sources */, @@ -2544,6 +2553,7 @@ B549F6601E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B559CD461CAA8B6300E4D58B /* CSSetupResult.swift in Sources */, B56321A61BD65216006C9394 /* MigrationType.swift in Sources */, + B5CA2B141F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */, B5A991EE1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5ECDBEF1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743F1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, diff --git a/Sources/AnyCoreStoreKeyPath.swift b/Sources/AnyCoreStoreKeyPath.swift new file mode 100644 index 0000000..36b4add --- /dev/null +++ b/Sources/AnyCoreStoreKeyPath.swift @@ -0,0 +1,83 @@ +// +// AnyCoreStoreKeyPath.swift +// CoreStore +// +// Created by John Estropia on 2017/10/02. +// Copyright © 2017 John Rommel Estropia. All rights reserved. +// + +import Foundation + + +// MARK: - AnyCoreStoreKeyPath + +public protocol AnyCoreStoreKeyPath { + + var cs_keyPathString: String { get } +} + +// SE-0143 is not implemented: https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md +//extension KeyPath: AnyCoreStoreKeyPath where Root: NSManagedObject, Value: ImportableAttributeType { +// +// public var cs_keyPathString: String { +// +// return self._kvcKeyPathString! +// } +//} + +extension ValueContainer.Required: AnyCoreStoreKeyPath { + + public var cs_keyPathString: String { + + return self.keyPath + } +} + +extension ValueContainer.Optional: AnyCoreStoreKeyPath { + + public var cs_keyPathString: String { + + return self.keyPath + } +} + +extension TransformableContainer.Required: AnyCoreStoreKeyPath { + + public var cs_keyPathString: String { + + return self.keyPath + } +} + +extension TransformableContainer.Optional: AnyCoreStoreKeyPath { + + public var cs_keyPathString: String { + + return self.keyPath + } +} + +extension RelationshipContainer.ToOne: AnyCoreStoreKeyPath { + + public var cs_keyPathString: String { + + return self.keyPath + } +} + +extension RelationshipContainer.ToManyOrdered: AnyCoreStoreKeyPath { + + public var cs_keyPathString: String { + + return self.keyPath + } +} + +extension RelationshipContainer.ToManyUnordered: AnyCoreStoreKeyPath { + + public var cs_keyPathString: String { + + return self.keyPath + } +} + diff --git a/Sources/OrderBy.swift b/Sources/OrderBy.swift index 39e92ee..96e575d 100644 --- a/Sources/OrderBy.swift +++ b/Sources/OrderBy.swift @@ -262,65 +262,17 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: ValueContainer.Required { + public static func ascending(_ attribute: (D) -> T) -> OrderBy.SortKey { - return .ascending(attribute(D.meta).keyPath) - } - - /** - Indicates that the `KeyPathString` should be sorted in ascending order - */ - public static func ascending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: ValueContainer.Optional { - - return .ascending(attribute(D.meta).keyPath) - } - - /** - Indicates that the `KeyPathString` should be sorted in ascending order - */ - public static func ascending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: TransformableContainer.Required { - - return .ascending(attribute(D.meta).keyPath) - } - - /** - Indicates that the `KeyPathString` should be sorted in ascending order - */ - public static func ascending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: TransformableContainer.Optional { - - return .ascending(attribute(D.meta).keyPath) + return .ascending(attribute(D.meta).cs_keyPathString) } /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: ValueContainer.Required { + public static func descending(_ attribute: (D) -> T) -> OrderBy.SortKey { - return .descending(attribute(D.meta).keyPath) - } - - /** - Indicates that the `KeyPathString` should be sorted in descending order - */ - public static func descending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: ValueContainer.Optional { - - return .descending(attribute(D.meta).keyPath) - } - - /** - Indicates that the `KeyPathString` should be sorted in descending order - */ - public static func descending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: TransformableContainer.Required { - - return .descending(attribute(D.meta).keyPath) - } - - /** - Indicates that the `KeyPathString` should be sorted in descending order - */ - public static func descending(_ attribute: (D) -> A) -> OrderBy.SortKey where A: TransformableContainer.Optional { - - return .descending(attribute(D.meta).keyPath) + return .descending(attribute(D.meta).cs_keyPathString) } } From 106275b2dd07e5bb73e0ed68b808bbff29151272 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Sat, 7 Oct 2017 12:21:27 +0800 Subject: [PATCH 40/56] fix compile error: inheritance from 'AnyObject' --- Sources/CSListObserver.swift | 2 +- Sources/CSObjectObserver.swift | 2 +- Sources/CoreDataNativeType.swift | 2 +- Sources/CoreStoreBridge.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/CSListObserver.swift b/Sources/CSListObserver.swift index ff57b34..e9dfafd 100644 --- a/Sources/CSListObserver.swift +++ b/Sources/CSListObserver.swift @@ -42,7 +42,7 @@ import CoreData */ @available(OSX 10.12, *) @objc -public protocol CSListObserver: class, AnyObject { +public protocol CSListObserver: class { /** Handles processing just before a change to the observed list occurs diff --git a/Sources/CSObjectObserver.swift b/Sources/CSObjectObserver.swift index 32fd3f9..a5392eb 100644 --- a/Sources/CSObjectObserver.swift +++ b/Sources/CSObjectObserver.swift @@ -40,7 +40,7 @@ import CoreData */ @available(OSX 10.12, *) @objc -public protocol CSObjectObserver: class, AnyObject { +public protocol CSObjectObserver: class { /** Handles processing just before a change to the observed `object` occurs diff --git a/Sources/CoreDataNativeType.swift b/Sources/CoreDataNativeType.swift index c382a6a..a358fd5 100644 --- a/Sources/CoreDataNativeType.swift +++ b/Sources/CoreDataNativeType.swift @@ -33,7 +33,7 @@ import CoreData Objective-C Foundation types that are natively supported by Core Data managed attributes all conform to `CoreDataNativeType`. */ @objc -public protocol CoreDataNativeType: class, NSObjectProtocol, AnyObject {} +public protocol CoreDataNativeType: class, NSObjectProtocol {} // MARK: - NSNumber diff --git a/Sources/CoreStoreBridge.swift b/Sources/CoreStoreBridge.swift index 7d77680..33ee605 100644 --- a/Sources/CoreStoreBridge.swift +++ b/Sources/CoreStoreBridge.swift @@ -31,7 +31,7 @@ import Foundation /** `CoreStoreObjectiveCType`s are Objective-C accessible classes that represent CoreStore's Swift types. */ -public protocol CoreStoreObjectiveCType: class, AnyObject { +public protocol CoreStoreObjectiveCType: class { /** The corresponding Swift type From 204025721cd496b28eae67378467111014d18841 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 13 Oct 2017 08:10:26 +0900 Subject: [PATCH 41/56] version bump --- CoreStore.podspec | 2 +- Sources/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CoreStore.podspec b/CoreStore.podspec index fef69a2..e4b8a9d 100644 --- a/CoreStore.podspec +++ b/CoreStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CoreStore" - s.version = "4.1.3" + s.version = "5.0.0" s.license = "MIT" s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift" s.homepage = "https://github.com/JohnEstropia/CoreStore" diff --git a/Sources/Info.plist b/Sources/Info.plist index a19ea3e..e677190 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.1.3 + 5.0.0 CFBundleSignature ???? CFBundleVersion From b6ee0b014f60430cb9e60b4f159fc5429372d4ac Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Tue, 24 Oct 2017 00:31:27 +0900 Subject: [PATCH 42/56] WIP: documentations --- Sources/BaseDataTransaction+Querying.swift | 106 ++++++++++++++++-- Sources/BaseDataTransaction.swift | 24 ++-- Sources/ChainedClauseBuilder.swift | 5 + Sources/CoreStore+Querying.swift | 104 +++++++++++++++-- Sources/DataStack+Querying.swift | 104 +++++++++++++++-- Sources/FetchableSource.swift | 65 ++++++++++- Sources/ImportableAttributeType.swift | 6 + Sources/Into.swift | 16 +-- Sources/NSManagedObjectContext+Querying.swift | 3 - Sources/QueryableSource.swift | 39 ++++++- Sources/Relationship.swift | 9 ++ Sources/Transformable.swift | 10 +- Sources/Value.swift | 7 ++ 13 files changed, 444 insertions(+), 54 deletions(-) diff --git a/Sources/BaseDataTransaction+Querying.swift b/Sources/BaseDataTransaction+Querying.swift index 0ece0ae..7661762 100644 --- a/Sources/BaseDataTransaction+Querying.swift +++ b/Sources/BaseDataTransaction+Querying.swift @@ -68,7 +68,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { /** Deletes all `DynamicObject`s that satisfy the specified conditions. ``` - transaction.deleteAll(From().where(\.age > 50) + transaction.deleteAll(From().where(\.age > 50)) ``` - parameter clauseChain: a `FetchChainableBuilderType` clause chain created from a `From` clause - returns: the number of `DynamicObject`s deleted @@ -163,7 +163,18 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchOne(from, fetchClauses) } - // TODO: docs + /** + Fetches the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let youngestTeen = transaction.fetchOne( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` + */ public func fetchOne(_ clauseChain: B) -> B.ObjectType? { CoreStore.assert( @@ -205,7 +216,18 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchAll(from, fetchClauses) } - // TODO: docs + /** + Fetches all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let people = transaction.fetchAll( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` + */ public func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { CoreStore.assert( @@ -247,7 +269,18 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchCount(from, fetchClauses) } - // TODO: docs + /** + Fetches the number of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let numberOfAdults = transaction.fetchCount( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the number `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func fetchCount(_ clauseChain: B) -> Int? { CoreStore.assert( @@ -289,7 +322,18 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchObjectID(from, fetchClauses) } - // TODO: docs + /** + Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let youngestTeenID = transaction.fetchObjectID( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` + */ public func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? { CoreStore.assert( @@ -331,7 +375,18 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.fetchObjectIDs(from, fetchClauses) } - // TODO: docs + /** + Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let idsOfAdults = transaction.fetchObjectIDs( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? { CoreStore.assert( @@ -382,7 +437,20 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.queryValue(from, selectClause, queryClauses) } - // TODO: docs + /** + Queries a property value or aggregate as specified by the `QueryChainableBuilderType` built from a chain of 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. + ``` + let averageAdultAge = transaction.queryValue( + From() + .select(Int.self, .average(\.age)) + .where(\.age > 18) + ) + ``` + - parameter clauseChain: a `QueryChainableBuilderType` indicating the property/aggregate to fetch and the series of queries for the request. + - returns: the result of the the query as specified by the `QueryChainableBuilderType` + */ public func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType { CoreStore.assert( @@ -430,7 +498,29 @@ extension BaseDataTransaction: FetchableSource, QueryableSource { return self.context.queryAttributes(from, selectClause, queryClauses) } - // TODO: docs + /** + Queries a dictionary of attribute values or as specified by the `QueryChainableBuilderType` built from a chain of 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. + ``` + let results = dataStack.queryAttributes( + From() + .select( + NSDictionary.self, + .attribute(\.age, as: "age"), + .count(\.age, as: "numberOfPeople") + ) + .groupBy(\.age) + ) + for dictionary in results! { + let age = dictionary["age"] as! Int + let count = dictionary["numberOfPeople"] as! Int + print("There are \(count) people who are \(age) years old." + } + ``` + - parameter clauseChain: a `QueryChainableBuilderType` indicating the properties to fetch and the series of queries for the request. + - returns: the result of the the query as specified by the `QueryChainableBuilderType` + */ public func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary { CoreStore.assert( diff --git a/Sources/BaseDataTransaction.swift b/Sources/BaseDataTransaction.swift index b9590a6..0c75700 100644 --- a/Sources/BaseDataTransaction.swift +++ b/Sources/BaseDataTransaction.swift @@ -116,10 +116,10 @@ public /*abstract*/ class BaseDataTransaction { } /** - Returns an editable proxy of a specified `NSManagedObject`. + Returns an editable proxy of a specified `NSManagedObject` or `CoreStoreObject`. - - parameter object: the `NSManagedObject` type to be edited - - returns: an editable proxy for the specified `NSManagedObject`. + - parameter object: the `NSManagedObject` or `CoreStoreObject` type to be edited + - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`. */ public func edit(_ object: D?) -> D? { @@ -139,7 +139,7 @@ public /*abstract*/ class BaseDataTransaction { - parameter into: an `Into` clause specifying the entity type - parameter objectID: the `NSManagedObjectID` for the object to be edited - - returns: an editable proxy for the specified `NSManagedObject`. + - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`. */ public func edit(_ into: Into, _ objectID: NSManagedObjectID) -> D? { @@ -156,9 +156,9 @@ public /*abstract*/ class BaseDataTransaction { } /** - Deletes a specified `NSManagedObject`. + Deletes a specified `NSManagedObject` or `CoreStoreObject`. - - parameter object: the `NSManagedObject` to be deleted + - parameter object: the `NSManagedObject` or `CoreStoreObject` to be deleted */ public func delete(_ object: D?) { @@ -173,11 +173,11 @@ public /*abstract*/ class BaseDataTransaction { } /** - Deletes the specified `NSManagedObject`s. + Deletes the specified `NSManagedObject`s or `CoreStoreObject`s. - - parameter object1: the `NSManagedObject` to be deleted - - parameter object2: another `NSManagedObject` to be deleted - - parameter objects: other `NSManagedObject`s to be deleted + - parameter object1: the `NSManagedObject` or `CoreStoreObject` to be deleted + - parameter object2: another `NSManagedObject` or `CoreStoreObject` to be deleted + - parameter objects: other `NSManagedObject`s or `CoreStoreObject`s to be deleted */ public func delete(_ object1: D?, _ object2: D?, _ objects: D?...) { @@ -185,9 +185,9 @@ public /*abstract*/ class BaseDataTransaction { } /** - Deletes the specified `NSManagedObject`s. + Deletes the specified `NSManagedObject`s or `CoreStoreObject`s. - - parameter objects: the `NSManagedObject`s to be deleted + - parameter objects: the `NSManagedObject`s or `CoreStoreObject`s to be deleted */ public func delete(_ objects: S) where S.Iterator.Element: DynamicObject { diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/ChainedClauseBuilder.swift index 4403610..a20974d 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/ChainedClauseBuilder.swift @@ -27,6 +27,8 @@ import Foundation import CoreData +// MARK: - FetchChainableBuilderType + public protocol FetchChainableBuilderType { associatedtype ObjectType: DynamicObject @@ -35,6 +37,9 @@ public protocol FetchChainableBuilderType { var fetchClauses: [FetchClause] { get set } } + +// MARK: - QueryChainableBuilderType + public protocol QueryChainableBuilderType { associatedtype ObjectType: DynamicObject diff --git a/Sources/CoreStore+Querying.swift b/Sources/CoreStore+Querying.swift index 4a6e43f..4c4cad7 100644 --- a/Sources/CoreStore+Querying.swift +++ b/Sources/CoreStore+Querying.swift @@ -99,7 +99,18 @@ public extension CoreStore { return self.defaultStack.fetchOne(from, fetchClauses) } - // TODO: docs + /** + Fetches the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let youngestTeen = CoreStore.fetchOne( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` + */ public static func fetchOne(_ clauseChain: B) -> B.ObjectType? { return self.defaultStack.fetchOne(clauseChain) @@ -129,7 +140,18 @@ public extension CoreStore { return self.defaultStack.fetchAll(from, fetchClauses) } - // TODO: docs + /** + Fetches all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let people = CoreStore.fetchAll( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` + */ public static func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { return self.defaultStack.fetchAll(clauseChain) @@ -159,7 +181,18 @@ public extension CoreStore { return self.defaultStack.fetchCount(from, fetchClauses) } - // TODO: docs + /** + Fetches the number of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let numberOfAdults = CoreStore.fetchCount( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the number `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public static func fetchCount(_ clauseChain: B) -> Int? { return self.defaultStack.fetchCount(clauseChain) @@ -189,7 +222,18 @@ public extension CoreStore { return self.defaultStack.fetchObjectID(from, fetchClauses) } - // TODO: docs + /** + Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let youngestTeenID = CoreStore.fetchObjectID( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` + */ public static func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? { return self.defaultStack.fetchObjectID(clauseChain) @@ -219,7 +263,18 @@ public extension CoreStore { return self.defaultStack.fetchObjectIDs(from, fetchClauses) } - // TODO: docs + /** + Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let idsOfAdults = transaction.fetchObjectIDs( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public static func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? { return self.defaultStack.fetchObjectIDs(clauseChain) @@ -255,7 +310,20 @@ public extension CoreStore { return self.defaultStack.queryValue(from, selectClause, queryClauses) } - // TODO: docs + /** + Queries a property value or aggregate as specified by the `QueryChainableBuilderType` built from a chain of 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. + ``` + let averageAdultAge = CoreStore.queryValue( + From() + .select(Int.self, .average(\.age)) + .where(\.age > 18) + ) + ``` + - parameter clauseChain: a `QueryChainableBuilderType` indicating the property/aggregate to fetch and the series of queries for the request. + - returns: the result of the the query as specified by the `QueryChainableBuilderType` + */ public static func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType { return self.defaultStack.queryValue(clauseChain) @@ -291,7 +359,29 @@ public extension CoreStore { return self.defaultStack.queryAttributes(from, selectClause, queryClauses) } - // TODO: docs + /** + Queries a dictionary of attribute values or as specified by the `QueryChainableBuilderType` built from a chain of 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. + ``` + let results = CoreStore.queryAttributes( + From() + .select( + NSDictionary.self, + .attribute(\.age, as: "age"), + .count(\.age, as: "numberOfPeople") + ) + .groupBy(\.age) + ) + for dictionary in results! { + let age = dictionary["age"] as! Int + let count = dictionary["numberOfPeople"] as! Int + print("There are \(count) people who are \(age) years old." + } + ``` + - parameter clauseChain: a `QueryChainableBuilderType` indicating the properties to fetch and the series of queries for the request. + - returns: the result of the the query as specified by the `QueryChainableBuilderType` + */ public static func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary { return self.defaultStack.queryAttributes(clauseChain) diff --git a/Sources/DataStack+Querying.swift b/Sources/DataStack+Querying.swift index 409644e..341d519 100644 --- a/Sources/DataStack+Querying.swift +++ b/Sources/DataStack+Querying.swift @@ -109,7 +109,18 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchOne(from, fetchClauses) } - // TODO: docs + /** + Fetches the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let youngestTeen = dataStack.fetchOne( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` + */ public func fetchOne(_ clauseChain: B) -> B.ObjectType? { CoreStore.assert( @@ -151,7 +162,18 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchAll(from, fetchClauses) } - // TODO: docs + /** + Fetches all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let people = dataStack.fetchAll( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` + */ public func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { CoreStore.assert( @@ -193,7 +215,18 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchCount(from, fetchClauses) } - // TODO: docs + /** + Fetches the number of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let numberOfAdults = dataStack.fetchCount( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the number `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func fetchCount(_ clauseChain: B) -> Int? { CoreStore.assert( @@ -235,7 +268,18 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchObjectID(from, fetchClauses) } - // TODO: docs + /** + Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let youngestTeenID = dataStack.fetchObjectID( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` + */ public func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? { CoreStore.assert( @@ -277,7 +321,18 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.fetchObjectIDs(from, fetchClauses) } - // TODO: docs + /** + Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let idsOfAdults = dataStack.fetchObjectIDs( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? { CoreStore.assert( @@ -328,7 +383,20 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.queryValue(from, selectClause, queryClauses) } - // TODO: docs + /** + Queries a property value or aggregate as specified by the `QueryChainableBuilderType` built from a chain of 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. + ``` + let averageAdultAge = dataStack.queryValue( + From() + .select(Int.self, .average(\.age)) + .where(\.age > 18) + ) + ``` + - parameter clauseChain: a `QueryChainableBuilderType` indicating the property/aggregate to fetch and the series of queries for the request. + - returns: the result of the the query as specified by the `QueryChainableBuilderType` + */ public func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType { CoreStore.assert( @@ -376,7 +444,29 @@ extension DataStack: FetchableSource, QueryableSource { return self.mainContext.queryAttributes(from, selectClause, queryClauses) } - // TODO: docs + /** + Queries a dictionary of attribute values or as specified by the `QueryChainableBuilderType` built from a chain of 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. + ``` + let results = dataStack.queryAttributes( + From() + .select( + NSDictionary.self, + .attribute(\.age, as: "age"), + .count(\.age, as: "numberOfPeople") + ) + .groupBy(\.age) + ) + for dictionary in results! { + let age = dictionary["age"] as! Int + let count = dictionary["numberOfPeople"] as! Int + print("There are \(count) people who are \(age) years old." + } + ``` + - parameter clauseChain: a `QueryChainableBuilderType` indicating the properties to fetch and the series of queries for the request. + - returns: the result of the the query as specified by the `QueryChainableBuilderType` + */ public func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary { CoreStore.assert( diff --git a/Sources/FetchableSource.swift b/Sources/FetchableSource.swift index ce63ef3..ce4a425 100644 --- a/Sources/FetchableSource.swift +++ b/Sources/FetchableSource.swift @@ -84,7 +84,18 @@ public protocol FetchableSource: class { */ func fetchOne(_ from: From, _ fetchClauses: [FetchClause]) -> D? - // TODO: docs + /** + Fetches the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let youngestTeen = source.fetchOne( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` + */ func fetchOne(_ clauseChain: B) -> B.ObjectType? /** @@ -105,7 +116,18 @@ public protocol FetchableSource: class { */ func fetchAll(_ from: From, _ fetchClauses: [FetchClause]) -> [D]? - // TODO: docs + /** + Fetches all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let people = source.fetchAll( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` + */ func fetchAll(_ clauseChain: B) -> [B.ObjectType]? /** @@ -126,7 +148,18 @@ public protocol FetchableSource: class { */ func fetchCount(_ from: From, _ fetchClauses: [FetchClause]) -> Int? - // TODO: docs + /** + Fetches the number of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let numberOfAdults = source.fetchCount( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the number `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ func fetchCount(_ clauseChain: B) -> Int? /** @@ -147,7 +180,18 @@ public protocol FetchableSource: class { */ func fetchObjectID(_ from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? - // TODO: docs + /** + Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let youngestTeenID = source.fetchObjectID( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` + */ func fetchObjectID(_ clauseChain: B) -> NSManagedObjectID? /** @@ -168,7 +212,18 @@ public protocol FetchableSource: class { */ func fetchObjectIDs(_ from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? - // TODO: docs + /** + Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let idsOfAdults = source.fetchObjectIDs( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ func fetchObjectIDs(_ clauseChain: B) -> [NSManagedObjectID]? /** diff --git a/Sources/ImportableAttributeType.swift b/Sources/ImportableAttributeType.swift index 5c3dd18..6c78e15 100644 --- a/Sources/ImportableAttributeType.swift +++ b/Sources/ImportableAttributeType.swift @@ -163,12 +163,18 @@ extension UUID: ImportableAttributeType {} extension RawRepresentable where RawValue: ImportableAttributeType { + /** + Creates an instance of this type from its `QueryableNativeType` value. + */ @inline(__always) public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { return RawValue.cs_fromQueryableNativeType(value).flatMap({ self.init(rawValue: $0) }) } + /** + Creates `QueryableNativeType` value from this instance. + */ @inline(__always) public func cs_toQueryableNativeType() -> QueryableNativeType { diff --git a/Sources/Into.swift b/Sources/Into.swift index f016a05..f3c0d0a 100644 --- a/Sources/Into.swift +++ b/Sources/Into.swift @@ -64,11 +64,11 @@ public struct Into: Hashable { } /** - Initializes an `Into` clause with the specified entity type. + Initializes an `Into` clause with the specified entity type. This is useful for querying a subclass while binding the generic type with a base class. ``` - let person = transaction.create(Into(MyPersonEntity.self)) + let person = transaction.create(Into(MyEmployeeEntity.self)) ``` - - parameter entity: the `NSManagedObject` type to be created + - parameter entity: the `NSManagedObject` or `CoreStoreObject` type to be created */ public init(_ entity: D.Type) { @@ -80,7 +80,7 @@ public struct Into: Hashable { ``` let person = transaction.create(Into("Configuration1")) ``` - - parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration. + - parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s or `CoreStoreObject`'s entity type. Set to `nil` to use the default configuration. */ public init(_ configuration: ModelConfiguration) { @@ -88,12 +88,12 @@ public struct Into: Hashable { } /** - Initializes an `Into` clause with the specified entity type and configuration. + Initializes an `Into` clause with the specified entity type and configuration. This is useful for querying a subclass while binding the generic type with a base class. ``` - let person = transaction.create(Into(MyPersonEntity.self, "Configuration1")) + let person = transaction.create(Into(MyEmployeeEntity.self, "Configuration1")) ``` - - parameter entity: the `NSManagedObject` type to be created - - parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration. + - parameter entity: the `NSManagedObject` or `CoreStoreObject` type to be created + - parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s or `CoreStoreObject`'s entity type. Set to `nil` to use the default configuration. */ public init(_ entity: D.Type, _ configuration: ModelConfiguration) { diff --git a/Sources/NSManagedObjectContext+Querying.swift b/Sources/NSManagedObjectContext+Querying.swift index 51f626b..706c07b 100644 --- a/Sources/NSManagedObjectContext+Querying.swift +++ b/Sources/NSManagedObjectContext+Querying.swift @@ -123,7 +123,6 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.fetchOne(fetchRequest.dynamicCast()).flatMap(from.entityClass.cs_fromRaw) } - // TODO: docs @nonobjc public func fetchOne(_ clauseChain: B) -> B.ObjectType? { @@ -154,7 +153,6 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.fetchAll(fetchRequest.dynamicCast())?.map(entityClass.cs_fromRaw) } - // TODO: docs @nonobjc public func fetchAll(_ clauseChain: B) -> [B.ObjectType]? { @@ -181,7 +179,6 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return self.fetchCount(fetchRequest.dynamicCast()) } - // TODO: docs @nonobjc public func fetchCount(_ clauseChain: B) -> Int? { diff --git a/Sources/QueryableSource.swift b/Sources/QueryableSource.swift index b8fb498..292a530 100644 --- a/Sources/QueryableSource.swift +++ b/Sources/QueryableSource.swift @@ -58,7 +58,20 @@ public protocol QueryableSource: class { */ func queryValue(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? - // TODO: docs + /** + Queries a property value or aggregate as specified by the `QueryChainableBuilderType` built from a chain of 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. + ``` + let averageAdultAge = dataStack.queryValue( + From() + .select(Int.self, .average(\.age)) + .where(\.age > 18) + ) + ``` + - parameter clauseChain: a `QueryChainableBuilderType` indicating the property/aggregate to fetch and the series of queries for the request. + - returns: the result of the the query as specified by the `QueryChainableBuilderType` + */ func queryValue(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType /** @@ -85,7 +98,29 @@ public protocol QueryableSource: class { */ func queryAttributes(_ from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[String: Any]]? - // TODO: docs + /** + Queries a dictionary of attribute values or as specified by the `QueryChainableBuilderType` built from a chain of 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. + ``` + let results = source.queryAttributes( + From() + .select( + NSDictionary.self, + .attribute(\.age, as: "age"), + .count(\.age, as: "numberOfPeople") + ) + .groupBy(\.age) + ) + for dictionary in results! { + let age = dictionary["age"] as! Int + let count = dictionary["numberOfPeople"] as! Int + print("There are \(count) people who are \(age) years old." + } + ``` + - parameter clauseChain: a `QueryChainableBuilderType` indicating the properties to fetch and the series of queries for the request. + - returns: the result of the the query as specified by the `QueryChainableBuilderType` + */ func queryAttributes(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary /** diff --git a/Sources/Relationship.swift b/Sources/Relationship.swift index 7720f71..744ec9a 100644 --- a/Sources/Relationship.swift +++ b/Sources/Relationship.swift @@ -234,6 +234,9 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol + /** + The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. + */ public let keyPath: KeyPathString internal let isToMany = false @@ -502,6 +505,9 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol + /** + The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. + */ public let keyPath: KeyPathString internal let isToMany = true @@ -776,6 +782,9 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol + /** + The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. + */ public let keyPath: KeyPathString internal let isToMany = true diff --git a/Sources/Transformable.swift b/Sources/Transformable.swift index c8896f7..59dc823 100644 --- a/Sources/Transformable.swift +++ b/Sources/Transformable.swift @@ -191,14 +191,17 @@ public enum TransformableContainer { // MARK: AttributeProtocol + + /** + The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. + */ + public let keyPath: KeyPathString internal static var attributeType: NSAttributeType { return .transformableAttributeType } - public let keyPath: KeyPathString - internal let isOptional = false internal let isIndexed: Bool internal let isTransient: Bool @@ -420,6 +423,9 @@ public enum TransformableContainer { return .transformableAttributeType } + /** + The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. + */ public let keyPath: KeyPathString internal let isOptional = true diff --git a/Sources/Value.swift b/Sources/Value.swift index e72debb..46db6f1 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -195,6 +195,9 @@ public enum ValueContainer { return V.cs_rawAttributeType } + /** + The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. + */ public let keyPath: KeyPathString internal let isOptional = false @@ -421,7 +424,11 @@ public enum ValueContainer { return V.cs_rawAttributeType } + /** + The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. + */ public let keyPath: KeyPathString + internal let isOptional = true internal let isIndexed: Bool internal let isTransient: Bool From aca1709e13d0acbf5fdd40396d3769aba2c0ffcf Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 29 Oct 2017 15:06:57 +0900 Subject: [PATCH 43/56] WIP: documentation --- CoreStore.xcodeproj/project.pbxproj | 58 +++- .../BaseTests/BaseTestDataTestCase.swift | 21 +- Sources/AnyCoreStoreKeyPath.swift | 21 +- Sources/CoreStore+Observing.swift | 62 ++++- Sources/CoreStoreManagedObject.swift | 21 +- Sources/DataStack+Observing.swift | 62 ++++- Sources/FetchChainBuilder.swift | 74 +++++ ...lauseBuilder.swift => From+Querying.swift} | 263 +++++++++--------- Sources/QueryChainBuilder.swift | 86 ++++++ Sources/SectionMonitorBuilder.swift | 81 ++++++ Sources/UnsafeDataTransaction+Observing.swift | 49 +++- Sources/WhereClauseType.swift | 21 +- 12 files changed, 664 insertions(+), 155 deletions(-) create mode 100644 Sources/FetchChainBuilder.swift rename Sources/{ChainedClauseBuilder.swift => From+Querying.swift} (78%) create mode 100644 Sources/QueryChainBuilder.swift create mode 100644 Sources/SectionMonitorBuilder.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 235f9d5..8308442 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -101,6 +101,18 @@ B51FE5AF1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51FE5AA1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift */; }; B5202CFA1C04688100DED140 /* NSFetchedResultsController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */; }; B5202CFD1C046E8400DED140 /* NSFetchedResultsController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */; }; + B5215CA41FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */; }; + B5215CA51FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */; }; + B5215CA61FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */; }; + B5215CA71FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */; }; + B5215CA91FA4810300139E3A /* QueryChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */; }; + B5215CAA1FA4810300139E3A /* QueryChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */; }; + B5215CAB1FA4810300139E3A /* QueryChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */; }; + B5215CAC1FA4810300139E3A /* QueryChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */; }; + B5215CAE1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */; }; + B5215CAF1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */; }; + B5215CB01FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */; }; + B5215CB11FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */; }; B5220E081D0C5F8D009BC71E /* ObjectObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */; }; B5220E091D0C5F8D009BC71E /* ObjectObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */; }; B5220E0C1D0D0D19009BC71E /* ImportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E0B1D0D0D19009BC71E /* ImportTests.swift */; }; @@ -324,10 +336,10 @@ B5519A601CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; B5519A611CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; B5519A621CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; - B55514EA1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; - B55514EB1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; - B55514EC1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; - B55514ED1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; + B55514EA1EED8BF900BAB888 /* From+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* From+Querying.swift */; }; + B55514EB1EED8BF900BAB888 /* From+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* From+Querying.swift */; }; + B55514EC1EED8BF900BAB888 /* From+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* From+Querying.swift */; }; + B55514ED1EED8BF900BAB888 /* From+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* From+Querying.swift */; }; B55717441D15B09E009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; B55717451D15B09F009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; B55717461D15B0A1009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -765,6 +777,9 @@ B51260921E9B28F100402229 /* EntityIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntityIdentifier.swift; sourceTree = ""; }; B51FE5AA1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+CustomDebugStringConvertible.swift"; sourceTree = ""; }; B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFetchedResultsController+Convenience.swift"; sourceTree = ""; }; + B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchChainBuilder.swift; sourceTree = ""; }; + B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryChainBuilder.swift; sourceTree = ""; }; + B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionMonitorBuilder.swift; sourceTree = ""; }; B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectObserverTests.swift; sourceTree = ""; }; B5220E0B1D0D0D19009BC71E /* ImportTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportTests.swift; sourceTree = ""; }; B5220E0F1D0DA6AB009BC71E /* ListObserverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListObserverTests.swift; sourceTree = ""; }; @@ -814,7 +829,7 @@ B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSAsynchronousDataTransaction.swift; sourceTree = ""; }; B5548CD51BD65AE00077652A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; B5548CD71BD65AE50077652A /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; - B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainedClauseBuilder.swift; sourceTree = ""; }; + B55514E91EED8BF900BAB888 /* From+Querying.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "From+Querying.swift"; sourceTree = ""; }; B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreStoreBridge.h; sourceTree = ""; }; B559CD421CAA8B6300E4D58B /* CSSetupResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSetupResult.swift; sourceTree = ""; }; B559CD481CAA8C6D00E4D58B /* CSStorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSStorageInterface.swift; sourceTree = ""; }; @@ -1149,6 +1164,17 @@ name = Observing; sourceTree = ""; }; + B5215CA21FA47BF300139E3A /* Chained Clauses */ = { + isa = PBXGroup; + children = ( + B55514E91EED8BF900BAB888 /* From+Querying.swift */, + B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */, + B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */, + B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */, + ); + name = "Chained Clauses"; + sourceTree = ""; + }; B52F74391E9B8724005F3DAC /* Dynamic Schema */ = { isa = PBXGroup; children = ( @@ -1431,7 +1457,7 @@ B596BBB51DD5BC67001DCDD9 /* FetchableSource.swift */, B596BBBA1DD5C39F001DCDD9 /* QueryableSource.swift */, B549F65D1E569C7400FBAB2D /* QueryableAttributeType.swift */, - B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */, + B5215CA21FA47BF300139E3A /* Chained Clauses */, B5A1DAC61F111BBE003CF369 /* KeyPath Utilities */, B5E84F0A1AFF847B0064E85B /* Protocol Clauses */, B5E84EFF1AFF847B0064E85B /* Concrete Clauses */, @@ -1855,7 +1881,7 @@ B5D339D81E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5D3F6451C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */, B56923FA1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EA1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, + B55514EA1EED8BF900BAB888 /* From+Querying.swift in Sources */, B596BBBB1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDBFF1CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC051CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -1945,6 +1971,7 @@ B5E84F0F1AFF847B0064E85B /* From.swift in Sources */, B5FAD6A91B50A4B400714891 /* Progress+Convenience.swift in Sources */, B5E84EFC1AFF846E0064E85B /* SynchronousDataTransaction.swift in Sources */, + B5215CA91FA4810300139E3A /* QueryChainBuilder.swift in Sources */, B5E222231CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, B5E84F281AFF84920064E85B /* NSManagedObject+Convenience.swift in Sources */, B52F744A1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, @@ -1953,6 +1980,7 @@ B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */, B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B275F1EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5215CA41FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A011E96012400C880DE /* Relationship.swift in Sources */, B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */, B56923C91EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, @@ -1981,6 +2009,7 @@ B5FE4DA71C84FB4400FA6A91 /* InMemoryStore.swift in Sources */, B52F743D1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, B56923FF1EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5215CAE1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B5ECDBEC1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B56923EC1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B5E834B91B76311F001D3D50 /* BaseDataTransaction+Importing.swift in Sources */, @@ -2048,7 +2077,7 @@ B5D339D91E9489AB00C880DE /* CoreStoreObject.swift in Sources */, 82BA18CE1C4BBD7100A0916E /* FetchedResultsControllerDelegate.swift in Sources */, B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EB1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, + B55514EB1EED8BF900BAB888 /* From+Querying.swift in Sources */, B596BBBC1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC071CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -2138,6 +2167,7 @@ 82BA18C71C4BBD5900A0916E /* CoreStore+Migration.swift in Sources */, B5E222251CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, 82BA18C41C4BBD5300A0916E /* ListMonitor.swift in Sources */, + B5215CAA1FA4810300139E3A /* QueryChainBuilder.swift in Sources */, 82BA18BA1C4BBD4A00A0916E /* Select.swift in Sources */, B52F744B1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, @@ -2146,6 +2176,7 @@ 82BA18D81C4BBD7100A0916E /* WeakObject.swift in Sources */, B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5215CA51FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A021E96012400C880DE /* Relationship.swift in Sources */, B559CD4B1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, B56923CA1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, @@ -2174,6 +2205,7 @@ B5ECDBEE1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, B56924001EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5215CAF1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, 82BA18D61C4BBD7100A0916E /* NSManagedObjectContext+Transaction.swift in Sources */, B56923ED1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, 82BA18B91C4BBD4A00A0916E /* From.swift in Sources */, @@ -2241,7 +2273,7 @@ B5D339DB1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */, B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514ED1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, + B55514ED1EED8BF900BAB888 /* From+Querying.swift in Sources */, B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B5ECDC0F1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, @@ -2331,6 +2363,7 @@ B52DD1CB1BE1F94600949AFE /* WeakObject.swift in Sources */, B52DD1C11BE1F94600949AFE /* Functions.swift in Sources */, B5220E1A1D130791009BC71E /* CoreStoreFetchedResultsController.swift in Sources */, + B5215CAC1FA4810300139E3A /* QueryChainBuilder.swift in Sources */, B53FBA0F1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */, B52F744D1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, B59FA0B21CCBACA8007C9BCA /* ICloudStore.swift in Sources */, @@ -2339,6 +2372,7 @@ B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5215CA71FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A041E96012400C880DE /* Relationship.swift in Sources */, B52DD1C61BE1F94600949AFE /* NSManagedObjectContext+CoreStore.swift in Sources */, B56923CC1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, @@ -2367,6 +2401,7 @@ B5220E201D130813009BC71E /* CSObjectMonitor.swift in Sources */, B52F74401E9B8724005F3DAC /* DynamicSchema.swift in Sources */, B56924021EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5215CB11FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B5220E171D1306DF009BC71E /* UnsafeDataTransaction+Observing.swift in Sources */, B56923EF1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B53FBA081CAB300C00F0D40A /* CSMigrationType.swift in Sources */, @@ -2434,7 +2469,7 @@ B5D339DA1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */, B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EC1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, + B55514EC1EED8BF900BAB888 /* From+Querying.swift in Sources */, B596BBBD1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */, B5E1B59B1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, @@ -2524,6 +2559,7 @@ B563218E1BD65216006C9394 /* SaveResult.swift in Sources */, B5E222261CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, B56321A21BD65216006C9394 /* ListObserver.swift in Sources */, + B5215CAB1FA4810300139E3A /* QueryChainBuilder.swift in Sources */, B563218A1BD65216006C9394 /* SynchronousDataTransaction.swift in Sources */, B52F744C1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, B5AEFAB71C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, @@ -2532,6 +2568,7 @@ B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */, B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B27611EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5215CA61FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A031E96012400C880DE /* Relationship.swift in Sources */, B559CD4C1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, B56923CB1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, @@ -2560,6 +2597,7 @@ B5ECDBEF1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743F1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, B56924011EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5215CB01FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B56321B41BD6521C006C9394 /* NSManagedObjectContext+Transaction.swift in Sources */, B56923EE1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B56321861BD65216006C9394 /* CoreStoreLogger.swift in Sources */, diff --git a/CoreStoreTests/BaseTests/BaseTestDataTestCase.swift b/CoreStoreTests/BaseTests/BaseTestDataTestCase.swift index 4b21375..36a7a01 100644 --- a/CoreStoreTests/BaseTests/BaseTestDataTestCase.swift +++ b/CoreStoreTests/BaseTests/BaseTestDataTestCase.swift @@ -2,8 +2,25 @@ // BaseTestDataTestCase.swift // CoreStore // -// Created by John Rommel Estropia on 2016/06/11. -// Copyright © 2016 John Rommel Estropia. All rights reserved. +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // import Foundation diff --git a/Sources/AnyCoreStoreKeyPath.swift b/Sources/AnyCoreStoreKeyPath.swift index 36b4add..eb86752 100644 --- a/Sources/AnyCoreStoreKeyPath.swift +++ b/Sources/AnyCoreStoreKeyPath.swift @@ -2,8 +2,25 @@ // AnyCoreStoreKeyPath.swift // CoreStore // -// Created by John Estropia on 2017/10/02. -// Copyright © 2017 John Rommel Estropia. All rights reserved. +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // import Foundation diff --git a/Sources/CoreStore+Observing.swift b/Sources/CoreStore+Observing.swift index 01f5993..3a5da14 100644 --- a/Sources/CoreStore+Observing.swift +++ b/Sources/CoreStore+Observing.swift @@ -67,7 +67,18 @@ public extension CoreStore { return self.defaultStack.monitorList(from, fetchClauses) } - // TODO: docs + /** + Creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let monitor = CoreStore.monitorList( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public static func monitorList(_ clauseChain: B) -> ListMonitor { return self.defaultStack.monitorList(clauseChain.from, clauseChain.fetchClauses) @@ -97,7 +108,23 @@ public extension CoreStore { self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. + + ``` + CoreStore.monitorList( + { (monitor) in + self.monitor = monitor + }, + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter createAsynchronously: the closure that receives the created `ListMonitor` instance + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public static func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.defaultStack.monitorList( @@ -133,7 +160,19 @@ public extension CoreStore { return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) } - // TODO: docs + /** + Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + let monitor = CoreStore.monitorSectionedList( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public static func monitorSectionedList(_ clauseChain: B) -> ListMonitor { return self.defaultStack.monitorSectionedList( @@ -169,7 +208,22 @@ public extension CoreStore { self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + CoreStore.monitorSectionedList( + { (monitor) in + self.monitor = monitor + }, + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.defaultStack.monitorSectionedList( diff --git a/Sources/CoreStoreManagedObject.swift b/Sources/CoreStoreManagedObject.swift index 4790660..6654a75 100644 --- a/Sources/CoreStoreManagedObject.swift +++ b/Sources/CoreStoreManagedObject.swift @@ -2,8 +2,25 @@ // CoreStoreManagedObject.swift // CoreStore // -// Created by John Rommel Estropia on 2017/06/04. -// Copyright © 2017 John Rommel Estropia. All rights reserved. +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // import CoreData diff --git a/Sources/DataStack+Observing.swift b/Sources/DataStack+Observing.swift index 611130d..9946fdd 100644 --- a/Sources/DataStack+Observing.swift +++ b/Sources/DataStack+Observing.swift @@ -88,7 +88,18 @@ public extension DataStack { ) } - // TODO: docs + /** + Creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let monitor = dataStack.monitorList( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func monitorList(_ clauseChain: B) -> ListMonitor { return self.monitorList(clauseChain.from, clauseChain.fetchClauses) @@ -136,7 +147,23 @@ public extension DataStack { ) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. + + ``` + dataStack.monitorList( + { (monitor) in + self.monitor = monitor + }, + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter createAsynchronously: the closure that receives the created `ListMonitor` instance + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.monitorList( @@ -190,7 +217,19 @@ public extension DataStack { ) } - // TODO: docs + /** + Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + let monitor = dataStack.monitorSectionedList( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public func monitorSectionedList(_ clauseChain: B) -> ListMonitor { return self.monitorSectionedList( @@ -245,7 +284,22 @@ public extension DataStack { ) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + dataStack.monitorSectionedList( + { (monitor) in + self.monitor = monitor + }, + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.monitorSectionedList( diff --git a/Sources/FetchChainBuilder.swift b/Sources/FetchChainBuilder.swift new file mode 100644 index 0000000..9ce7063 --- /dev/null +++ b/Sources/FetchChainBuilder.swift @@ -0,0 +1,74 @@ +// +// FetchChainBuilder.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - FetchChainBuilder + +/** + The fetch builder type used for fetches. A `FetchChainBuilder` is created from a `From` clause. + ``` + let people = source.fetchAll( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + */ +public struct FetchChainBuilder: FetchChainableBuilderType { + + // MARK: FetchChainableBuilderType + + public typealias ObjectType = D + + public var from: From + public var fetchClauses: [FetchClause] = [] +} + + +// MARK: - FetchChainableBuilderType + +/** + Utility protocol for `FetchChainBuilder`. Used in fetch methods that support chained fetch builders. + */ +public protocol FetchChainableBuilderType { + + /** + The `DynamicObject` type for the fetch + */ + associatedtype ObjectType: DynamicObject + + /** + The `From` clause specifies the source entity and source persistent store for the fetch + */ + var from: From { get set } + + /** + The `FetchClause`s to be used for the fetch + */ + var fetchClauses: [FetchClause] { get set } +} diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/From+Querying.swift similarity index 78% rename from Sources/ChainedClauseBuilder.swift rename to Sources/From+Querying.swift index a20974d..277f97b 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/From+Querying.swift @@ -1,5 +1,5 @@ // -// FetchCondition.swift +// From+Querying.swift // CoreStore // // Copyright © 2017 John Rommel Estropia @@ -27,85 +27,96 @@ import Foundation import CoreData -// MARK: - FetchChainableBuilderType - -public protocol FetchChainableBuilderType { - - associatedtype ObjectType: DynamicObject - - var from: From { get set } - var fetchClauses: [FetchClause] { get set } -} - - -// MARK: - QueryChainableBuilderType - -public protocol QueryChainableBuilderType { - - associatedtype ObjectType: DynamicObject - associatedtype ResultType: SelectResultType - - var from: From { get set } - var select: Select { get set } - var queryClauses: [QueryClause] { get set } -} - -@available(OSX 10.12, *) -public protocol SectionMonitorBuilderType { - - associatedtype ObjectType: DynamicObject - - var from: From { get set } - var sectionBy: SectionBy { get set } - var fetchClauses: [FetchClause] { get set } -} - - -// MARK: - FetchChainBuilder - -public struct FetchChainBuilder: FetchChainableBuilderType { - - // MARK: FetchChainableBuilderType - - public typealias ObjectType = D - - public var from: From - public var fetchClauses: [FetchClause] = [] -} - - -// MARK: - QueryChainBuilder - -public struct QueryChainBuilder: QueryChainableBuilderType { - - // MARK: QueryChainableBuilderType - - public typealias ObjectType = D - public typealias ResultType = R - - public var from: From - public var select: Select - public var queryClauses: [QueryClause] = [] -} - - -// MARK: - SectionMonitorChainBuilder - -@available(OSX 10.12, *) -public struct SectionMonitorChainBuilder: SectionMonitorBuilderType { - - // MARK: SectionMonitorBuilderType - - public var from: From - public var sectionBy: SectionBy - public var fetchClauses: [FetchClause] = [] -} - - // MARK: - From public extension From { + /** + Creates a `FetchChainBuilder` that starts with the specified `Where` clause + + - parameter clause: the `Where` clause to create a `FetchChainBuilder` with + - returns: a `FetchChainBuilder` that starts with the specified `Where` clause + */ + public func `where`(_ clause: Where) -> FetchChainBuilder { + + return self.fetchChain(appending: clause) + } + + /** + Creates a `FetchChainBuilder` with a predicate using the specified string format and arguments + + - parameter format: the format string for the predicate + - parameter args: the arguments for `format` + - returns: a `FetchChainBuilder` with a predicate using the specified string format and arguments + */ + public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { + + return self.fetchChain(appending: Where(format, argumentArray: args)) + } + + /** + Creates a `FetchChainBuilder` with a predicate using the specified string format and arguments + + - parameter format: the format string for the predicate + - parameter argumentArray: the arguments for `format` + - returns: a `FetchChainBuilder` with a predicate using the specified string format and arguments + */ + public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { + + return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) + } + + /** + Creates a `FetchChainBuilder` with a series of `SortKey`s + + - parameter sortKey: a single `SortKey` + - parameter sortKeys: a series of other `SortKey`s + - returns: a `FetchChainBuilder` with a series of `SortKey`s + */ + public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { + + return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) + } + + /** + Creates a `FetchChainBuilder` with a closure where the `NSFetchRequest` may be configured + + - parameter fetchRequest: the block to customize the `NSFetchRequest` + - returns: a `FetchChainBuilder` with closure where the `NSFetchRequest` may be configured + */ + public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { + + return self.fetchChain(appending: Tweak(fetchRequest)) + } + + /** + Creates a `FetchChainBuilder` and immediately appending a `FetchClause` + + - parameter clause: the `FetchClause` to add to the `FetchChainBuilder` + - returns: a `FetchChainBuilder` containing the specified `FetchClause` + */ + public func appending(_ clause: FetchClause) -> FetchChainBuilder { + + return self.fetchChain(appending: clause) + } + + /** + Creates a `FetchChainBuilder` and immediately appending a series of `FetchClause`s + + - parameter clauses: the `FetchClause`s to add to the `FetchChainBuilder` + - returns: a `FetchChainBuilder` containing the specified `FetchClause`s + */ + public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { + + return self.fetchChain(appending: clauses) + } + + /** + Creates a `QueryChainBuilder` that starts with the specified `Select` clause + + - parameter clause: the `Select` clause to create a `QueryChainBuilder` with + - returns: a `QueryChainBuilder` that starts with the specified `Select` clause + */ public func select(_ clause: Select) -> QueryChainBuilder { return .init( @@ -115,11 +126,26 @@ public extension From { ) } + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s + + - parameter resultType: the generic `SelectResultType` for the `Select` clause + - parameter selectTerm: a `SelectTerm` + - parameter selectTerms: a series of `SelectTerm`s + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s + */ public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { return self.select(resultType, [selectTerm] + selectTerms) } + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s + + - parameter resultType: the generic `SelectResultType` for the `Select` clause + - parameter selectTerms: a series of `SelectTerm`s + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s + */ public func select(_ resultType: R.Type, _ selectTerms: [SelectTerm]) -> QueryChainBuilder { return .init( @@ -129,6 +155,12 @@ public extension From { ) } + /** + Creates a `SectionMonitorChainBuilder` that starts with the `SectionBy` to use to group `ListMonitor` objects into sections + + - parameter clause: the `SectionBy` to be used by the `ListMonitor` + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ clause: SectionBy) -> SectionMonitorChainBuilder { @@ -139,12 +171,26 @@ public extension From { ) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the key path to use to group the objects into sections + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString) -> SectionMonitorChainBuilder { return self.sectionBy(sectionKeyPath, { $0 }) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the key path to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { @@ -155,41 +201,6 @@ public extension From { ) } - public func `where`(_ clause: Where) -> FetchChainBuilder { - - return self.fetchChain(appending: clause) - } - - public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { - - return self.fetchChain(appending: Where(format, argumentArray: args)) - } - - public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { - - return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) - } - - public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { - - return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) - } - - public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { - - return self.fetchChain(appending: Tweak(fetchRequest)) - } - - public func appending(_ clause: FetchClause) -> FetchChainBuilder { - - return self.fetchChain(appending: clause) - } - - public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { - - return self.fetchChain(appending: clauses) - } - // MARK: Private @@ -367,21 +378,6 @@ public extension FetchChainBuilder where D: CoreStoreObject { public extension QueryChainBuilder { - public func groupBy(_ clause: GroupBy) -> QueryChainBuilder { - - return self.queryChain(appending: clause) - } - - public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder { - - return self.groupBy(GroupBy([keyPath] + keyPaths)) - } - - public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder { - - return self.queryChain(appending: GroupBy(keyPaths)) - } - public func `where`(_ clause: Where) -> QueryChainBuilder { return self.queryChain(appending: clause) @@ -407,6 +403,21 @@ public extension QueryChainBuilder { return self.queryChain(appending: Tweak(fetchRequest)) } + public func groupBy(_ clause: GroupBy) -> QueryChainBuilder { + + return self.queryChain(appending: clause) + } + + public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder { + + return self.groupBy(GroupBy([keyPath] + keyPaths)) + } + + public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder { + + return self.queryChain(appending: GroupBy(keyPaths)) + } + public func appending(_ clause: QueryClause) -> QueryChainBuilder { return self.queryChain(appending: clause) diff --git a/Sources/QueryChainBuilder.swift b/Sources/QueryChainBuilder.swift new file mode 100644 index 0000000..00522c4 --- /dev/null +++ b/Sources/QueryChainBuilder.swift @@ -0,0 +1,86 @@ +// +// QueryChainBuilder.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - QueryChainBuilder + +/** + The fetch builder type used for a queries. A `QueryChainBuilder` is created from a `From` clause and then a `select(...)` chain. + ``` + let averageAdultAge = dataStack.queryValue( + From() + .select(Int.self, .average(\.age)) + .where(\.age > 18) + ) + ``` + */ +public struct QueryChainBuilder: QueryChainableBuilderType { + + // MARK: QueryChainableBuilderType + + public typealias ObjectType = D + public typealias ResultType = R + + public var from: From + public var select: Select + public var queryClauses: [QueryClause] = [] +} + + +// MARK: - QueryChainableBuilderType + +/** + Utility protocol for `QueryChainBuilder`. Used in fetch methods that support chained query builders. + */ +public protocol QueryChainableBuilderType { + + /** + The `DynamicObject` type for the query + */ + associatedtype ObjectType: DynamicObject + + /** + The `SelectResultType` type for the query + */ + associatedtype ResultType: SelectResultType + + /** + The `From` clause specifies the source entity and source persistent store for the query + */ + var from: From { get set } + + /** + The `Select` clause to be used for the query + */ + var select: Select { get set } + + /** + The `QueryClause`s to be used for the query + */ + var queryClauses: [QueryClause] { get set } +} diff --git a/Sources/SectionMonitorBuilder.swift b/Sources/SectionMonitorBuilder.swift new file mode 100644 index 0000000..9530512 --- /dev/null +++ b/Sources/SectionMonitorBuilder.swift @@ -0,0 +1,81 @@ +// +// SectionMonitorBuilder.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - SectionMonitorChainBuilder + +/** + The fetch builder type used for a sectioned `ListMonitor`. A `SectionMonitorChainBuilder` is created from a `From` clause and then a `sectionBy(...)` chain. + ``` + let monitor = transaction.monitorSectionedList( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + */ +@available(OSX 10.12, *) +public struct SectionMonitorChainBuilder: SectionMonitorBuilderType { + + // MARK: SectionMonitorBuilderType + + public var from: From + public var sectionBy: SectionBy + public var fetchClauses: [FetchClause] = [] +} + + +// MARK: - SectionMonitorBuilderType + +/** + Utility protocol for `SectionMonitorChainBuilder`. Used in methods that support chained fetch builders. + */ +@available(OSX 10.12, *) +public protocol SectionMonitorBuilderType { + + /** + The `DynamicObject` type for the `ListMonitor` + */ + associatedtype ObjectType: DynamicObject + + /** + The `From` clause specifies the source entity and source persistent store for the `ListMonitor` + */ + var from: From { get set } + + /** + The `SectionBy` clause to be used for the `ListMonitor` + */ + var sectionBy: SectionBy { get set } + + /** + The `FetchClause`s to be used for the `ListMonitor` + */ + var fetchClauses: [FetchClause] { get set } +} diff --git a/Sources/UnsafeDataTransaction+Observing.swift b/Sources/UnsafeDataTransaction+Observing.swift index 7ad6a67..f8a2449 100644 --- a/Sources/UnsafeDataTransaction+Observing.swift +++ b/Sources/UnsafeDataTransaction+Observing.swift @@ -83,7 +83,23 @@ public extension UnsafeDataTransaction { ) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. + + ``` + transaction.monitorList( + { (monitor) in + self.monitor = monitor + }, + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter createAsynchronously: the closure that receives the created `ListMonitor` instance + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func monitorList(_ clauseChain: B) -> ListMonitor { return self.monitorList(clauseChain.from, clauseChain.fetchClauses) @@ -176,7 +192,19 @@ public extension UnsafeDataTransaction { ) } - // TODO: docs + /** + Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + let monitor = transaction.monitorSectionedList( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public func monitorSectionedList(_ clauseChain: B) -> ListMonitor { return self.monitorSectionedList( @@ -226,7 +254,22 @@ public extension UnsafeDataTransaction { ) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + transaction.monitorSectionedList( + { (monitor) in + self.monitor = monitor + }, + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.monitorSectionedList( diff --git a/Sources/WhereClauseType.swift b/Sources/WhereClauseType.swift index 38ece53..0df78c7 100644 --- a/Sources/WhereClauseType.swift +++ b/Sources/WhereClauseType.swift @@ -2,8 +2,25 @@ // WhereClauseType.swift // CoreStore // -// Created by John Estropia on 2017/09/29. -// Copyright © 2017 John Rommel Estropia. All rights reserved. +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // import Foundation From 02d7870d759afc6bb356dab0bcddb446957fd6d7 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 29 Oct 2017 15:20:58 +0900 Subject: [PATCH 44/56] fix compile errors for Xcode 9.1 beta --- Sources/CSListObserver.swift | 2 +- Sources/CSObjectObserver.swift | 2 +- Sources/CoreDataNativeType.swift | 2 +- Sources/CoreStoreBridge.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/CSListObserver.swift b/Sources/CSListObserver.swift index ff57b34..e9dfafd 100644 --- a/Sources/CSListObserver.swift +++ b/Sources/CSListObserver.swift @@ -42,7 +42,7 @@ import CoreData */ @available(OSX 10.12, *) @objc -public protocol CSListObserver: class, AnyObject { +public protocol CSListObserver: class { /** Handles processing just before a change to the observed list occurs diff --git a/Sources/CSObjectObserver.swift b/Sources/CSObjectObserver.swift index 32fd3f9..a5392eb 100644 --- a/Sources/CSObjectObserver.swift +++ b/Sources/CSObjectObserver.swift @@ -40,7 +40,7 @@ import CoreData */ @available(OSX 10.12, *) @objc -public protocol CSObjectObserver: class, AnyObject { +public protocol CSObjectObserver: class { /** Handles processing just before a change to the observed `object` occurs diff --git a/Sources/CoreDataNativeType.swift b/Sources/CoreDataNativeType.swift index c382a6a..a358fd5 100644 --- a/Sources/CoreDataNativeType.swift +++ b/Sources/CoreDataNativeType.swift @@ -33,7 +33,7 @@ import CoreData Objective-C Foundation types that are natively supported by Core Data managed attributes all conform to `CoreDataNativeType`. */ @objc -public protocol CoreDataNativeType: class, NSObjectProtocol, AnyObject {} +public protocol CoreDataNativeType: class, NSObjectProtocol {} // MARK: - NSNumber diff --git a/Sources/CoreStoreBridge.swift b/Sources/CoreStoreBridge.swift index 7d77680..33ee605 100644 --- a/Sources/CoreStoreBridge.swift +++ b/Sources/CoreStoreBridge.swift @@ -31,7 +31,7 @@ import Foundation /** `CoreStoreObjectiveCType`s are Objective-C accessible classes that represent CoreStore's Swift types. */ -public protocol CoreStoreObjectiveCType: class, AnyObject { +public protocol CoreStoreObjectiveCType: class { /** The corresponding Swift type From 0430f662409767ba90483efebb28462633b065ac Mon Sep 17 00:00:00 2001 From: John Estropia Date: Wed, 1 Nov 2017 11:33:41 +0900 Subject: [PATCH 45/56] force dynamic typing on DynamicObject.Type to mitigate optimization issues --- Sources/DynamicObject.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/DynamicObject.swift b/Sources/DynamicObject.swift index cf148af..b6af806 100644 --- a/Sources/DynamicObject.swift +++ b/Sources/DynamicObject.swift @@ -121,7 +121,7 @@ extension CoreStoreObject { return unsafeDowncast(coreStoreObject, to: self) } - func forceTypeCast(_ type: DynamicObject.Type, to: T.Type) -> T.Type { + func forceTypeCast(_ type: AnyClass, to: T.Type) -> T.Type { return type as! T.Type } From 8ce26c213df9be26410afc9fc24bffc0c41ee405 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sat, 4 Nov 2017 10:37:54 +0900 Subject: [PATCH 46/56] more source docs, deprecated some Where clause utilities --- CoreStoreTests/DynamicModelTests.swift | 14 +- Sources/CoreStoreObject+Querying.swift | 159 ++++++------- Sources/From+Querying.swift | 302 +++++++++++++++++++++++++ Sources/GroupBy.swift | 25 ++ Sources/KeyPath+Querying.swift | 270 ++++++++++++++++++++++ Sources/Where.swift | 10 + 6 files changed, 684 insertions(+), 96 deletions(-) diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index 4d68ddb..197cd7c 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -208,14 +208,14 @@ class DynamicModelTests: BaseTestDataTestCase { stack.perform( asynchronous: { (transaction) in - let p1 = Animal.where({ $0.species == "Sparrow" }) + let p1 = Where({ $0.species == "Sparrow" }) XCTAssertEqual(p1.predicate, NSPredicate(format: "%K == %@", "species", "Sparrow")) let bird = transaction.fetchOne(From(), p1) XCTAssertNotNil(bird) XCTAssertEqual(bird!.species.value, "Sparrow") - let p2 = Dog.where({ $0.nickname == "Spot" }) + let p2 = Where({ $0.nickname == "Spot" }) XCTAssertEqual(p2.predicate, NSPredicate(format: "%K == %@", "nickname", "Spot")) let dog = transaction.fetchOne(From().where(\.nickname == "Spot")) @@ -227,7 +227,7 @@ class DynamicModelTests: BaseTestDataTestCase { XCTAssertNotNil(person) XCTAssertEqual(person!.pets.value.first, dog) - let p3 = Dog.where({ $0.age == 10 }) + let p3 = Where({ $0.age == 10 }) XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10)) _ = transaction.fetchAll( @@ -241,19 +241,19 @@ class DynamicModelTests: BaseTestDataTestCase { ) _ = transaction.fetchAll( From(), - Dog.where({ $0.age > 10 && $0.age <= 15 }) + Where({ $0.age > 10 && $0.age <= 15 }) ) _ = transaction.fetchAll( From(), - Dog.where({ $0.species == "Dog" && $0.age == 10 }) + Where({ $0.species == "Dog" && $0.age == 10 }) ) _ = transaction.fetchAll( From(), - Dog.where({ $0.age == 10 && $0.species == "Dog" }) + Where({ $0.age == 10 && $0.species == "Dog" }) ) _ = transaction.fetchAll( From(), - Dog.where({ $0.age > 10 && $0.age <= 15 }) + Where({ $0.age > 10 && $0.age <= 15 }) ) _ = transaction.fetchAll( From(), diff --git a/Sources/CoreStoreObject+Querying.swift b/Sources/CoreStoreObject+Querying.swift index 1d5800f..1f28aee 100644 --- a/Sources/CoreStoreObject+Querying.swift +++ b/Sources/CoreStoreObject+Querying.swift @@ -85,61 +85,6 @@ public extension DynamicObject where Self: CoreStoreObject { return relationship(self.meta).keyPath } - - /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. - ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.nickname == "John" }) - ``` - */ - public static func `where`(_ condition: (Self) -> Where) -> Where { - - return condition(self.meta) - } - - /** - Creates an `OrderBy` clause from a `CoreStoreObject.Value` property. - ``` - let person = CoreStore.fetchAll(From(), Person.orderBy(ascending: { $0.age })) - ``` - */ - public static func orderBy(ascending attribute: (Self) -> ValueContainer.Required) -> OrderBy { - - return OrderBy(.ascending(attribute(self.meta).keyPath)) - } - - /** - Creates an `OrderBy` clause from a `CoreStoreObject.Value` property. - ``` - let person = CoreStore.fetchAll(From(), Person.orderBy(ascending: { $0.age })) - ``` - */ - public static func orderBy(ascending attribute: (Self) -> ValueContainer.Optional) -> OrderBy { - - return OrderBy(.ascending(attribute(self.meta).keyPath)) - } - - /** - Creates an `OrderBy` clause from a `CoreStoreObject.Value` property. - ``` - let person = CoreStore.fetchAll(From(), Person.orderBy(descending: { $0.age })) - ``` - */ - public static func orderBy(descending attribute: (Self) -> ValueContainer.Required) -> OrderBy { - - return OrderBy(.descending(attribute(self.meta).keyPath)) - } - - /** - Creates an `OrderBy` clause from a `CoreStoreObject.Value` property. - ``` - let person = CoreStore.fetchAll(From(), Person.orderBy(descending: { $0.age })) - ``` - */ - public static func orderBy(descending attribute: (Self) -> ValueContainer.Optional) -> OrderBy { - - return OrderBy(.descending(attribute(self.meta).keyPath)) - } } @@ -148,9 +93,9 @@ public extension DynamicObject where Self: CoreStoreObject { public extension ValueContainer.Required { /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is equal to a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.nickname == "John" }) + let person = CoreStore.fetchOne(From().where({ $0.nickname == "John" })) ``` */ @inline(__always) @@ -160,9 +105,9 @@ public extension ValueContainer.Required { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is not equal to a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.nickname != "John" }) + let person = CoreStore.fetchOne(From().where({ $0.nickname != "John" })) ``` */ @inline(__always) @@ -172,9 +117,9 @@ public extension ValueContainer.Required { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is less than a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.age < 20 }) + let person = CoreStore.fetchOne(From().where({ $0.age < 20 })) ``` */ @inline(__always) @@ -184,9 +129,9 @@ public extension ValueContainer.Required { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is greater than a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.age > 20 }) + let person = CoreStore.fetchOne(From().where({ $0.age > 20 })) ``` */ @inline(__always) @@ -196,9 +141,9 @@ public extension ValueContainer.Required { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is less than or equal to a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.age <= 20 }) + let person = CoreStore.fetchOne(From().where({ $0.age <= 20 })) ``` */ @inline(__always) @@ -208,9 +153,9 @@ public extension ValueContainer.Required { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is greater than or equal to a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.age >= 20 }) + let person = CoreStore.fetchOne(From().where({ $0.age >= 20 })) ``` */ @inline(__always) @@ -220,9 +165,9 @@ public extension ValueContainer.Required { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by checking if a sequence contains the value of a property ``` - let dog = CoreStore.fetchOne(From(), Dog.where { ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname }) + let dog = CoreStore.fetchOne(From().where({ ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname })) ``` */ @inline(__always) @@ -238,9 +183,9 @@ public extension ValueContainer.Required { public extension ValueContainer.Optional { /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is equal to a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.nickname == "John" }) + let person = CoreStore.fetchOne(From().where({ $0.nickname == "John" })) ``` */ @inline(__always) @@ -250,9 +195,9 @@ public extension ValueContainer.Optional { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is not equal to a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.nickname != "John" }) + let person = CoreStore.fetchOne(From().where({ $0.nickname != "John" })) ``` */ @inline(__always) @@ -262,9 +207,9 @@ public extension ValueContainer.Optional { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is less than a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.age < 20 }) + let person = CoreStore.fetchOne(From().where({ $0.age < 20 })) ``` */ @inline(__always) @@ -281,9 +226,9 @@ public extension ValueContainer.Optional { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is greater than a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.age > 20 }) + let person = CoreStore.fetchOne(From().where({ $0.age > 20 })) ``` */ @inline(__always) @@ -300,9 +245,9 @@ public extension ValueContainer.Optional { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is less than or equal to a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.age <= 20 }) + let person = CoreStore.fetchOne(From().where({ $0.age <= 20 })) ``` */ @inline(__always) @@ -319,9 +264,9 @@ public extension ValueContainer.Optional { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by comparing if a property is greater than or equal to a value ``` - let person = CoreStore.fetchOne(From(), Person.where { $0.age >= 20 }) + let person = CoreStore.fetchOne(From().where({ $0.age >= 20 })) ``` */ @inline(__always) @@ -338,9 +283,9 @@ public extension ValueContainer.Optional { } /** - Creates a `Where` clause from a `CoreStoreObject.Value` property. + Creates a `Where` clause by checking if a sequence contains the value of a property ``` - let dog = CoreStore.fetchOne(From(), Dog.where { ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname }) + let dog = CoreStore.fetchOne(From().where({ ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname })) ``` */ @inline(__always) @@ -356,9 +301,9 @@ public extension ValueContainer.Optional { public extension RelationshipContainer.ToOne { /** - Creates a `Where` clause from a `CoreStoreObject.Relationship` property. + Creates a `Where` clause by comparing if a property is equal to a value ``` - let dog = CoreStore.fetchOne(From(), Dog.where { $0.master == me }) + let dog = CoreStore.fetchOne(From().where({ $0.master == me })) ``` */ @inline(__always) @@ -368,9 +313,9 @@ public extension RelationshipContainer.ToOne { } /** - Creates a `Where` clause from a `CoreStoreObject.Relationship` property. + Creates a `Where` clause by comparing if a property is not equal to a value ``` - let dog = CoreStore.fetchOne(From(), Dog.where { $0.master != me }) + let dog = CoreStore.fetchOne(From().where({ $0.master != me })) ``` */ @inline(__always) @@ -380,9 +325,9 @@ public extension RelationshipContainer.ToOne { } /** - Creates a `Where` clause from a `CoreStoreObject.Relationship` property. + Creates a `Where` clause by checking if a sequence contains the value of a property ``` - let dog = CoreStore.fetchOne(From(), Dog.where { [john, joe, bob] ~= $0.master }) + let dog = CoreStore.fetchOne(From().where({ [john, joe, bob] ~= $0.master })) ``` */ @inline(__always) @@ -391,3 +336,39 @@ public extension RelationshipContainer.ToOne { return Where(relationship.keyPath, isMemberOf: sequence) } } + + +// MARK: Deprecated + +extension DynamicObject where Self: CoreStoreObject { + + @available(*, deprecated, message: "Use the Where(_:) initializer that accepts the same closure argument") + public static func `where`(_ condition: (Self) -> Where) -> Where { + + return condition(self.meta) + } + + @available(*, deprecated, message: "Use the new OrderBy(ascending:) overload that accepts the same closure argument") + public static func orderBy(ascending attribute: (Self) -> ValueContainer.Required) -> OrderBy { + + return OrderBy(.ascending(attribute(self.meta).keyPath)) + } + + @available(*, deprecated, message: "Use the new OrderBy(ascending:) overload that accepts the same closure argument") + public static func orderBy(ascending attribute: (Self) -> ValueContainer.Optional) -> OrderBy { + + return OrderBy(.ascending(attribute(self.meta).keyPath)) + } + + @available(*, deprecated, message: "Use the new OrderBy(descending:) overload that accepts the same closure argument") + public static func orderBy(descending attribute: (Self) -> ValueContainer.Required) -> OrderBy { + + return OrderBy(.descending(attribute(self.meta).keyPath)) + } + + @available(*, deprecated, message: "Use the new OrderBy(descending:) overload that accepts the same closure argument") + public static func orderBy(descending attribute: (Self) -> ValueContainer.Optional) -> OrderBy { + + return OrderBy(.descending(attribute(self.meta).keyPath)) + } +} diff --git a/Sources/From+Querying.swift b/Sources/From+Querying.swift index 277f97b..91648bb 100644 --- a/Sources/From+Querying.swift +++ b/Sources/From+Querying.swift @@ -217,17 +217,37 @@ public extension From { public extension From where D: NSManagedObject { + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + + - parameter keyPath: the keyPath to query the value for + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + */ public func select(_ keyPath: KeyPath) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath) -> SectionMonitorChainBuilder { return self.sectionBy(sectionKeyPath._kvcKeyPathString!, { $0 }) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { @@ -237,73 +257,159 @@ public extension From where D: NSManagedObject { public extension From where D: CoreStoreObject { + /** + Creates a `FetchChainBuilder` that starts with the specified `Where` clause + + - parameter clause: a closure that returns a `Where` clause + - returns: a `FetchChainBuilder` that starts with the specified `Where` clause + */ public func `where`(_ clause: (D) -> T) -> FetchChainBuilder { return self.fetchChain(appending: clause(D.meta)) } + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + + - parameter keyPath: the keyPath to query the value for + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + */ public func select(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + + - parameter keyPath: the keyPath to query the value for + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + */ public func select(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + + - parameter keyPath: the keyPath to query the value for + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + */ public func select(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + + - parameter keyPath: the keyPath to query the value for + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path + */ public func select(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Required>) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Optional>) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Required>) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Optional>) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Required>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Optional>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Required>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the `KeyPath` to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Optional>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { @@ -313,36 +419,81 @@ public extension From where D: CoreStoreObject { public extension FetchChainBuilder { + /** + Adds a `Where` clause to the `FetchChainBuilder` + + - parameter clause: a `Where` clause to add to the fetch builder + - returns: a new `FetchChainBuilder` containing the `Where` clause + */ public func `where`(_ clause: Where) -> FetchChainBuilder { return self.fetchChain(appending: clause) } + /** + Adds a `Where` clause to the `FetchChainBuilder` + + - parameter format: the format string for the predicate + - parameter args: the arguments for `format` + - returns: a new `FetchChainBuilder` containing the `Where` clause + */ public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { return self.fetchChain(appending: Where(format, argumentArray: args)) } + /** + Adds a `Where` clause to the `FetchChainBuilder` + + - parameter format: the format string for the predicate + - parameter argumentArray: the arguments for `format` + - returns: a new `FetchChainBuilder` containing the `Where` clause + */ public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) } + /** + Adds an `OrderBy` clause to the `FetchChainBuilder` + + - parameter sortKey: a single `SortKey` + - parameter sortKeys: a series of other `SortKey`s + - returns: a new `FetchChainBuilder` containing the `OrderBy` clause + */ public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) } + /** + Adds a `Tweak` clause to the `FetchChainBuilder` with a closure where the `NSFetchRequest` may be configured + + - parameter fetchRequest: the block to customize the `NSFetchRequest` + - returns: a new `FetchChainBuilder` containing the `Tweak` clause + */ public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { return self.fetchChain(appending: Tweak(fetchRequest)) } + /** + Appends a `FetchClause` to the `FetchChainBuilder` + + - parameter clause: the `FetchClause` to add to the `FetchChainBuilder` + - returns: a new `FetchChainBuilder` containing the `FetchClause` + */ public func appending(_ clause: FetchClause) -> FetchChainBuilder { return self.fetchChain(appending: clause) } + /** + Appends a series of `FetchClause`s to the `FetchChainBuilder` + + - parameter clauses: the `FetchClause`s to add to the `FetchChainBuilder` + - returns: a new `FetchChainBuilder` containing the `FetchClause`s + */ public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return self.fetchChain(appending: clauses) @@ -378,51 +529,115 @@ public extension FetchChainBuilder where D: CoreStoreObject { public extension QueryChainBuilder { + /** + Adds a `Where` clause to the `QueryChainBuilder` + + - parameter clause: a `Where` clause to add to the query builder + - returns: a new `QueryChainBuilder` containing the `Where` clause + */ public func `where`(_ clause: Where) -> QueryChainBuilder { return self.queryChain(appending: clause) } + /** + Adds a `Where` clause to the `QueryChainBuilder` + + - parameter format: the format string for the predicate + - parameter args: the arguments for `format` + - returns: a new `QueryChainBuilder` containing the `Where` clause + */ public func `where`(format: String, _ args: Any...) -> QueryChainBuilder { return self.queryChain(appending: Where(format, argumentArray: args)) } + /** + Adds a `Where` clause to the `QueryChainBuilder` + + - parameter format: the format string for the predicate + - parameter argumentArray: the arguments for `format` + - returns: a new `QueryChainBuilder` containing the `Where` clause + */ public func `where`(format: String, argumentArray: [Any]?) -> QueryChainBuilder { return self.queryChain(appending: Where(format, argumentArray: argumentArray)) } + /** + Adds an `OrderBy` clause to the `QueryChainBuilder` + + - parameter sortKey: a single `SortKey` + - parameter sortKeys: a series of other `SortKey`s + - returns: a new `QueryChainBuilder` containing the `OrderBy` clause + */ public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> QueryChainBuilder { return self.queryChain(appending: OrderBy([sortKey] + sortKeys)) } + /** + Adds a `Tweak` clause to the `QueryChainBuilder` with a closure where the `NSFetchRequest` may be configured + + - parameter fetchRequest: the block to customize the `NSFetchRequest` + - returns: a new `QueryChainBuilder` containing the `Tweak` clause + */ public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> QueryChainBuilder { return self.queryChain(appending: Tweak(fetchRequest)) } + /** + Adds a `GroupBy` clause to the `QueryChainBuilder` + + - parameter clause: a `GroupBy` clause to add to the query builder + - returns: a new `QueryChainBuilder` containing the `GroupBy` clause + */ public func groupBy(_ clause: GroupBy) -> QueryChainBuilder { return self.queryChain(appending: clause) } + /** + Adds a `GroupBy` clause to the `QueryChainBuilder` + + - parameter keyPath: a key path to group the query results with + - parameter keyPaths: other key paths to group the query results with + - returns: a new `QueryChainBuilder` containing the `GroupBy` clause + */ public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder { return self.groupBy(GroupBy([keyPath] + keyPaths)) } + /** + Adds a `GroupBy` clause to the `QueryChainBuilder` + + - parameter keyPaths: a series of key paths to group the query results with + - returns: a new `QueryChainBuilder` containing the `GroupBy` clause + */ public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder { return self.queryChain(appending: GroupBy(keyPaths)) } + /** + Appends a `QueryClause` to the `QueryChainBuilder` + + - parameter clause: the `QueryClause` to add to the `QueryChainBuilder` + - returns: a new `QueryChainBuilder` containing the `QueryClause` + */ public func appending(_ clause: QueryClause) -> QueryChainBuilder { return self.queryChain(appending: clause) } + /** + Appends a series of `QueryClause`s to the `QueryChainBuilder` + + - parameter clauses: the `QueryClause`s to add to the `QueryChainBuilder` + - returns: a new `QueryChainBuilder` containing the `QueryClause`s + */ public func appending(contentsOf clauses: S) -> QueryChainBuilder where S.Element == QueryClause { return self.queryChain(appending: clauses) @@ -452,6 +667,12 @@ public extension QueryChainBuilder { public extension QueryChainBuilder where D: NSManagedObject { + /** + Adds a `GroupBy` clause to the `QueryChainBuilder` + + - parameter keyPath: a key path to group the query results with + - returns: a new `QueryChainBuilder` containing the `GroupBy` clause + */ public func groupBy(_ keyPath: KeyPath) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) @@ -460,26 +681,56 @@ public extension QueryChainBuilder where D: NSManagedObject { public extension QueryChainBuilder where D: CoreStoreObject { + /** + Adds a `Where` clause to the `QueryChainBuilder` + + - parameter clause: a `Where` clause to add to the query builder + - returns: a new `QueryChainBuilder` containing the `Where` clause + */ public func `where`(_ clause: (D) -> T) -> QueryChainBuilder { return self.queryChain(appending: clause(D.meta)) } + /** + Adds a `GroupBy` clause to the `QueryChainBuilder` + + - parameter keyPath: a key path to group the query results with + - returns: a new `QueryChainBuilder` containing the `GroupBy` clause + */ public func groupBy(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) } + /** + Adds a `GroupBy` clause to the `QueryChainBuilder` + + - parameter keyPath: a key path to group the query results with + - returns: a new `QueryChainBuilder` containing the `GroupBy` clause + */ public func groupBy(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) } + /** + Adds a `GroupBy` clause to the `QueryChainBuilder` + + - parameter keyPath: a key path to group the query results with + - returns: a new `QueryChainBuilder` containing the `GroupBy` clause + */ public func groupBy(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) } + /** + Adds a `GroupBy` clause to the `QueryChainBuilder` + + - parameter keyPath: a key path to group the query results with + - returns: a new `QueryChainBuilder` containing the `GroupBy` clause + */ public func groupBy(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) @@ -489,36 +740,81 @@ public extension QueryChainBuilder where D: CoreStoreObject { @available(OSX 10.12, *) public extension SectionMonitorChainBuilder { + /** + Adds a `Where` clause to the `SectionMonitorChainBuilder` + + - parameter clause: a `Where` clause to add to the fetch builder + - returns: a new `SectionMonitorChainBuilder` containing the `Where` clause + */ public func `where`(_ clause: Where) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause) } + /** + Adds a `Where` clause to the `SectionMonitorChainBuilder` + + - parameter format: the format string for the predicate + - parameter args: the arguments for `format` + - returns: a new `SectionMonitorChainBuilder` containing the `Where` clause + */ public func `where`(format: String, _ args: Any...) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: Where(format, argumentArray: args)) } + /** + Adds a `Where` clause to the `SectionMonitorChainBuilder` + + - parameter format: the format string for the predicate + - parameter argumentArray: the arguments for `format` + - returns: a new `SectionMonitorChainBuilder` containing the `Where` clause + */ public func `where`(format: String, argumentArray: [Any]?) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: Where(format, argumentArray: argumentArray)) } + /** + Adds an `OrderBy` clause to the `SectionMonitorChainBuilder` + + - parameter sortKey: a single `SortKey` + - parameter sortKeys: a series of other `SortKey`s + - returns: a new `SectionMonitorChainBuilder` containing the `OrderBy` clause + */ public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: OrderBy([sortKey] + sortKeys)) } + /** + Adds a `Tweak` clause to the `SectionMonitorChainBuilder` with a closure where the `NSFetchRequest` may be configured + + - parameter fetchRequest: the block to customize the `NSFetchRequest` + - returns: a new `SectionMonitorChainBuilder` containing the `Tweak` clause + */ public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: Tweak(fetchRequest)) } + /** + Appends a `QueryClause` to the `SectionMonitorChainBuilder` + + - parameter clause: the `QueryClause` to add to the `SectionMonitorChainBuilder` + - returns: a new `SectionMonitorChainBuilder` containing the `QueryClause` + */ public func appending(_ clause: FetchClause) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause) } + /** + Appends a series of `QueryClause`s to the `SectionMonitorChainBuilder` + + - parameter clauses: the `QueryClause`s to add to the `SectionMonitorChainBuilder` + - returns: a new `SectionMonitorChainBuilder` containing the `QueryClause`s + */ public func appending(contentsOf clauses: S) -> SectionMonitorChainBuilder where S.Element == FetchClause { return self.sectionMonitorChain(appending: clauses) @@ -549,6 +845,12 @@ public extension SectionMonitorChainBuilder { @available(OSX 10.12, *) public extension SectionMonitorChainBuilder where D: CoreStoreObject { + /** + Adds a `Where` clause to the `SectionMonitorChainBuilder` + + - parameter clause: a `Where` clause to add to the fetch builder + - returns: a new `SectionMonitorChainBuilder` containing the `Where` clause + */ public func `where`(_ clause: (D) -> T) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause(D.meta)) diff --git a/Sources/GroupBy.swift b/Sources/GroupBy.swift index 88fdb5d..8c9c929 100644 --- a/Sources/GroupBy.swift +++ b/Sources/GroupBy.swift @@ -105,6 +105,11 @@ public struct GroupBy: GroupByClause, QueryClause, Hashable { public extension GroupBy where D: NSManagedObject { + /** + Initializes a `GroupBy` clause with a key path + + - parameter keyPath: a key path to group results with + */ public init(_ keyPath: KeyPath) { self.init([keyPath._kvcKeyPathString!]) @@ -113,21 +118,41 @@ public extension GroupBy where D: NSManagedObject { public extension GroupBy where D: CoreStoreObject { + /** + Initializes a `GroupBy` clause with a key path + + - parameter keyPath: a key path to group results with + */ public init(_ keyPath: KeyPath.Required>) { self.init([D.meta[keyPath: keyPath].keyPath]) } + /** + Initializes a `GroupBy` clause with a key path + + - parameter keyPath: a key path to group results with + */ public init(_ keyPath: KeyPath.Optional>) { self.init([D.meta[keyPath: keyPath].keyPath]) } + /** + Initializes a `GroupBy` clause with a key path + + - parameter keyPath: a key path to group results with + */ public init(_ keyPath: KeyPath.Required>) { self.init([D.meta[keyPath: keyPath].keyPath]) } + /** + Initializes a `GroupBy` clause with a key path + + - parameter keyPath: a key path to group results with + */ public init(_ keyPath: KeyPath.Optional>) { self.init([D.meta[keyPath: keyPath].keyPath]) diff --git a/Sources/KeyPath+Querying.swift b/Sources/KeyPath+Querying.swift index 9893789..f9a120c 100644 --- a/Sources/KeyPath+Querying.swift +++ b/Sources/KeyPath+Querying.swift @@ -29,16 +29,34 @@ import Foundation // MARK: - KeyPath where Root: NSManagedObject, Value: QueryableAttributeType & Equatable +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname == "John")) + ``` + */ public func == (_ keyPath: KeyPath, _ value: V) -> Where { return Where(keyPath._kvcKeyPathString!, isEqualTo: value) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname != "John")) + ``` + */ public func != (_ keyPath: KeyPath, _ value: V) -> Where { return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) } +/** + Creates a `Where` clause by checking if a sequence contains the value of a property + ``` + let dog = CoreStore.fetchOne(From().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == V { return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) @@ -47,16 +65,34 @@ public func ~= +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname == "John")) + ``` + */ public func == (_ keyPath: KeyPath>, _ value: V?) -> Where { return Where(keyPath._kvcKeyPathString!, isEqualTo: value) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname != "John")) + ``` + */ public func != (_ keyPath: KeyPath>, _ value: V?) -> Where { return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) } +/** + Creates a `Where` clause by checking if a sequence contains the value of a property + ``` + let dog = CoreStore.fetchOne(From().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath>) -> Where where S.Iterator.Element == V { return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) @@ -65,21 +101,45 @@ public func ~= ().where(\.age < 20)) + ``` + */ public func < (_ keyPath: KeyPath, _ value: V) -> Where { return Where("%K < %@", keyPath._kvcKeyPathString!, value) } +/** + Creates a `Where` clause by comparing if a property is greater than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age > 20)) + ``` + */ public func > (_ keyPath: KeyPath, _ value: V) -> Where { return Where("%K > %@", keyPath._kvcKeyPathString!, value) } +/** + Creates a `Where` clause by comparing if a property is less than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age <= 20)) + ``` + */ public func <= (_ keyPath: KeyPath, _ value: V) -> Where { return Where("%K <= %@", keyPath._kvcKeyPathString!, value) } +/** + Creates a `Where` clause by comparing if a property is greater than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age >= 20)) + ``` + */ public func >= (_ keyPath: KeyPath, _ value: V) -> Where { return Where("%K >= %@", keyPath._kvcKeyPathString!, value) @@ -88,6 +148,12 @@ public func >= (_ ke // MARK: - KeyPath where Root: NSManagedObject, Value: Optional +/** + Creates a `Where` clause by comparing if a property is less than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age < 20)) + ``` + */ public func < (_ keyPath: KeyPath>, _ value: V?) -> Where { if let value = value { @@ -100,6 +166,12 @@ public func < (_ key } } +/** + Creates a `Where` clause by comparing if a property is greater than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age > 20)) + ``` + */ public func > (_ keyPath: KeyPath>, _ value: V?) -> Where { if let value = value { @@ -112,6 +184,12 @@ public func > (_ key } } +/** + Creates a `Where` clause by comparing if a property is less than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age <= 20)) + ``` + */ public func <= (_ keyPath: KeyPath>, _ value: V?) -> Where { if let value = value { @@ -124,6 +202,12 @@ public func <= (_ ke } } +/** + Creates a `Where` clause by comparing if a property is greater than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age >= 20)) + ``` + */ public func >= (_ keyPath: KeyPath>, _ value: V?) -> Where { if let value = value { @@ -139,31 +223,67 @@ public func >= (_ ke // MARK: - KeyPath where Root: NSManagedObject, Value: NSManagedObject +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master == john)) + ``` + */ public func == (_ keyPath: KeyPath, _ object: D) -> Where { return Where(keyPath._kvcKeyPathString!, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master != john)) + ``` + */ public func != (_ keyPath: KeyPath, _ object: D) -> Where { return !Where(keyPath._kvcKeyPathString!, isEqualTo: object) } +/** + Creates a `Where` clause by checking if a sequence contains a value of a property + ``` + let dog = CoreStore.fetchOne(From().where([john, bob, joe] ~= \.master)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == D { return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) } +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master == john)) + ``` + */ public func == (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master != john)) + ``` + */ public func != (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) } +/** + Creates a `Where` clause by checking if a sequence contains a value of a property + ``` + let dog = CoreStore.fetchOne(From().where([john, bob, joe] ~= \.master)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath) -> Where where S.Iterator.Element == NSManagedObjectID { return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) @@ -172,31 +292,67 @@ public func ~= (_ sequence: // MARK: - KeyPath where Root: NSManagedObject, Value: Optional +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master == john)) + ``` + */ public func == (_ keyPath: KeyPath>, _ object: D?) -> Where { return Where(keyPath._kvcKeyPathString!, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master != john)) + ``` + */ public func != (_ keyPath: KeyPath>, _ object: D?) -> Where { return !Where(keyPath._kvcKeyPathString!, isEqualTo: object) } +/** + Creates a `Where` clause by checking if a sequence contains a value of a property + ``` + let dog = CoreStore.fetchOne(From().where([john, bob, joe] ~= \.master)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath>) -> Where where S.Iterator.Element == D { return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) } +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master == john)) + ``` + */ public func == (_ keyPath: KeyPath>, _ objectID: NSManagedObjectID?) -> Where { return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master != john)) + ``` + */ public func != (_ keyPath: KeyPath>, _ objectID: NSManagedObjectID?) -> Where { return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) } +/** + Creates a `Where` clause by checking if a sequence contains a value of a property + ``` + let dog = CoreStore.fetchOne(From().where([john, bob, joe] ~= \.master)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath>) -> Where where S.Iterator.Element == NSManagedObjectID { return Where(keyPath._kvcKeyPathString!, isMemberOf: sequence) @@ -205,16 +361,34 @@ public func ~= (_ sequence: // MARK: - KeyPath where Root: CoreStoreObject, Value: ValueContainer.Required +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname == "John")) + ``` + */ public func == (_ keyPath: KeyPath.Required>, _ value: V) -> Where { return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname != "John")) + ``` + */ public func != (_ keyPath: KeyPath.Required>, _ value: V) -> Where { return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) } +/** + Creates a `Where` clause by checking if a sequence contains the value of a property + ``` + let dog = CoreStore.fetchOne(From().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath.Required>) -> Where where S.Iterator.Element == V { return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) @@ -223,16 +397,34 @@ public func ~= (_ sequence: S, _ keyPath: KeyPath.Optional +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname == "John")) + ``` + */ public func == (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname != "John")) + ``` + */ public func != (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) } +/** + Creates a `Where` clause by checking if a sequence contains the value of a property + ``` + let dog = CoreStore.fetchOne(From().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath.Optional>) -> Where where S.Iterator.Element == V { return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) @@ -241,21 +433,45 @@ public func ~= (_ sequence: S, _ keyPath: KeyPath.Required +/** + Creates a `Where` clause by comparing if a property is less than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age < 20)) + ``` + */ public func < (_ keyPath: KeyPath.Required>, _ value: V) -> Where { return Where("%K < %@", O.meta[keyPath: keyPath].keyPath, value) } +/** + Creates a `Where` clause by comparing if a property is greater than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age > 20)) + ``` + */ public func > (_ keyPath: KeyPath.Required>, _ value: V) -> Where { return Where("%K > %@", O.meta[keyPath: keyPath].keyPath, value) } +/** + Creates a `Where` clause by comparing if a property is less than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age <= 20)) + ``` + */ public func <= (_ keyPath: KeyPath.Required>, _ value: V) -> Where { return Where("%K <= %@", O.meta[keyPath: keyPath].keyPath, value) } +/** + Creates a `Where` clause by comparing if a property is greater than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age >= 20)) + ``` + */ public func >= (_ keyPath: KeyPath.Required>, _ value: V) -> Where { return Where("%K >= %@", O.meta[keyPath: keyPath].keyPath, value) @@ -264,6 +480,12 @@ public func >= (_ keyPath: KeyPath.Requir // MARK: - KeyPath where Root: CoreStoreObject, Value: ValueContainer.Optional +/** + Creates a `Where` clause by comparing if a property is less than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age < 20)) + ``` + */ public func < (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { if let value = value { @@ -276,6 +498,12 @@ public func < (_ keyPath: KeyPath.Optional>, _ val } } +/** + Creates a `Where` clause by comparing if a property is greater than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age > 20)) + ``` + */ public func > (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { if let value = value { @@ -288,6 +516,12 @@ public func > (_ keyPath: KeyPath.Optional>, _ val } } +/** + Creates a `Where` clause by comparing if a property is less than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age <= 20)) + ``` + */ public func <= (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { if let value = value { @@ -300,6 +534,12 @@ public func <= (_ keyPath: KeyPath.Optional>, _ va } } +/** + Creates a `Where` clause by comparing if a property is greater than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age >= 20)) + ``` + */ public func >= (_ keyPath: KeyPath.Optional>, _ value: V?) -> Where { if let value = value { @@ -315,26 +555,56 @@ public func >= (_ keyPath: KeyPath.Optional>, _ va // MARK: - KeyPath where Root: CoreStoreObject, Value: RelationshipContainer.ToOne +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master == john)) + ``` + */ public func == (_ keyPath: KeyPath.ToOne>, _ object: D) -> Where { return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master == john)) + ``` + */ public func == (_ keyPath: KeyPath.ToOne>, _ object: D?) -> Where { return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master != john)) + ``` + */ public func != (_ keyPath: KeyPath.ToOne>, _ object: D) -> Where { return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master != john)) + ``` + */ public func != (_ keyPath: KeyPath.ToOne>, _ object: D?) -> Where { return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +/** + Creates a `Where` clause by checking if a sequence contains a value of a property + ``` + let dog = CoreStore.fetchOne(From().where([john, bob, joe] ~= \.master)) + ``` + */ public func ~= (_ sequence: S, _ keyPath: KeyPath.ToOne>) -> Where where S.Iterator.Element == D { return Where(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence) diff --git a/Sources/Where.swift b/Sources/Where.swift index 96eb888..9ec9329 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -502,6 +502,16 @@ public extension Where where D: CoreStoreObject { self.init(D.meta[keyPath: keyPath].keyPath, isMemberOf: list) } + + /** + Initializes a `Where` clause from a closure + + - parameter condition: closure that returns the `Where` clause + */ + public init(_ condition: (D) -> Where) { + + self = condition(D.meta) + } } From 8a4d1cd7c60d09d10d05d5c4c8e139d72168e9a6 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Thu, 9 Nov 2017 19:27:05 +0900 Subject: [PATCH 47/56] fix demo app warnings --- .../ListObserverDemoViewController.swift | 14 +++++++------- .../ObjectObserverDemoViewController.swift | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift index 85632f2..acba564 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift @@ -32,9 +32,9 @@ struct ColorsDemo { switch self { - case .all: return Where(true) - case .light: return Palette.where({ $0.brightness >= 0.9 }) - case .dark: return Palette.where({ $0.brightness <= 0.4 }) + case .all: return .init() + case .light: return (\Palette.brightness >= 0.9) + case .dark: return (\Palette.brightness <= 0.4) } } } @@ -45,7 +45,7 @@ struct ColorsDemo { self.palettes.refetch( self.filter.whereClause(), - Palette.orderBy(ascending: { $0.hue }) + OrderBy(.ascending(\.hue)) ) } } @@ -74,9 +74,9 @@ struct ColorsDemo { ) ) return ColorsDemo.stack.monitorSectionedList( - From(), - SectionBy(Palette.keyPath({ $0.colorName })), - Palette.orderBy(ascending: { $0.hue }) + From() + .sectionBy(\.colorName) + .orderBy(.ascending(\.hue)) ) }() } diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift index 502bcbc..059c735 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ObjectObserverDemoViewController.swift @@ -50,7 +50,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver { required init?(coder aDecoder: NSCoder) { - if let palette = ColorsDemo.stack.fetchOne(From(), Palette.orderBy(ascending: { $0.hue })) { + if let palette = ColorsDemo.stack.fetchOne(From().orderBy(.ascending(\.hue))) { self.monitor = ColorsDemo.stack.monitorObject(palette) } @@ -64,7 +64,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver { } ) - let palette = ColorsDemo.stack.fetchOne(From(), Palette.orderBy(ascending: { $0.hue }))! + let palette = ColorsDemo.stack.fetchOne(From().orderBy(.ascending(\.hue)))! self.monitor = ColorsDemo.stack.monitorObject(palette) } From b2ff8a15ef9dd9f116948a469726cef7d67a8d65 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Thu, 9 Nov 2017 19:27:56 +0900 Subject: [PATCH 48/56] add query overloads to == so comparison with nil don't confuse the compiler --- Sources/KeyPath+Querying.swift | 176 +++++++++++++++++++++++++++++++++ Sources/Where.swift | 28 +++--- 2 files changed, 190 insertions(+), 14 deletions(-) diff --git a/Sources/KeyPath+Querying.swift b/Sources/KeyPath+Querying.swift index f9a120c..3082303 100644 --- a/Sources/KeyPath+Querying.swift +++ b/Sources/KeyPath+Querying.swift @@ -76,6 +76,17 @@ public func == (_ key return Where(keyPath._kvcKeyPathString!, isEqualTo: value) } +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname == nil)) + ``` + */ +public func == (_ keyPath: KeyPath>, _ null: Void?) -> Where { + + return Where("%K == nil", keyPath._kvcKeyPathString!) +} + /** Creates a `Where` clause by comparing if a property is not equal to a value ``` @@ -87,6 +98,17 @@ public func != (_ key return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname != nil)) + ``` + */ +public func != (_ keyPath: KeyPath>, _ null: Void?) -> Where { + + return Where("%K != nil", keyPath._kvcKeyPathString!) +} + /** Creates a `Where` clause by checking if a sequence contains the value of a property ``` @@ -166,6 +188,17 @@ public func < (_ key } } +/** + Creates a `Where` clause by comparing if a property is less than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age < 20)) + ``` + */ +public func < (_ keyPath: KeyPath>, _ null: Void?) -> Where { + + return Where("%K < nil", keyPath._kvcKeyPathString!) +} + /** Creates a `Where` clause by comparing if a property is greater than a value ``` @@ -184,6 +217,17 @@ public func > (_ key } } +/** + Creates a `Where` clause by comparing if a property is greater than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age > nil)) + ``` + */ +public func > (_ keyPath: KeyPath>, _ null: Void?) -> Where { + + return Where("%K > nil", keyPath._kvcKeyPathString!) +} + /** Creates a `Where` clause by comparing if a property is less than or equal to a value ``` @@ -202,6 +246,17 @@ public func <= (_ ke } } +/** + Creates a `Where` clause by comparing if a property is less than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age <= nil)) + ``` + */ +public func <= (_ keyPath: KeyPath>, _ null: Void?) -> Where { + + return Where("%K <= nil", keyPath._kvcKeyPathString!) +} + /** Creates a `Where` clause by comparing if a property is greater than or equal to a value ``` @@ -220,6 +275,17 @@ public func >= (_ ke } } +/** + Creates a `Where` clause by comparing if a property is greater than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age >= nil)) + ``` + */ +public func >= (_ keyPath: KeyPath>, _ null: Void?) -> Where { + + return Where("%K >= nil", keyPath._kvcKeyPathString!) +} + // MARK: - KeyPath where Root: NSManagedObject, Value: NSManagedObject @@ -303,6 +369,17 @@ public func == (_ keyPath: KeyPath(keyPath._kvcKeyPathString!, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master == nil)) + ``` + */ +public func == (_ keyPath: KeyPath>, _ null: Void?) -> Where { + + return Where("%K == nil", keyPath._kvcKeyPathString!) +} + /** Creates a `Where` clause by comparing if a property is not equal to a value ``` @@ -314,6 +391,17 @@ public func != (_ keyPath: KeyPath(keyPath._kvcKeyPathString!, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master != nil)) + ``` + */ +public func != (_ keyPath: KeyPath>, _ null: Void?) -> Where { + + return Where("%K != nil", keyPath._kvcKeyPathString!) +} + /** Creates a `Where` clause by checking if a sequence contains a value of a property ``` @@ -408,6 +496,17 @@ public func == (_ keyPath: KeyPath.Optional>, _ va return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) } +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname == nil)) + ``` + */ +public func == (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { + + return Where("%K == nil", O.meta[keyPath: keyPath].keyPath) +} + /** Creates a `Where` clause by comparing if a property is not equal to a value ``` @@ -419,6 +518,17 @@ public func != (_ keyPath: KeyPath.Optional>, _ va return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.nickname != nil)) + ``` + */ +public func != (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { + + return Where("%K != nil", O.meta[keyPath: keyPath].keyPath) +} + /** Creates a `Where` clause by checking if a sequence contains the value of a property ``` @@ -498,6 +608,17 @@ public func < (_ keyPath: KeyPath.Optional>, _ val } } +/** + Creates a `Where` clause by comparing if a property is less than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age < nil)) + ``` + */ +public func < (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { + + return Where("%K < nil", O.meta[keyPath: keyPath].keyPath) +} + /** Creates a `Where` clause by comparing if a property is greater than a value ``` @@ -516,6 +637,17 @@ public func > (_ keyPath: KeyPath.Optional>, _ val } } +/** + Creates a `Where` clause by comparing if a property is greater than a value + ``` + let person = CoreStore.fetchOne(From().where(\.age > nil)) + ``` + */ +public func > (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { + + return Where("%K > nil", O.meta[keyPath: keyPath].keyPath) +} + /** Creates a `Where` clause by comparing if a property is less than or equal to a value ``` @@ -534,6 +666,17 @@ public func <= (_ keyPath: KeyPath.Optional>, _ va } } +/** + Creates a `Where` clause by comparing if a property is less than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age <= nil)) + ``` + */ +public func <= (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { + + return Where("%K <= nil", O.meta[keyPath: keyPath].keyPath) +} + /** Creates a `Where` clause by comparing if a property is greater than or equal to a value ``` @@ -552,6 +695,17 @@ public func >= (_ keyPath: KeyPath.Optional>, _ va } } +/** + Creates a `Where` clause by comparing if a property is greater than or equal to a value + ``` + let person = CoreStore.fetchOne(From().where(\.age >= nil)) + ``` + */ +public func >= (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { + + return Where("%K >= nil", O.meta[keyPath: keyPath].keyPath) +} + // MARK: - KeyPath where Root: CoreStoreObject, Value: RelationshipContainer.ToOne @@ -577,6 +731,17 @@ public func == (_ keyPath: KeyPath.ToOne>, return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master == nil)) + ``` + */ +public func == (_ keyPath: KeyPath.ToOne>, _ null: Void?) -> Where { + + return Where("%K == nil", O.meta[keyPath: keyPath].keyPath) +} + /** Creates a `Where` clause by comparing if a property is not equal to a value ``` @@ -599,6 +764,17 @@ public func != (_ keyPath: KeyPath.ToOne>, return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } +/** + Creates a `Where` clause by comparing if a property is not equal to a value + ``` + let dog = CoreStore.fetchOne(From().where(\.master != nil)) + ``` + */ +public func != (_ keyPath: KeyPath.ToOne>, _ null: Void?) -> Where { + + return Where("%K != nil", O.meta[keyPath: keyPath].keyPath) +} + /** Creates a `Where` clause by checking if a sequence contains a value of a property ``` diff --git a/Sources/Where.swift b/Sources/Where.swift index 9ec9329..3f53808 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -158,9 +158,9 @@ public struct Where: WhereClauseType, FetchClause, QueryClause Initializes a `Where` clause that compares equality to `nil` - parameter keyPath: the keyPath to compare with - - parameter value: the arguments for the `==` operator + - parameter null: the arguments for the `==` operator */ - public init(_ keyPath: KeyPathString, isEqualTo value: Void?) { + public init(_ keyPath: KeyPathString, isEqualTo null: Void?) { self.init(NSPredicate(format: "\(keyPath) == nil")) } @@ -303,22 +303,22 @@ public extension Where where D: NSManagedObject { Initializes a `Where` clause that compares equality to `nil` - parameter keyPath: the keyPath to compare with - - parameter value: the arguments for the `==` operator + - parameter null: the arguments for the `==` operator */ - public init(_ keyPath: KeyPath, isEqualTo value: Void?) { + public init(_ keyPath: KeyPath, isEqualTo null: Void?) { - self.init(keyPath._kvcKeyPathString!, isEqualTo: value) + self.init(keyPath._kvcKeyPathString!, isEqualTo: null) } /** Initializes a `Where` clause that compares equality to `nil` - parameter keyPath: the keyPath to compare with - - parameter value: the arguments for the `==` operator + - parameter null: the arguments for the `==` operator */ - public init(_ keyPath: KeyPath, isEqualTo value: Void?) { + public init(_ keyPath: KeyPath, isEqualTo null: Void?) { - self.init(keyPath._kvcKeyPathString!, isEqualTo: value) + self.init(keyPath._kvcKeyPathString!, isEqualTo: null) } /** @@ -397,22 +397,22 @@ public extension Where where D: CoreStoreObject { Initializes a `Where` clause that compares equality to `nil` - parameter keyPath: the keyPath to compare with - - parameter value: the arguments for the `==` operator + - parameter null: the arguments for the `==` operator */ - public init(_ keyPath: KeyPath.Optional>, isEqualTo value: Void?) { + public init(_ keyPath: KeyPath.Optional>, isEqualTo null: Void?) { - self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: value) + self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: null) } /** Initializes a `Where` clause that compares equality to `nil` - parameter keyPath: the keyPath to compare with - - parameter value: the arguments for the `==` operator + - parameter null: the arguments for the `==` operator */ - public init(_ keyPath: KeyPath.ToOne>, isEqualTo value: Void?) { + public init(_ keyPath: KeyPath.ToOne>, isEqualTo null: Void?) { - self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: value) + self.init(D.meta[keyPath: keyPath].keyPath, isEqualTo: null) } /** From dd4e47d7f90d695d4b3198129e036ef9b43049fa Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Thu, 9 Nov 2017 23:26:53 +0900 Subject: [PATCH 49/56] revert null overloads, remove optional objectIDs as condition --- Sources/KeyPath+Querying.swift | 184 +-------------------------------- 1 file changed, 4 insertions(+), 180 deletions(-) diff --git a/Sources/KeyPath+Querying.swift b/Sources/KeyPath+Querying.swift index 3082303..16ac974 100644 --- a/Sources/KeyPath+Querying.swift +++ b/Sources/KeyPath+Querying.swift @@ -76,17 +76,6 @@ public func == (_ key return Where(keyPath._kvcKeyPathString!, isEqualTo: value) } -/** - Creates a `Where` clause by comparing if a property is equal to a value - ``` - let person = CoreStore.fetchOne(From().where(\.nickname == nil)) - ``` - */ -public func == (_ keyPath: KeyPath>, _ null: Void?) -> Where { - - return Where("%K == nil", keyPath._kvcKeyPathString!) -} - /** Creates a `Where` clause by comparing if a property is not equal to a value ``` @@ -98,17 +87,6 @@ public func != (_ key return !Where(keyPath._kvcKeyPathString!, isEqualTo: value) } -/** - Creates a `Where` clause by comparing if a property is not equal to a value - ``` - let person = CoreStore.fetchOne(From().where(\.nickname != nil)) - ``` - */ -public func != (_ keyPath: KeyPath>, _ null: Void?) -> Where { - - return Where("%K != nil", keyPath._kvcKeyPathString!) -} - /** Creates a `Where` clause by checking if a sequence contains the value of a property ``` @@ -188,17 +166,6 @@ public func < (_ key } } -/** - Creates a `Where` clause by comparing if a property is less than a value - ``` - let person = CoreStore.fetchOne(From().where(\.age < 20)) - ``` - */ -public func < (_ keyPath: KeyPath>, _ null: Void?) -> Where { - - return Where("%K < nil", keyPath._kvcKeyPathString!) -} - /** Creates a `Where` clause by comparing if a property is greater than a value ``` @@ -217,17 +184,6 @@ public func > (_ key } } -/** - Creates a `Where` clause by comparing if a property is greater than a value - ``` - let person = CoreStore.fetchOne(From().where(\.age > nil)) - ``` - */ -public func > (_ keyPath: KeyPath>, _ null: Void?) -> Where { - - return Where("%K > nil", keyPath._kvcKeyPathString!) -} - /** Creates a `Where` clause by comparing if a property is less than or equal to a value ``` @@ -246,17 +202,6 @@ public func <= (_ ke } } -/** - Creates a `Where` clause by comparing if a property is less than or equal to a value - ``` - let person = CoreStore.fetchOne(From().where(\.age <= nil)) - ``` - */ -public func <= (_ keyPath: KeyPath>, _ null: Void?) -> Where { - - return Where("%K <= nil", keyPath._kvcKeyPathString!) -} - /** Creates a `Where` clause by comparing if a property is greater than or equal to a value ``` @@ -275,17 +220,6 @@ public func >= (_ ke } } -/** - Creates a `Where` clause by comparing if a property is greater than or equal to a value - ``` - let person = CoreStore.fetchOne(From().where(\.age >= nil)) - ``` - */ -public func >= (_ keyPath: KeyPath>, _ null: Void?) -> Where { - - return Where("%K >= nil", keyPath._kvcKeyPathString!) -} - // MARK: - KeyPath where Root: NSManagedObject, Value: NSManagedObject @@ -328,7 +262,7 @@ public func ~= (_ sequence: let dog = CoreStore.fetchOne(From().where(\.master == john)) ``` */ -public func == (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { +public func == (_ keyPath: KeyPath, _ objectID: NSManagedObjectID) -> Where { return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) } @@ -339,7 +273,7 @@ public func == (_ keyPath: KeyPath let dog = CoreStore.fetchOne(From().where(\.master != john)) ``` */ -public func != (_ keyPath: KeyPath, _ objectID: NSManagedObjectID?) -> Where { +public func != (_ keyPath: KeyPath, _ objectID: NSManagedObjectID) -> Where { return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) } @@ -369,17 +303,6 @@ public func == (_ keyPath: KeyPath(keyPath._kvcKeyPathString!, isEqualTo: object) } -/** - Creates a `Where` clause by comparing if a property is equal to a value - ``` - let dog = CoreStore.fetchOne(From().where(\.master == nil)) - ``` - */ -public func == (_ keyPath: KeyPath>, _ null: Void?) -> Where { - - return Where("%K == nil", keyPath._kvcKeyPathString!) -} - /** Creates a `Where` clause by comparing if a property is not equal to a value ``` @@ -391,17 +314,6 @@ public func != (_ keyPath: KeyPath(keyPath._kvcKeyPathString!, isEqualTo: object) } -/** - Creates a `Where` clause by comparing if a property is not equal to a value - ``` - let dog = CoreStore.fetchOne(From().where(\.master != nil)) - ``` - */ -public func != (_ keyPath: KeyPath>, _ null: Void?) -> Where { - - return Where("%K != nil", keyPath._kvcKeyPathString!) -} - /** Creates a `Where` clause by checking if a sequence contains a value of a property ``` @@ -419,7 +331,7 @@ public func ~= (_ sequence: let dog = CoreStore.fetchOne(From().where(\.master == john)) ``` */ -public func == (_ keyPath: KeyPath>, _ objectID: NSManagedObjectID?) -> Where { +public func == (_ keyPath: KeyPath>, _ objectID: NSManagedObjectID) -> Where { return Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) } @@ -430,7 +342,7 @@ public func == (_ keyPath: KeyPath().where(\.master != john)) ``` */ -public func != (_ keyPath: KeyPath>, _ objectID: NSManagedObjectID?) -> Where { +public func != (_ keyPath: KeyPath>, _ objectID: NSManagedObjectID) -> Where { return !Where(keyPath._kvcKeyPathString!, isEqualTo: objectID) } @@ -496,17 +408,6 @@ public func == (_ keyPath: KeyPath.Optional>, _ va return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) } -/** - Creates a `Where` clause by comparing if a property is equal to a value - ``` - let person = CoreStore.fetchOne(From().where(\.nickname == nil)) - ``` - */ -public func == (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { - - return Where("%K == nil", O.meta[keyPath: keyPath].keyPath) -} - /** Creates a `Where` clause by comparing if a property is not equal to a value ``` @@ -518,17 +419,6 @@ public func != (_ keyPath: KeyPath.Optional>, _ va return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: value) } -/** - Creates a `Where` clause by comparing if a property is not equal to a value - ``` - let person = CoreStore.fetchOne(From().where(\.nickname != nil)) - ``` - */ -public func != (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { - - return Where("%K != nil", O.meta[keyPath: keyPath].keyPath) -} - /** Creates a `Where` clause by checking if a sequence contains the value of a property ``` @@ -608,17 +498,6 @@ public func < (_ keyPath: KeyPath.Optional>, _ val } } -/** - Creates a `Where` clause by comparing if a property is less than a value - ``` - let person = CoreStore.fetchOne(From().where(\.age < nil)) - ``` - */ -public func < (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { - - return Where("%K < nil", O.meta[keyPath: keyPath].keyPath) -} - /** Creates a `Where` clause by comparing if a property is greater than a value ``` @@ -637,17 +516,6 @@ public func > (_ keyPath: KeyPath.Optional>, _ val } } -/** - Creates a `Where` clause by comparing if a property is greater than a value - ``` - let person = CoreStore.fetchOne(From().where(\.age > nil)) - ``` - */ -public func > (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { - - return Where("%K > nil", O.meta[keyPath: keyPath].keyPath) -} - /** Creates a `Where` clause by comparing if a property is less than or equal to a value ``` @@ -666,17 +534,6 @@ public func <= (_ keyPath: KeyPath.Optional>, _ va } } -/** - Creates a `Where` clause by comparing if a property is less than or equal to a value - ``` - let person = CoreStore.fetchOne(From().where(\.age <= nil)) - ``` - */ -public func <= (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { - - return Where("%K <= nil", O.meta[keyPath: keyPath].keyPath) -} - /** Creates a `Where` clause by comparing if a property is greater than or equal to a value ``` @@ -695,17 +552,6 @@ public func >= (_ keyPath: KeyPath.Optional>, _ va } } -/** - Creates a `Where` clause by comparing if a property is greater than or equal to a value - ``` - let person = CoreStore.fetchOne(From().where(\.age >= nil)) - ``` - */ -public func >= (_ keyPath: KeyPath.Optional>, _ null: Void?) -> Where { - - return Where("%K >= nil", O.meta[keyPath: keyPath].keyPath) -} - // MARK: - KeyPath where Root: CoreStoreObject, Value: RelationshipContainer.ToOne @@ -731,17 +577,6 @@ public func == (_ keyPath: KeyPath.ToOne>, return Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } -/** - Creates a `Where` clause by comparing if a property is equal to a value - ``` - let dog = CoreStore.fetchOne(From().where(\.master == nil)) - ``` - */ -public func == (_ keyPath: KeyPath.ToOne>, _ null: Void?) -> Where { - - return Where("%K == nil", O.meta[keyPath: keyPath].keyPath) -} - /** Creates a `Where` clause by comparing if a property is not equal to a value ``` @@ -764,17 +599,6 @@ public func != (_ keyPath: KeyPath.ToOne>, return !Where(O.meta[keyPath: keyPath].keyPath, isEqualTo: object) } -/** - Creates a `Where` clause by comparing if a property is not equal to a value - ``` - let dog = CoreStore.fetchOne(From().where(\.master != nil)) - ``` - */ -public func != (_ keyPath: KeyPath.ToOne>, _ null: Void?) -> Where { - - return Where("%K != nil", O.meta[keyPath: keyPath].keyPath) -} - /** Creates a `Where` clause by checking if a sequence contains a value of a property ``` From 662aaa1e759a41b990284a7f96d391972d7d9f4b Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 10 Nov 2017 02:48:37 +0900 Subject: [PATCH 50/56] force true lightweight migration --- Sources/DataStack+Migration.swift | 29 +++++++++++++++++-- .../NSPersistentStoreCoordinator+Setup.swift | 15 +++------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Sources/DataStack+Migration.swift b/Sources/DataStack+Migration.swift index 5de36bd..7c985f9 100644 --- a/Sources/DataStack+Migration.swift +++ b/Sources/DataStack+Migration.swift @@ -575,6 +575,7 @@ public extension DataStack { sourceModel: sourceModel, destinationModel: destinationModel, mappingModel: mappingModel, + migrationType: migrationType, progress: childProgress ) } @@ -680,10 +681,34 @@ public extension DataStack { return nil } - private func startMigrationForStorage(_ storage: T, sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, progress: Progress) throws { + private func startMigrationForStorage(_ storage: T, sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType, progress: Progress) throws { let fileURL = storage.fileURL - + if case .lightweight = migrationType { + + do { + + _ = try withExtendedLifetime(NSPersistentStoreCoordinator(managedObjectModel: destinationModel)) { (coordinator: NSPersistentStoreCoordinator) in + + try coordinator.addPersistentStoreSynchronously( + type(of: storage).storeType, + configuration: storage.configuration, + URL: fileURL, + options: storage.dictionary( + forOptions: storage.localStorageOptions.union(.allowSynchronousLightweightMigration) + ) + ) + } + _ = try? storage.cs_finalizeStorageAndWait(soureModelHint: destinationModel) + progress.completedUnitCount = progress.totalUnitCount + return + } + catch { + + // try manual migration + } + } + let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) .appendingPathComponent(Bundle.main.bundleIdentifier ?? "com.CoreStore.DataStack") .appendingPathComponent(ProcessInfo().globallyUniqueString) diff --git a/Sources/NSPersistentStoreCoordinator+Setup.swift b/Sources/NSPersistentStoreCoordinator+Setup.swift index 8bba039..13aa538 100644 --- a/Sources/NSPersistentStoreCoordinator+Setup.swift +++ b/Sources/NSPersistentStoreCoordinator+Setup.swift @@ -72,15 +72,13 @@ internal extension NSPersistentStoreCoordinator { } @nonobjc - internal func addPersistentStoreSynchronously(_ storeType: String, configuration: ModelConfiguration, URL storeURL: URL?, options: [NSObject : AnyObject]?) throws -> NSPersistentStore { + internal func addPersistentStoreSynchronously(_ storeType: String, configuration: ModelConfiguration, URL storeURL: URL?, options: [AnyHashable: Any]?) throws -> NSPersistentStore { - var store: NSPersistentStore? - var storeError: NSError? - self.performSynchronously { + return try self.performSynchronously { do { - store = try self.addPersistentStore( + return try self.addPersistentStore( ofType: storeType, configurationName: configuration, at: storeURL, @@ -89,13 +87,8 @@ internal extension NSPersistentStoreCoordinator { } catch { - storeError = error as NSError + throw CoreStoreError(error) } } - if let store = store { - - return store - } - throw CoreStoreError(storeError) } } From 49c4b770eb7a2715baa74476d377928043a3e875 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Fri, 10 Nov 2017 19:19:48 +0900 Subject: [PATCH 51/56] WIP: Readme --- README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 613f227..e6aa319 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ Unleashing the real power of Core Data with the elegance and safety of Swift

-* **Swift 3.2:** iOS 8+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+ -* Other Swift versions: [Swift 3.1(version 4.1.4)](https://github.com/JohnEstropia/CoreStore/tree/4.1.4), [Swift 4.0](https://github.com/JohnEstropia/CoreStore/tree/5.0.0) +* **Swift 4.0:** iOS 8+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+ +* Other Swift versions: [Swift 3.2(version 4.2.3)](https://github.com/JohnEstropia/CoreStore/tree/4.2.3) -Upgrading from CoreStore 4.1 (Swift 3.1) to 4.2 (Swift 3.2)? Check out the [new features](#features) and make sure to read the [Change logs](https://github.com/JohnEstropia/CoreStore/releases). +Upgrading from CoreStore 4.2 (Swift 3.2) to 5.0 (Swift 4.0)? Check out the [new features](#features) and make sure to read the [Change logs](https://github.com/JohnEstropia/CoreStore/releases). CoreStore is now part of the [Swift Source Compatibility projects](https://swift.org/source-compatibility/#current-list-of-projects). @@ -146,20 +146,18 @@ let people = CoreStore.fetchAll(From()) Fetching objects (complex): ```swift let people = CoreStore.fetchAll( - From(), - Where("age > 30"), - OrderBy(.ascending("name"), .descending("age")), - Tweak { (fetchRequest) -> Void in - fetchRequest.includesPendingChanges = false - } + From() + .where(\.age > 30), + .orderBy(.ascending(\.name), .descending(.\age)), + .tweak({ $0.includesPendingChanges = false }) ) ``` Querying values: ```swift let maxAge = CoreStore.queryValue( - From(), - Select(.maximum("age")) + From() + .select(Int.self, .maximum(\.age)) ) ``` From 66dd5b6f27ca02daee9eff72768e1bb9d9617bdb Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 13 Nov 2017 02:44:26 +0900 Subject: [PATCH 52/56] added fake progress for lightweight migrations --- Sources/DataStack+Migration.swift | 33 ++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Sources/DataStack+Migration.swift b/Sources/DataStack+Migration.swift index 7c985f9..e441dbf 100644 --- a/Sources/DataStack+Migration.swift +++ b/Sources/DataStack+Migration.swift @@ -688,8 +688,35 @@ public extension DataStack { do { + let timerQueue = DispatchQueue( + label: "DataStack.lightweightMigration.timerQueue", + qos: .utility, + attributes: [] + ) + let estimatedTime: TimeInterval = 60 * 3 // 3 mins + let interval: TimeInterval = 1 + let fakeTotalUnitCount: Float = 0.9 * Float(progress.totalUnitCount) + var fakeProgress: Float = 0 + + var recursiveCheck: () -> Void = {} + recursiveCheck = { + + guard fakeProgress < 1 else { + + return + } + progress.completedUnitCount = Int64(fakeTotalUnitCount * fakeProgress) + fakeProgress += Float(interval / estimatedTime) + + timerQueue.asyncAfter( + deadline: .now() + interval, + execute: recursiveCheck + ) + } + timerQueue.async(execute: recursiveCheck) + _ = try withExtendedLifetime(NSPersistentStoreCoordinator(managedObjectModel: destinationModel)) { (coordinator: NSPersistentStoreCoordinator) in - + try coordinator.addPersistentStoreSynchronously( type(of: storage).storeType, configuration: storage.configuration, @@ -699,6 +726,10 @@ public extension DataStack { ) ) } + timerQueue.sync { + + fakeProgress = 1 + } _ = try? storage.cs_finalizeStorageAndWait(soureModelHint: destinationModel) progress.completedUnitCount = progress.totalUnitCount return From b55dd13dffdc87ad1b081a0b63eb459e15931a31 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 15 Nov 2017 23:50:10 +0900 Subject: [PATCH 53/56] merge sqlite journal files before migration --- Sources/DataStack+Migration.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/DataStack+Migration.swift b/Sources/DataStack+Migration.swift index e441dbf..11601f6 100644 --- a/Sources/DataStack+Migration.swift +++ b/Sources/DataStack+Migration.swift @@ -715,6 +715,7 @@ public extension DataStack { } timerQueue.async(execute: recursiveCheck) + _ = try storage.cs_finalizeStorageAndWait(soureModelHint: sourceModel) _ = try withExtendedLifetime(NSPersistentStoreCoordinator(managedObjectModel: destinationModel)) { (coordinator: NSPersistentStoreCoordinator) in try coordinator.addPersistentStoreSynchronously( From 583c6b7249bcd038578b21b0d6677d14e52b9ceb Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 19 Nov 2017 13:49:21 +0900 Subject: [PATCH 54/56] minor code cleanup --- Sources/DataStack+Migration.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Sources/DataStack+Migration.swift b/Sources/DataStack+Migration.swift index f723670..ec7c849 100644 --- a/Sources/DataStack+Migration.swift +++ b/Sources/DataStack+Migration.swift @@ -682,6 +682,15 @@ public extension DataStack { private func startMigrationForStorage(_ storage: T, sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType, progress: Progress) throws { + do { + + try storage.cs_finalizeStorageAndWait(soureModelHint: sourceModel) + } + catch { + + throw CoreStoreError(error) + } + let fileURL = storage.fileURL if case .lightweight = migrationType { @@ -714,7 +723,6 @@ public extension DataStack { } timerQueue.async(execute: recursiveCheck) - _ = try storage.cs_finalizeStorageAndWait(soureModelHint: sourceModel) _ = try withExtendedLifetime(NSPersistentStoreCoordinator(managedObjectModel: destinationModel)) { (coordinator: NSPersistentStoreCoordinator) in try coordinator.addPersistentStoreSynchronously( @@ -730,13 +738,13 @@ public extension DataStack { fakeProgress = 1 } - _ = try? storage.cs_finalizeStorageAndWait(soureModelHint: destinationModel) + try storage.cs_finalizeStorageAndWait(soureModelHint: destinationModel) progress.completedUnitCount = progress.totalUnitCount return } catch { - // try manual migration + throw CoreStoreError(error) } } @@ -764,7 +772,6 @@ public extension DataStack { do { - try storage.cs_finalizeStorageAndWait(soureModelHint: sourceModel) try migrationManager.migrateStore( from: fileURL, sourceType: type(of: storage).storeType, From 15e5e4fdf62bf64d9a044b28ac80f8d9c76b3cda Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 19 Nov 2017 15:34:09 +0900 Subject: [PATCH 55/56] delete shm file after converting to DELETE journal mode --- Sources/SQLiteStore.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/SQLiteStore.swift b/Sources/SQLiteStore.swift index be95dfb..7abebde 100644 --- a/Sources/SQLiteStore.swift +++ b/Sources/SQLiteStore.swift @@ -219,6 +219,7 @@ public final class SQLiteStore: LocalStorage { options: [NSSQLitePragmasOption: ["journal_mode": "DELETE"]] ) } + _ = try? FileManager.default.removeItem(atPath: "\(self.fileURL.path)-shm") } /** From 15edabdbb5f230b237106c2d59603e3b1108c905 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 29 Dec 2017 00:05:11 +0900 Subject: [PATCH 56/56] changed keyPath string utility to use String initializer --- CoreStore.xcodeproj/project.pbxproj | 20 +-- CoreStoreTests/DynamicModelTests.swift | 20 ++- CoreStoreTests/WhereTests.swift | 6 + README.md | 216 ++++++++++++++++--------- Sources/AnyCoreStoreKeyPath.swift | 100 ------------ Sources/CoreStoreObject+Querying.swift | 108 ++++--------- Sources/DynamicKeyPath.swift | 193 ++++++++++++++++++++++ Sources/OrderBy.swift | 20 +-- Sources/Relationship.swift | 18 +-- Sources/Transformable.swift | 12 +- Sources/Value.swift | 12 +- 11 files changed, 407 insertions(+), 318 deletions(-) delete mode 100644 Sources/AnyCoreStoreKeyPath.swift create mode 100644 Sources/DynamicKeyPath.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 8308442..480a76c 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -532,10 +532,10 @@ B5CA2B091F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; B5CA2B0A1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; B5CA2B0B1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */; }; - B5CA2B121F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */; }; - B5CA2B131F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */; }; - B5CA2B141F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */; }; - B5CA2B151F81DBFF004B1936 /* AnyCoreStoreKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */; }; + B5CA2B121F81DBFE004B1936 /* DynamicKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* DynamicKeyPath.swift */; }; + B5CA2B131F81DBFE004B1936 /* DynamicKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* DynamicKeyPath.swift */; }; + B5CA2B141F81DBFE004B1936 /* DynamicKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* DynamicKeyPath.swift */; }; + B5CA2B151F81DBFF004B1936 /* DynamicKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CA2B111F81DBFE004B1936 /* DynamicKeyPath.swift */; }; B5D1E22C19FA9FBC003B2874 /* CoreStoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */; }; B5D339B41E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; B5D339B51E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; @@ -881,7 +881,7 @@ B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = ""; }; B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreFetchedResultsController.swift; sourceTree = ""; }; B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhereClauseType.swift; sourceTree = ""; }; - B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCoreStoreKeyPath.swift; sourceTree = ""; }; + B5CA2B111F81DBFE004B1936 /* DynamicKeyPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicKeyPath.swift; sourceTree = ""; }; B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreError.swift; sourceTree = ""; }; B5D2D5A91F7558CB00A4DE67 /* .cocoapods.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .cocoapods.yml; sourceTree = SOURCE_ROOT; }; B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicModelTests.swift; sourceTree = ""; }; @@ -1323,7 +1323,7 @@ children = ( B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */, B5D339EB1E9495E500C880DE /* CoreStoreObject+Querying.swift */, - B5CA2B111F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift */, + B5CA2B111F81DBFE004B1936 /* DynamicKeyPath.swift */, ); name = "KeyPath Utilities"; sourceTree = ""; @@ -2004,7 +2004,7 @@ B549F65E1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B5E84F211AFF84860064E85B /* CoreStore+Observing.swift in Sources */, B559CD431CAA8B6300E4D58B /* CSSetupResult.swift in Sources */, - B5CA2B121F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */, + B5CA2B121F81DBFE004B1936 /* DynamicKeyPath.swift in Sources */, B5A991EC1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5FE4DA71C84FB4400FA6A91 /* InMemoryStore.swift in Sources */, B52F743D1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, @@ -2200,7 +2200,7 @@ B549F65F1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B559CD451CAA8B6300E4D58B /* CSSetupResult.swift in Sources */, 82BA18B81C4BBD4200A0916E /* TypeErasedClauses.swift in Sources */, - B5CA2B131F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */, + B5CA2B131F81DBFE004B1936 /* DynamicKeyPath.swift in Sources */, B5A991ED1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5ECDBEE1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, @@ -2396,7 +2396,7 @@ B549F6611E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B52DD19B1BE1F92800949AFE /* CoreStoreLogger.swift in Sources */, B52DD1991BE1F92800949AFE /* DefaultLogger.swift in Sources */, - B5CA2B151F81DBFF004B1936 /* AnyCoreStoreKeyPath.swift in Sources */, + B5CA2B151F81DBFF004B1936 /* DynamicKeyPath.swift in Sources */, B5A991EF1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5220E201D130813009BC71E /* CSObjectMonitor.swift in Sources */, B52F74401E9B8724005F3DAC /* DynamicSchema.swift in Sources */, @@ -2592,7 +2592,7 @@ B549F6601E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, B559CD461CAA8B6300E4D58B /* CSSetupResult.swift in Sources */, B56321A61BD65216006C9394 /* MigrationType.swift in Sources */, - B5CA2B141F81DBFE004B1936 /* AnyCoreStoreKeyPath.swift in Sources */, + B5CA2B141F81DBFE004B1936 /* DynamicKeyPath.swift in Sources */, B5A991EE1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5ECDBEF1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743F1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index 197cd7c..8cadd78 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -101,8 +101,8 @@ class Person: CoreStoreObject { static func keyPathsAffectingDisplayName() -> Set { return [ - self.keyPath({ $0.title }), - self.keyPath({ $0.name }) + String(keyPath: \Person.title), + String(keyPath: \Person.name) ] } } @@ -112,7 +112,8 @@ class Person: CoreStoreObject { class DynamicModelTests: BaseTestDataTestCase { - func testDynamicModels_CanBeDeclaredCorrectly() { + @objc + dynamic func test_ThatDynamicModels_CanBeDeclaredCorrectly() { let dataStack = DataStack( CoreStoreSchema( @@ -131,13 +132,13 @@ class DynamicModelTests: BaseTestDataTestCase { ) self.prepareStack(dataStack, configurations: [nil]) { (stack) in - let k1 = Animal.keyPath({ $0.species }) + let k1 = String(keyPath: \Animal.species) XCTAssertEqual(k1, "species") - let k2 = Dog.keyPath({ $0.species }) + let k2 = String(keyPath: \Dog.species) XCTAssertEqual(k2, "species") - let k3 = Dog.keyPath({ $0.nickname }) + let k3 = String(keyPath: \Dog.nickname) XCTAssertEqual(k3, "nickname") let updateDone = self.expectation(description: "update-done") @@ -273,6 +274,13 @@ class DynamicModelTests: BaseTestDataTestCase { } } + @objc + dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() { + + XCTAssertEqual(String(keyPath: \Animal.species), "species") + XCTAssertEqual(String(keyPath: \Dog.species), "species") + } + @nonobjc func prepareStack(_ dataStack: DataStack, configurations: [ModelConfiguration] = [nil], _ closure: (_ dataStack: DataStack) -> Void) { diff --git a/CoreStoreTests/WhereTests.swift b/CoreStoreTests/WhereTests.swift index 11175f9..35540ab 100644 --- a/CoreStoreTests/WhereTests.swift +++ b/CoreStoreTests/WhereTests.swift @@ -52,6 +52,12 @@ private func XCTAssertAllEqual(_ whereClauses: [Where]) { final class WhereTests: XCTestCase { + @objc + dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() { + + XCTAssertEqual(String(keyPath: \TestEntity1.testEntityID), "testEntityID") + } + @objc dynamic func test_ThatWhereClauses_ConfigureCorrectly() { diff --git a/README.md b/README.md index e6aa319..8f25bc0 100644 --- a/README.md +++ b/README.md @@ -27,27 +27,26 @@ CoreStore is now part of the [Swift Source Compatibility projects](https://swift ## Why use CoreStore? -CoreStore is the answer to the [challenges](http://inessential.com/2010/02/26/on_switching_away_from_core_data) [of](http://bsktapp.com/blog/why-is-realm-great-and-why-are-we-not-using-it/) [using](https://www.quora.com/Why-would-you-use-Realm-over-Core-Data) [Core](http://sebastiandobrincu.com/blog/5-reasons-why-you-should-choose-realm-over-coredata) [Data](https://medium.com/the-way-north/ditching-core-data-865c1bb5564c#.a5h8ou6ri). CoreStore was (and is) heavily shaped by real-world needs of developing data-dependent apps. It enforces safe and convenient Core Data usage while letting you take advantage of the industry's encouraged best practices. ### Features -- **Tight design around Swift’s code elegance and type safety.** CoreStore fully utilizes Swift's community-driven language features. -- **Safer concurrency architecture.** CoreStore makes it hard to fall into common concurrency mistakes. The main `NSManagedObjectContext` is strictly read-only, while all updates are done through serial *transactions*. *(See [Saving and processing transactions](#saving-and-processing-transactions))* -- **Clean fetching and querying API.** Fetching objects is easy, but querying for raw aggregates (`min`, `max`, etc.) and raw property values is now just as convenient. *(See [Fetching and querying](#fetching-and-querying))* -- **Type-safe, easy to configure observers.** You don't have to deal with the burden of setting up `NSFetchedResultsController`s and KVO. As an added bonus, `ListMonitor`s and `ObjectMonitor`s can have multiple observers. This means you can have multiple view controllers efficiently share a single resource! *(See [Observing changes and notifications](#observing-changes-and-notifications))* -- **Efficient importing utilities.** Map your entities once with their corresponding import source (JSON for example), and importing from *transactions* becomes elegant. Uniquing is also done with an efficient find-and-replace algorithm. *(See [Importing data](#importing-data))* -- **Say goodbye to *.xcdatamodeld* files!** The new `CoreStoreObject` is *the* replacement to `NSManagedObject`. `CoreStoreObject` subclasses can declare type-safe properties all in Swift code, no need to maintain separate resource files for the models. As bonus, these special properties support custom types, and can be used to create type-safe keypaths and queries. *(See [Type-safe `CoreStoreObject`s](#type-safe-corestoreobjects))* -- **Progressive migrations.** No need to think how to migrate from all previous model versions to your latest model. Just tell the `DataStack` the sequence of version strings (`MigrationChain`s) and CoreStore will automatically use progressive migrations when needed. *(See [Migrations](#migrations))* +- **💎Tight design around Swift’s code elegance and type safety.** CoreStore fully utilizes Swift's community-driven language features. +- **🚦Safer concurrency architecture.** CoreStore makes it hard to fall into common concurrency mistakes. The main `NSManagedObjectContext` is strictly read-only, while all updates are done through serial *transactions*. *(See [Saving and processing transactions](#saving-and-processing-transactions))* +- **🔍Clean fetching and querying API.** Fetching objects is easy, but querying for raw aggregates (`min`, `max`, etc.) and raw property values is now just as convenient. *(See [Fetching and querying](#fetching-and-querying))* +- **🔭Type-safe, easy to configure observers.** You don't have to deal with the burden of setting up `NSFetchedResultsController`s and KVO. As an added bonus, `ListMonitor`s and `ObjectMonitor`s can have multiple observers. This means you can have multiple view controllers efficiently share a single resource! *(See [Observing changes and notifications](#observing-changes-and-notifications))* +- **📥Efficient importing utilities.** Map your entities once with their corresponding import source (JSON for example), and importing from *transactions* becomes elegant. Uniquing is also done with an efficient find-and-replace algorithm. *(See [Importing data](#importing-data))* +- **🗑Say goodbye to *.xcdatamodeld* files!** The new `CoreStoreObject` is *the* replacement to `NSManagedObject`. `CoreStoreObject` subclasses can declare type-safe properties all in Swift code, no need to maintain separate resource files for the models. As bonus, these special properties support custom types, and can be used to create type-safe keypaths and queries. *(See [Type-safe `CoreStoreObject`s](#type-safe-corestoreobjects))* +- **🔗Progressive migrations.** No need to think how to migrate from all previous model versions to your latest model. Just tell the `DataStack` the sequence of version strings (`MigrationChain`s) and CoreStore will automatically use progressive migrations when needed. *(See [Migrations](#migrations))* - **Easier custom migrations.** Say goodbye to *.xcmappingmodel* files; CoreStore can now infer entity mappings when possible, while still allowing an easy way to write custom mappings. *(See [Migrations](#migrations))* -- **Plug-in your own logging framework.** Although a default logger is built-in, all logging, asserting, and error reporting can be funneled to `CoreStoreLogger` protocol implementations. *(See [Logging and error reporting](#logging-and-error-reporting))* -- **Heavy support for multiple persistent stores per data stack.** CoreStore lets you manage separate stores in a single `DataStack`, just the way *.xcdatamodeld* configurations are designed to. CoreStore will also manage one stack by default, but you can create and manage as many as you need. *(See [Setting up](#setting-up))* -- **Free to name entities and their class names independently.** CoreStore gets around a restriction with other Core Data wrappers where the entity name should be the same as the `NSManagedObject` subclass name. CoreStore loads entity-to-class mappings from the managed object model file, so you can assign different names for the entities and their class names. -- **Full Documentation.** No magic here; all public classes, functions, properties, etc. have detailed *Apple Docs*. This *README* also introduces a lot of concepts and explains a lot of CoreStore's behavior. -- **Informative (and pretty) logs.** All CoreStore and Core Data-related types now have very informative and pretty print outputs! *(See [Logging and error reporting](#logging-and-error-reporting))* -- **Objective-C support!** Is your project transitioning from Objective-C to Swift but still can't quite fully convert some huge classes to Swift yet? CoreStore adjusts to the ever-increasing Swift adoption. While still written in pure Swift, all CoreStore types have their corresponding Objective-C-visible "bridging classes". *(See [Objective-C support](#objective-c-support))* -- **More extensive Unit Tests.** Extending CoreStore is safe without having to worry about breaking old behavior. +- **📝Plug-in your own logging framework.** Although a default logger is built-in, all logging, asserting, and error reporting can be funneled to `CoreStoreLogger` protocol implementations. *(See [Logging and error reporting](#logging-and-error-reporting))* +- **⛓Heavy support for multiple persistent stores per data stack.** CoreStore lets you manage separate stores in a single `DataStack`, just the way *.xcdatamodeld* configurations are designed to. CoreStore will also manage one stack by default, but you can create and manage as many as you need. *(See [Setting up](#setting-up))* +- **🎯Free to name entities and their class names independently.** CoreStore gets around a restriction with other Core Data wrappers where the entity name should be the same as the `NSManagedObject` subclass name. CoreStore loads entity-to-class mappings from the managed object model file, so you can assign different names for the entities and their class names. +- **📙Full Documentation.** No magic here; all public classes, functions, properties, etc. have detailed *Apple Docs*. This *README* also introduces a lot of concepts and explains a lot of CoreStore's behavior. +- **ℹ️Informative (and pretty) logs.** All CoreStore and Core Data-related types now have very informative and pretty print outputs! *(See [Logging and error reporting](#logging-and-error-reporting))* +- **🎗Objective-C support!** Is your project transitioning from Objective-C to Swift but still can't quite fully convert some huge classes to Swift yet? CoreStore adjusts to the ever-increasing Swift adoption. While still written in pure Swift, all CoreStore types have their corresponding Objective-C-visible "bridging classes". *(See [Objective-C support](#objective-c-support))* +- **🛡More extensive Unit Tests.** Extending CoreStore is safe without having to worry about breaking old behavior. *Have ideas that may benefit other Core Data users? [Feature Request](https://github.com/JohnEstropia/CoreStore/issues)s are welcome!* @@ -86,7 +85,7 @@ CoreStore was (and is) heavily shaped by real-world needs of developing data-dep - [`Select` clause](#selectt-clause) - [`GroupBy` clause](#groupby-clause) - [Logging and error reporting](#logging-and-error-reporting) - - [Observing changes and notifications](#observing-changes-and-notifications) (unavailable on macOS) + - [Observing changes and notifications](#observing-changes-and-notifications) - [Observe a single object](#observe-a-single-object) - [Observe a list of objects](#observe-a-list-of-objects) - [Objective-C support](#objective-c-support) @@ -226,8 +225,8 @@ let migrationProgress = dataStack.addStorage( CoreStore.defaultStack = dataStack // pass the dataStack to CoreStore for easier access later on ``` -(If you have never heard of "Configurations", you'll find them in your *.xcdatamodeld* file) -xcode configurations screenshot +> 💡If you have never heard of "Configurations", you'll find them in your *.xcdatamodeld* file +> xcode configurations screenshot In our sample code above, note that you don't need to do the `CoreStore.defaultStack = dataStack` line. You can just as well hold a reference to the `DataStack` like below and call all its instance methods directly: ```swift @@ -260,12 +259,14 @@ class MyViewController: UIViewController { } } func methodToBeCalledLaterOn() { - let objects = CoreStore.fetchAll(From(MyEntity)) + let objects = CoreStore.fetchAll(From()) print(objects) } } ``` +> 💡By default, CoreStore will initialize `NSManagedObject`s from *.xcdatamodeld* files, but you can create models completely from source code using `CoreStoreObject`s and `CoreStoreSchema`. To use this feature, refer to [Type-safe `CoreStoreObject`s](#type-safe-corestoreobjects). + Notice that in our previous examples, `addStorageAndWait(_:)` and `addStorage(_:completion:)` both accept either `InMemoryStore`, `SQLiteStore`, or `ICloudStore`. These implement the `StorageInterface` protocol. ### In-memory store @@ -311,7 +312,7 @@ public protocol LocalStorage: StorageInterface { If you have custom `NSIncrementalStore` or `NSAtomicStore` subclasses, you can implement this protocol and use it similarly to `SQLiteStore`. ### iCloud Store -> **Important:** The iCloud Store is currently in beta. Please use with caution. If you have any concerns please do send me a message on [Twitter](https://twitter.com/JohnEstropia) or on the [CoreStore Slack Team](http://swift-corestore-slack.herokuapp.com/) +> ⚠️**Important:** The iCloud Store is being deprecated. Please use with caution. If you have any concerns please do send me a message on [Twitter](https://twitter.com/JohnEstropia) or on the [CoreStore Slack Team](http://swift-corestore-slack.herokuapp.com/) As a counterpart to `LocalStorage`, the `CloudStorage` protocol abstracts stores managed in the cloud. CoreStore currently provides the concrete class `ICloudStore`. Unlike `InMemoryStore` and `SQLiteStore` though, the `ICloudStore`'s initializer may return `nil` if the iCloud container could not be located or if iCloud is not available on the device: ```swift @@ -550,14 +551,14 @@ This closure is executed on the main thread so UIKit and AppKit calls can be don ### Progressive migrations -By default, CoreStore uses Core Data's default automatic migration mechanism. In other words, CoreStore will try to migrate the existing persistent store to the *.xcdatamodeld* file's current model version. If no mapping model is found from the store's version to the data model's version, CoreStore gives up and reports an error. +By default, CoreStore uses Core Data's default automatic migration mechanism. In other words, CoreStore will try to migrate the existing persistent store until it matches the `SchemaHistory`'s `currentModelVersion`. If no mapping model path is found from the store's version to the data model's version, CoreStore gives up and reports an error. The `DataStack` lets you specify hints on how to break a migration into several sub-migrations using a `MigrationChain`. This is typically passed to the `DataStack` initializer and will be applied to all stores added to the `DataStack` with `addSQLiteStore(...)` and its variants: ```swift let dataStack = DataStack(migrationChain: ["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"]) ``` -The most common usage is to pass in the *.xcdatamodeld* version names in increasing order as above. +The most common usage is to pass in the model version (*.xcdatamodeld* version names for `NSManagedObject`s, or the `modelName` for `CoreStoreSchema`s) in increasing order as above. For more complex, non-linear migration paths, you can also pass in a version tree that maps the key-values to the source-destination versions: ```swift @@ -582,7 +583,7 @@ The `MigrationChain` is validated when passed to the `DataStack` and unless it i - a version appears twice as a key in a dictionary literal - a loop is found in any of the paths -One important thing to remember is that **if a `MigrationChain` is specified, the *.xcdatamodeld*'s "Current Version" will be bypassed** and the `MigrationChain`'s leafmost version will be the `DataStack`'s base model version. +> ⚠️**Important: If a `MigrationChain` is specified, the *.xcdatamodeld*'s "Current Version" will be bypassed** and the `MigrationChain`'s leafmost version will be the `DataStack`'s base model version. ### Forecasting migrations @@ -717,7 +718,7 @@ dataStack.perform( } ) ``` -Never use `try?` or `try!` on a `transaction.cancel()` call. Always use `try`. Using `try?` will swallow the cancellation and the transaction will proceed to save as normal. Using `try!` will crash the app as `transaction.cancel()` will *always* throw an error. +> ⚠️**Important:** Never use `try?` or `try!` on a `transaction.cancel()` call. Always use `try`. Using `try?` will swallow the cancellation and the transaction will proceed to save as normal. Using `try!` will crash the app as `transaction.cancel()` will *always* throw an error. The examples above use `perform(asynchronous:...)`, but there are actually 3 types of transactions at your disposal: *asynchronous*, *synchronous*, and *unsafe*. @@ -754,6 +755,9 @@ CoreStore.perform( } ) ``` + +> ⚠️Be careful when returning `NSManagedObject`s or `CoreStoreObject`s from the transaction closure. Those instances are for the transaction's use only. See [Passing objects safely](#passing-objects-safely). + Transactions created from `perform(asynchronous:...)` are instances of `AsynchronousDataTransaction`. #### Synchronous transactions @@ -770,6 +774,21 @@ let hasChanges = CoreStore.perform( Since `perform(synchronous:...)` technically blocks two queues (the caller's queue and the transaction's background queue), it is considered less safe as it's more prone to deadlock. Take special care that the closure does not block on any other external queues. +By default, `perform(synchronous:...)` will wait for observers such as `ListMonitor`s to be notified before the method returns. This may cause deadlocks, especially if you are calling this from the main thread. To reduce this risk, you may try to set the `waitForAllObservers:` parameter to `false`. Doing so tells the `SynchronousDataTransaction` to block only until it completes saving. It will not wait for other context's to receive those changes. This reduces deadlock risk but may have surprising side-effects: +```swift +CoreStore.perform( + synchronous: { (transaction) in + let person = transaction.create(Into()) + person.name = "John" + }, + waitForAllObservers: false +) +let newPerson = CoreStore.fetchOne(From.where(\.name == "John")) +// newPerson may be nil! +// The DataStack may have not yet received the update notification. +``` +Due to this complicated nature of synchronous transactions, if your app has very heavy transaction throughput it is highly recommended to use [asynchronous transactions](#asynchronous-transactions) instead. + #### Unsafe transactions are special in that they do not enclose updates within a closure: ```swift @@ -802,8 +821,8 @@ let person = transaction.create(Into()) While the syntax is straightforward, CoreStore does not just naively insert a new object. This single line does the following: - Checks that the entity type exists in any of the transaction's parent persistent store - If the entity belongs to only one persistent store, a new object is inserted into that store and returned from `create(...)` -- If the entity does not belong to any store, an assert will be triggered. **This is a programmer error and should never occur in production code.** -- If the entity belongs to multiple stores, an assert will be triggered. **This is also a programmer error and should never occur in production code.** Normally, with Core Data you can insert an object in this state but saving the `NSManagedObjectContext` will always fail. CoreStore checks this for you at creation time when it makes sense (not during save). +- If the entity does not belong to any store, an assertion failure will be raised. **This is a programmer error and should never occur in production code.** +- If the entity belongs to multiple stores, an assertion failure will be raised. **This is also a programmer error and should never occur in production code.** Normally, with Core Data you can insert an object in this state but saving the `NSManagedObjectContext` will always fail. CoreStore checks this for you at creation time when it makes sense (not during save). If the entity exists in multiple configurations, you need to provide the configuration name for the destination persistent store: @@ -833,8 +852,8 @@ To update an existing object, fetch the object's instance from the transaction: CoreStore.perform( asynchronous: { (transaction) -> Void in let person = transaction.fetchOne( - From(), - Where("name", isEqualTo: "Jane Smith") + From() + .where(\.name == "Jane Smith") ) person.age = person.age + 1 }, @@ -905,8 +924,8 @@ If you do not have references yet to the objects to be deleted, transactions hav CoreStore.perform( asynchronous: { (transaction) -> Void in transaction.deleteAll( - From(), - Where("age > 30") + From() + .where(\.age > 30) ) }, completion: { _ in } @@ -953,8 +972,8 @@ var peopleIDs: [NSManagedObjectID] = // ... CoreStore.perform( asynchronous: { (transaction) -> Void in let jane = transaction.fetchOne( - From(), - Where("name", isEqualTo: "Jane Smith") + From() + .where(\.name == "Jane Smith") ) jane.friends = NSSet(array: transaction.fetchExisting(peopleIDs)!) // ... @@ -999,7 +1018,7 @@ typealias ImportSource = [String: Any] ```swift typealias ImportSource = NSData ``` -You can even use external types from popular 3rd-party JSON libraries ([SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)'s `JSON` type is a personal favorite), or just simple tuples or primitives. +You can even use external types from popular 3rd-party JSON libraries, or just simple tuples or primitives. #### `ImportableObject` `ImportableObject` is a very simple protocol: @@ -1197,11 +1216,11 @@ The `Where` clause is CoreStore's `NSPredicate` wrapper. It specifies the search ```swift var people = CoreStore.fetchAll( From(), - Where("%K > %d", "age", 30) // string format initializer + Where("%K > %d", "age", 30) // string format initializer ) people = CoreStore.fetchAll( From(), - Where(true) // boolean initializer + Where(true) // boolean initializer ) ``` If you do have an existing `NSPredicate` instance already, you can pass that to `Where` as well: @@ -1209,14 +1228,21 @@ If you do have an existing `NSPredicate` instance already, you can pass that to let predicate = NSPredicate(...) var people = CoreStore.fetchAll( From(), - Where(predicate) // predicate initializer + Where(predicate) // predicate initializer +) +``` +⭐️Starting CoreStore 5.0, `Where` clauses became more type-safe and are now generic types. To avoid verbose repetition of the generic object type, fetch methods now support **Fetch Chain builders**. We can also use Swift's Smart KeyPaths as the `Where` clause expression: +```swift +var people = CoreStore.fetchAll( + From() + .where(\.age > 30) // Type-safe! ) ``` `Where` clauses also implement the `&&`, `||`, and `!` logic operators, so you can provide logical conditions without writing too much `AND`, `OR`, and `NOT` strings: ```swift var people = CoreStore.fetchAll( - From(), - Where("age > %d", 30) && Where("gender == %@", "M") + From() + .where(\.age > 30 && \.gender == "M") ) ``` If you do not provide a `Where` clause, all objects that belong to the specified `From` will be returned. @@ -1227,16 +1253,23 @@ The `OrderBy` clause is CoreStore's `NSSortDescriptor` wrapper. Use it to specif ```swift var mostValuablePeople = CoreStore.fetchAll( From(), - OrderBy(.descending("rating"), .ascending("surname")) + OrderBy(.descending("rating"), .ascending("surname")) ) ``` As seen above, `OrderBy` accepts a list of `SortKey` enumeration values, which can be either `.ascending` or `.descending`. +⭐️As with `Where` clauses, CoreStore 5.0 turned `OrderBy` clauses into generic types. To avoid verbose repetition of the generic object type, fetch methods now support **Fetch Chain builders**. We can also use Swift's Smart KeyPaths as the `OrderBy` clause expression: +```swift +var people = CoreStore.fetchAll( + From() + .orderBy(.descending(\.rating), .ascending(\.surname)) // Type-safe! +) +``` You can use the `+` and `+=` operator to append `OrderBy`s together. This is useful when sorting conditionally: ```swift -var orderBy = OrderBy(.descending("rating")) +var orderBy = OrderBy(.descending(\.rating)) if sortFromYoungest { - orderBy += OrderBy(.ascending("age")) + orderBy += OrderBy(.ascending(\.age)) } var mostValuablePeople = CoreStore.fetchAll( From(), @@ -1250,8 +1283,8 @@ The `Tweak` clause lets you, uh, *tweak* the fetch (or query). `Tweak` exposes t ```swift var people = CoreStore.fetchAll( From(), - Where("age > %d", 30), - OrderBy(.ascending("surname")), + Where("age > %d", 30), + OrderBy(.ascending("surname")), Tweak { (fetchRequest) -> Void in fetchRequest.includesPendingChanges = false fetchRequest.returnsObjectsAsFaults = false @@ -1259,6 +1292,19 @@ var people = CoreStore.fetchAll( } ) ``` +`Tweak` also supports **Fetch Chain builders**: +```swift +var people = CoreStore.fetchAll( + From(), + .where(\.age > 30) + .orderBy(.ascending(\.surname)) + .tweak { + $0.includesPendingChanges = false + $0.returnsObjectsAsFaults = false + $0.includesSubentities = false + } +) +``` The clauses are evaluated the order they appear in the fetch/query, so you typically need to set `Tweak` as the last clause. `Tweak`'s closure is executed only just before the fetch occurs, so make sure that any values captured by the closure is not prone to race conditions. @@ -1294,6 +1340,14 @@ let allAges = CoreStore.queryAttributes( Select("age") ) ``` +⭐️Starting CoreStore 5.0, query methods now support **Query Chain builders**. We can also use Swift's Smart KeyPaths to use in the expressions: +```swift +let johnsAge = CoreStore.queryValue( + From() + .select(\.age) // binds the result to Int + .where(\.name == "John Smith") +) +``` If you only need a value for a particular attribute, you can just specify the key name (like we did with `Select("age")`), but several aggregate functions can also be used as parameter to `Select`: - `.average(...)` @@ -1380,6 +1434,14 @@ let personJSON = CoreStore.queryAttributes( GroupBy("age") ) ``` +⭐️Starting CoreStore 5.0, `GroupBy` clauses are now also generic types and now support **Query Chain builders**. We can also use Swift's Smart KeyPaths to use in the expressions: +```swift +let personJSON = CoreStore.queryAttributes( + From() + .select(.attribute(\.age), .count(\.age, as: "count")) + .groupBy(\.age) +) +``` this returns dictionaries that shows the count for each `"age"`: ```swift [ @@ -1428,7 +1490,9 @@ A couple of examples, `ListMonitor`: These are all implemented with `CustomDebugStringConvertible.debugDescription`, so they work with lldb's `po` command as well. -## Observing changes and notifications (unavailable on macOS) +## Observing changes and notifications +> (unavailable on macOS versions below 10.12) + CoreStore provides type-safe wrappers for observing managed objects: - `ObjectMonitor`: use to monitor changes to a single `NSManagedObject` or `CoreStoreObject` instance (instead of Key-Value Observing) @@ -1503,12 +1567,10 @@ Including `ListObserver`, there are 3 observer protocols you can implement depen We then need to create a `ListMonitor` instance and register our `ListObserver` as an observer: ```swift self.monitor = CoreStore.monitorList( - From(), - Where("age > 30"), - OrderBy(.ascending("name")), - Tweak { (fetchRequest) -> Void in - fetchRequest.fetchBatchSize = 20 - } + From() + .where(\.age > 30) + .orderBy(.ascending(\.name)) + .tweak { $0.fetchBatchSize = 20 } ) self.monitor.addObserver(self) ``` @@ -1524,13 +1586,11 @@ let firstPerson = self.monitor[0] If the list needs to be grouped into sections, create the `ListMonitor` instance with the `monitorSectionedList(...)` method and a `SectionBy` clause: ```swift self.monitor = CoreStore.monitorSectionedList( - From(), - SectionBy("age"), - Where("gender", isEqualTo: "M"), - OrderBy(.ascending("age"), .ascending("name")), - Tweak { (fetchRequest) -> Void in - fetchRequest.fetchBatchSize = 20 - } + From() + .sectionBy(\.age) + .where(\.gender == "M") + .orderBy(.ascending(\.age), .ascending(\.name)) + .tweak { $0.fetchBatchSize = 20 } ) ``` A list controller created this way will group the objects by the attribute key indicated by the `SectionBy` clause. One more thing to remember is that the `OrderBy` clause should sort the list in such a way that the `SectionBy` attribute would be sorted together (a requirement shared by `NSFetchedResultsController`.) @@ -1538,11 +1598,11 @@ A list controller created this way will group the objects by the attribute key i The `SectionBy` clause can also be passed a closure to transform the section name into a displayable string: ```swift self.monitor = CoreStore.monitorSectionedList( - From(), - SectionBy("age") { (sectionName) -> String? in - "\(sectionName) years old" - }, - OrderBy(.ascending("age"), .ascending("name")) + From() + .sectionBy(\.age) { (sectionName) -> String? in + "\(sectionName) years old" + } + .orderBy(.ascending(\.age), .ascending(\.name)) ) ``` This is useful when implementing a `UITableViewDelegate`'s section header: @@ -1562,9 +1622,9 @@ let person2 = self.monitor[1, 2] ``` ## Objective-C support -CoreStore 2.0 was a big move to address the large number of apps starting to convert from Objective-C to Swift. The basic problem is this: The cost of converting all code base to Swift is very big, so most apps are forced to do undergo a *transitional* ObjC-Swift hybrid phase. This used to mean that these apps could not use the Swifty-est libraries out there yet, or that they may have to write their own bridging methods just to make new code usable in their old Objective-C code. +> ⚠️Objective-C support is planned to be deprecated in a future CoreStore version. -With 2.0, all CoreStore types are still written in pure Swift, but they now have Objective-C "bridging classes" that are visible to Objective-C code. To show a couple of usage examples: +All CoreStore types are still written in pure Swift, but most core types have Objective-C "bridging classes" that are visible to Objective-C code. To show a couple of usage examples: @@ -1612,7 +1672,7 @@ All of these `CS`-prefixed bridging classes have very similar usage to the exist For example, you may have a new, modern Swift class that holds a `ListMonitor`: ```swift class MyViewController: UIViewController { - let monitor = CoreStore.monitorList(From(MyEntity), ...) + let monitor = CoreStore.monitorList(From(), ...) // ... } ``` @@ -1626,7 +1686,7 @@ Now let's say you have a legacy Objective-C class that previously uses `NSFetche When you need to instantiate this class from Swift, you just call `bridgeToObjectiveC`: ```swift class MyViewController: UIViewController { - let monitor = CoreStore.monitorList(From(MyEntity), ...) + let monitor = CoreStore.monitorList(From(), ...) func showOldController() { let controller = MYOldViewController(monitor: self.monitor.bridgeToObjectiveC) self.presentViewController(controller, animated: true, completion: nil) @@ -1688,14 +1748,14 @@ The property names to be saved to Core Data is specified as the `keyPath` argume ```swift class Person: CoreStoreObject { private let _name = Value.Required("name", initial: "") - // ... + // note property name is independent of the storage key name } ``` Here we added an underscore to the property name and made it `private`, but the underlying key-path `"name"` was unchanged so our model will not trigger a data migration. -> **Important:** As a rule, CoreStore can only process *stored properties*. Computed, `static`, `weak`, or `lazy` properties will not be added to the store. It is also strictly advised use `let` instead of `var` to declare these properties, as any changes to the schema after declaration is not allowed. +> ⚠️**Important:** As a rule, CoreStore can only process *stored properties*. Computed, `static`, `weak`, or `lazy` properties will be ignored and will not be added to the store. It is also strictly advised use `let` instead of `var` to declare these properties, as any changes to the property value will break the schema. -Also note how `Relationship`s are linked statically with the `inverse:` argument. All relationships are required to have an "inverse" relationship. Unfortunately, due to Swift compiler limitation we can only declare the `inverse:` on one end of the relationship-pair. +Also note how `Relationship`s are linked statically with the `inverse:` argument. **All relationships are required to have an "inverse" relationship**. Unfortunately, due to Swift compiler limitation we can only declare the `inverse:` on one end of the relationship-pair. To tell the `DataStack` about these types, add all `CoreStoreObject`s' entities to a `CoreStoreSchema`: ```swift @@ -1737,9 +1797,9 @@ let keyPath: String = Dog.keyPath { $0.nickname } as well as `Where` and `OrderBy` clauses ```swift let puppies = CoreStore.fetchAll( - From(), - Dog.where { $0.age < 1 }, - Dog.ascending { $0.age } + From() + .where(\.age < 1) + .orderBy(.ascending(\.age)) ) ``` @@ -1749,7 +1809,7 @@ All CoreStore APIs that are usable with `NSManagedObject`s are also available fo While it is convenient to be able to declare entities only in code, it is worrying that we might accidentally change the `CoreStoreObject`'s properties and break our users' model version history. For this, the `CoreStoreSchema` allows us to "lock" our properties to a particular configuration. Any changes to that `VersionLock` will raise an assertion failure during the `CoreStoreSchema` initialization, so you can then look for the commit which changed the `VersionLock` hash. -To use `VersionLock`s, create the `CoreStoreSchema`, run the app, and look for this particular log message that is automatically printed to the console: +To use `VersionLock`s, create the `CoreStoreSchema`, run the app, and look for a similar log message that is automatically printed to the console: VersionLock @@ -1782,7 +1842,7 @@ Once the version lock is set, any changes in the properties or to the model will # Installation - Requires: - iOS 8 SDK and above - - Swift 3.1 (Xcode 8.3.2+) + - Swift 4 (Xcode 9+) - Dependencies: - *None* - Other notes: @@ -1791,7 +1851,7 @@ Once the version lock is set, any changes in the properties or to the model will ### Install with CocoaPods In your `Podfile`, add ``` -pod 'CoreStore', '~> 4.0' +pod 'CoreStore', '~> 5.0' ``` and run ``` @@ -1802,7 +1862,7 @@ This installs CoreStore as a framework. Declare `import CoreStore` in your swift ### Install with Carthage In your `Cartfile`, add ``` -github "JohnEstropia/CoreStore" >= 4.0.0 +github "JohnEstropia/CoreStore" >= 5.0.0 ``` and run ``` @@ -1833,9 +1893,7 @@ For the full Changelog, refer to the [Releases](https://github.com/JohnEstropia/ # Contact -Questions? Suggestions? - -Reach me on Twitter [@JohnEstropia](https://twitter.com/JohnEstropia) +You can reach me on Twitter [@JohnEstropia](https://twitter.com/JohnEstropia) or join our Slack team at [swift-corestore.slack.com](http://swift-corestore-slack.herokuapp.com/) diff --git a/Sources/AnyCoreStoreKeyPath.swift b/Sources/AnyCoreStoreKeyPath.swift deleted file mode 100644 index eb86752..0000000 --- a/Sources/AnyCoreStoreKeyPath.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// AnyCoreStoreKeyPath.swift -// CoreStore -// -// Copyright © 2017 John Rommel Estropia -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -import Foundation - - -// MARK: - AnyCoreStoreKeyPath - -public protocol AnyCoreStoreKeyPath { - - var cs_keyPathString: String { get } -} - -// SE-0143 is not implemented: https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md -//extension KeyPath: AnyCoreStoreKeyPath where Root: NSManagedObject, Value: ImportableAttributeType { -// -// public var cs_keyPathString: String { -// -// return self._kvcKeyPathString! -// } -//} - -extension ValueContainer.Required: AnyCoreStoreKeyPath { - - public var cs_keyPathString: String { - - return self.keyPath - } -} - -extension ValueContainer.Optional: AnyCoreStoreKeyPath { - - public var cs_keyPathString: String { - - return self.keyPath - } -} - -extension TransformableContainer.Required: AnyCoreStoreKeyPath { - - public var cs_keyPathString: String { - - return self.keyPath - } -} - -extension TransformableContainer.Optional: AnyCoreStoreKeyPath { - - public var cs_keyPathString: String { - - return self.keyPath - } -} - -extension RelationshipContainer.ToOne: AnyCoreStoreKeyPath { - - public var cs_keyPathString: String { - - return self.keyPath - } -} - -extension RelationshipContainer.ToManyOrdered: AnyCoreStoreKeyPath { - - public var cs_keyPathString: String { - - return self.keyPath - } -} - -extension RelationshipContainer.ToManyUnordered: AnyCoreStoreKeyPath { - - public var cs_keyPathString: String { - - return self.keyPath - } -} - diff --git a/Sources/CoreStoreObject+Querying.swift b/Sources/CoreStoreObject+Querying.swift index 1f28aee..52807e4 100644 --- a/Sources/CoreStoreObject+Querying.swift +++ b/Sources/CoreStoreObject+Querying.swift @@ -27,67 +27,6 @@ import CoreData import Foundation -// MARK: - DynamicObject - -public extension DynamicObject where Self: CoreStoreObject { - - /** - Extracts the keyPath string from a `CoreStoreObject.Value` property. - ``` - let keyPath: String = Person.keyPath { $0.nickname } - ``` - */ - public static func keyPath(_ attribute: (Self) -> ValueContainer.Required) -> String { - - return attribute(self.meta).keyPath - } - - /** - Extracts the keyPath string from a `CoreStoreObject.Value` property. - ``` - let keyPath: String = Person.keyPath { $0.nickname } - ``` - */ - public static func keyPath(_ attribute: (Self) -> ValueContainer.Optional) -> String { - - return attribute(self.meta).keyPath - } - - /** - Extracts the keyPath string from a `CoreStoreObject.Relationship` property. - ``` - let keyPath: String = Person.keyPath { $0.pets } - ``` - */ - public static func keyPath(_ relationship: (Self) -> RelationshipContainer.ToOne) -> String { - - return relationship(self.meta).keyPath - } - - /** - Extracts the keyPath string from a `CoreStoreObject.Relationship` property. - ``` - let keyPath: String = Person.keyPath { $0.pets } - ``` - */ - public static func keyPath(_ relationship: (Self) -> RelationshipContainer.ToManyOrdered) -> String { - - return relationship(self.meta).keyPath - } - - /** - Extracts the keyPath string from a `CoreStoreObject.Relationship` property. - ``` - let keyPath: String = Person.keyPath { $0.pets } - ``` - */ - public static func keyPath(_ relationship: (Self) -> RelationshipContainer.ToManyUnordered) -> String { - - return relationship(self.meta).keyPath - } -} - - // MARK: - ValueContainer.Required public extension ValueContainer.Required { @@ -98,7 +37,6 @@ public extension ValueContainer.Required { let person = CoreStore.fetchOne(From().where({ $0.nickname == "John" })) ``` */ - @inline(__always) public static func == (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where(attribute.keyPath, isEqualTo: value) @@ -110,7 +48,6 @@ public extension ValueContainer.Required { let person = CoreStore.fetchOne(From().where({ $0.nickname != "John" })) ``` */ - @inline(__always) public static func != (_ attribute: ValueContainer.Required, _ value: V) -> Where { return !Where(attribute.keyPath, isEqualTo: value) @@ -122,7 +59,6 @@ public extension ValueContainer.Required { let person = CoreStore.fetchOne(From().where({ $0.age < 20 })) ``` */ - @inline(__always) public static func < (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where("%K < %@", attribute.keyPath, value) @@ -134,7 +70,6 @@ public extension ValueContainer.Required { let person = CoreStore.fetchOne(From().where({ $0.age > 20 })) ``` */ - @inline(__always) public static func > (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where("%K > %@", attribute.keyPath, value) @@ -146,7 +81,6 @@ public extension ValueContainer.Required { let person = CoreStore.fetchOne(From().where({ $0.age <= 20 })) ``` */ - @inline(__always) public static func <= (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where("%K <= %@", attribute.keyPath, value) @@ -158,7 +92,6 @@ public extension ValueContainer.Required { let person = CoreStore.fetchOne(From().where({ $0.age >= 20 })) ``` */ - @inline(__always) public static func >= (_ attribute: ValueContainer.Required, _ value: V) -> Where { return Where("%K >= %@", attribute.keyPath, value) @@ -170,7 +103,6 @@ public extension ValueContainer.Required { let dog = CoreStore.fetchOne(From().where({ ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname })) ``` */ - @inline(__always) public static func ~= (_ sequence: S, _ attribute: ValueContainer.Required) -> Where where S.Iterator.Element == V { return Where(attribute.keyPath, isMemberOf: sequence) @@ -188,7 +120,6 @@ public extension ValueContainer.Optional { let person = CoreStore.fetchOne(From().where({ $0.nickname == "John" })) ``` */ - @inline(__always) public static func == (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { return Where(attribute.keyPath, isEqualTo: value) @@ -200,7 +131,6 @@ public extension ValueContainer.Optional { let person = CoreStore.fetchOne(From().where({ $0.nickname != "John" })) ``` */ - @inline(__always) public static func != (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { return !Where(attribute.keyPath, isEqualTo: value) @@ -212,7 +142,6 @@ public extension ValueContainer.Optional { let person = CoreStore.fetchOne(From().where({ $0.age < 20 })) ``` */ - @inline(__always) public static func < (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { if let value = value { @@ -231,7 +160,6 @@ public extension ValueContainer.Optional { let person = CoreStore.fetchOne(From().where({ $0.age > 20 })) ``` */ - @inline(__always) public static func > (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { if let value = value { @@ -250,7 +178,6 @@ public extension ValueContainer.Optional { let person = CoreStore.fetchOne(From().where({ $0.age <= 20 })) ``` */ - @inline(__always) public static func <= (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { if let value = value { @@ -269,7 +196,6 @@ public extension ValueContainer.Optional { let person = CoreStore.fetchOne(From().where({ $0.age >= 20 })) ``` */ - @inline(__always) public static func >= (_ attribute: ValueContainer.Optional, _ value: V?) -> Where { if let value = value { @@ -288,7 +214,6 @@ public extension ValueContainer.Optional { let dog = CoreStore.fetchOne(From().where({ ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname })) ``` */ - @inline(__always) public static func ~= (_ sequence: S, _ attribute: ValueContainer.Optional) -> Where where S.Iterator.Element == V { return Where(attribute.keyPath, isMemberOf: sequence) @@ -306,7 +231,6 @@ public extension RelationshipContainer.ToOne { let dog = CoreStore.fetchOne(From().where({ $0.master == me })) ``` */ - @inline(__always) public static func == (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { return Where(relationship.keyPath, isEqualTo: object) @@ -318,7 +242,6 @@ public extension RelationshipContainer.ToOne { let dog = CoreStore.fetchOne(From().where({ $0.master != me })) ``` */ - @inline(__always) public static func != (_ relationship: RelationshipContainer.ToOne, _ object: D?) -> Where { return !Where(relationship.keyPath, isEqualTo: object) @@ -330,7 +253,6 @@ public extension RelationshipContainer.ToOne { let dog = CoreStore.fetchOne(From().where({ [john, joe, bob] ~= $0.master })) ``` */ - @inline(__always) public static func ~= (_ sequence: S, _ relationship: RelationshipContainer.ToOne) -> Where where S.Iterator.Element == D { return Where(relationship.keyPath, isMemberOf: sequence) @@ -342,6 +264,36 @@ public extension RelationshipContainer.ToOne { extension DynamicObject where Self: CoreStoreObject { + @available(*, deprecated, message: "Use the String(keyPath:) initializer and pass the KeyPath: String(keyPath: \\Person.name)") + public static func keyPath(_ attribute: (Self) -> ValueContainer.Required) -> String { + + return attribute(self.meta).keyPath + } + + @available(*, deprecated, message: "Use the String(keyPath:) initializer and pass the KeyPath: String(keyPath: \\Person.name)") + public static func keyPath(_ attribute: (Self) -> ValueContainer.Optional) -> String { + + return attribute(self.meta).keyPath + } + + @available(*, deprecated, message: "Use the String(keyPath:) initializer and pass the KeyPath: String(keyPath: \\Person.friend)") + public static func keyPath(_ relationship: (Self) -> RelationshipContainer.ToOne) -> String { + + return relationship(self.meta).keyPath + } + + @available(*, deprecated, message: "Use the String(keyPath:) initializer and pass the KeyPath: String(keyPath: \\Person.friends)") + public static func keyPath(_ relationship: (Self) -> RelationshipContainer.ToManyOrdered) -> String { + + return relationship(self.meta).keyPath + } + + @available(*, deprecated, message: "Use the String(keyPath:) initializer and pass the KeyPath: String(keyPath: \\Person.friends)") + public static func keyPath(_ relationship: (Self) -> RelationshipContainer.ToManyUnordered) -> String { + + return relationship(self.meta).keyPath + } + @available(*, deprecated, message: "Use the Where(_:) initializer that accepts the same closure argument") public static func `where`(_ condition: (Self) -> Where) -> Where { diff --git a/Sources/DynamicKeyPath.swift b/Sources/DynamicKeyPath.swift new file mode 100644 index 0000000..f37974b --- /dev/null +++ b/Sources/DynamicKeyPath.swift @@ -0,0 +1,193 @@ +// +// DynamicKeyPath.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation + + +// MARK: - DynamicKeyPath + +/** + Used only for utility methods. + */ +public protocol DynamicKeyPath { + + /** + The DynamicObject type + */ + associatedtype ObjectType + + /** + The Value type + */ + associatedtype ValueType + + /** + The keyPath string + */ + var cs_keyPathString: String { get } +} + + +// MARK: - KeyPathString + +public extension KeyPathString { + + /** + Extracts the keyPath string from the property. + ``` + let keyPath = String(keyPath: \Person.nickname) + ``` + */ + public init(keyPath: KeyPath) { + + self = keyPath.cs_keyPathString + } + + /** + Extracts the keyPath string from the property. + ``` + let keyPath = String(keyPath: \Person.nickname) + ``` + */ + public init(keyPath: KeyPath) { + + self = O.meta[keyPath: keyPath].cs_keyPathString + } +} + + +// MARK: - KeyPath: DynamicKeyPath + +// TODO: SE-0143 is not implemented: https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md +//extension KeyPath: DynamicKeyPath where Root: NSManagedObject, Value: ImportableAttributeType { +extension KeyPath: DynamicKeyPath { + + public typealias ObjectType = Root + public typealias ValueType = Value + + public var cs_keyPathString: String { + + return self._kvcKeyPathString! + } +} + + +// MARK: - ValueContainer.Required: DynamicKeyPath + +extension ValueContainer.Required: DynamicKeyPath { + + public typealias ObjectType = O + public typealias ValueType = V + + public var cs_keyPathString: String { + + return self.keyPath + } +} + + +// MARK: - ValueContainer.Optional: DynamicKeyPath + +extension ValueContainer.Optional: DynamicKeyPath { + + public typealias ObjectType = O + public typealias ValueType = V + + public var cs_keyPathString: String { + + return self.keyPath + } +} + + +// MARK: - TransformableContainer.Required: DynamicKeyPath + +extension TransformableContainer.Required: DynamicKeyPath { + + public typealias ObjectType = O + public typealias ValueType = V + + public var cs_keyPathString: String { + + return self.keyPath + } +} + + +// MARK: - TransformableContainer.Optional: DynamicKeyPath + +extension TransformableContainer.Optional: DynamicKeyPath { + + public typealias ObjectType = O + public typealias ValueType = V + + public var cs_keyPathString: String { + + return self.keyPath + } +} + + +// MARK: - RelationshipContainer.ToOne: DynamicKeyPath + +extension RelationshipContainer.ToOne: DynamicKeyPath { + + public typealias ObjectType = O + public typealias ValueType = D + + public var cs_keyPathString: String { + + return self.keyPath + } +} + + +// MARK: - RelationshipContainer.ToManyOrdered: DynamicKeyPath + +extension RelationshipContainer.ToManyOrdered: DynamicKeyPath { + + public typealias ObjectType = O + public typealias ValueType = D + + public var cs_keyPathString: String { + + return self.keyPath + } +} + + +// MARK: - RelationshipContainer.ToManyUnordered: DynamicKeyPath + +extension RelationshipContainer.ToManyUnordered: DynamicKeyPath { + + public typealias ObjectType = O + public typealias ValueType = D + + public var cs_keyPathString: String { + + return self.keyPath + } +} + diff --git a/Sources/OrderBy.swift b/Sources/OrderBy.swift index 96e575d..b34b8d4 100644 --- a/Sources/OrderBy.swift +++ b/Sources/OrderBy.swift @@ -189,7 +189,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: KeyPath) -> SortKey where A: ValueContainer.Required { + public static func ascending(_ attribute: KeyPath.Required>) -> SortKey { return .ascending(D.meta[keyPath: attribute].keyPath) } @@ -197,7 +197,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: KeyPath) -> SortKey where A: ValueContainer.Optional { + public static func ascending(_ attribute: KeyPath.Optional>) -> SortKey { return .ascending(D.meta[keyPath: attribute].keyPath) } @@ -205,7 +205,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: KeyPath) -> SortKey where A: TransformableContainer.Required { + public static func ascending(_ attribute: KeyPath.Required>) -> SortKey { return .ascending(D.meta[keyPath: attribute].keyPath) } @@ -213,7 +213,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: KeyPath) -> SortKey where A: TransformableContainer.Optional { + public static func ascending(_ attribute: KeyPath.Optional>) -> SortKey { return .ascending(D.meta[keyPath: attribute].keyPath) } @@ -221,7 +221,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: KeyPath) -> SortKey where A: ValueContainer.Required { + public static func descending(_ attribute: KeyPath.Required>) -> SortKey { return .descending(D.meta[keyPath: attribute].keyPath) } @@ -229,7 +229,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: KeyPath) -> SortKey where A: ValueContainer.Optional { + public static func descending(_ attribute: KeyPath.Optional>) -> SortKey { return .descending(D.meta[keyPath: attribute].keyPath) } @@ -237,7 +237,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: KeyPath) -> SortKey where A: TransformableContainer.Required { + public static func descending(_ attribute: KeyPath.Required>) -> SortKey { return .descending(D.meta[keyPath: attribute].keyPath) } @@ -245,7 +245,7 @@ public struct OrderBy: OrderByClause, FetchClause, QueryClause /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: KeyPath) -> SortKey where A: TransformableContainer.Optional { + public static func descending(_ attribute: KeyPath.Optional>) -> SortKey { return .descending(D.meta[keyPath: attribute].keyPath) } @@ -262,7 +262,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in ascending order */ - public static func ascending(_ attribute: (D) -> T) -> OrderBy.SortKey { + public static func ascending(_ attribute: (D) -> K) -> OrderBy.SortKey { return .ascending(attribute(D.meta).cs_keyPathString) } @@ -270,7 +270,7 @@ public extension OrderBy.SortKey where D: CoreStoreObject { /** Indicates that the `KeyPathString` should be sorted in descending order */ - public static func descending(_ attribute: (D) -> T) -> OrderBy.SortKey { + public static func descending(_ attribute: (D) -> K) -> OrderBy.SortKey { return .descending(attribute(D.meta).cs_keyPathString) } diff --git a/Sources/Relationship.swift b/Sources/Relationship.swift index 744ec9a..f80269d 100644 --- a/Sources/Relationship.swift +++ b/Sources/Relationship.swift @@ -234,11 +234,7 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol - /** - The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. - */ - public let keyPath: KeyPathString - + internal let keyPath: KeyPathString internal let isToMany = false internal let isOrdered = false internal let deleteRule: NSDeleteRule @@ -505,11 +501,7 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol - /** - The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. - */ - public let keyPath: KeyPathString - + internal let keyPath: KeyPathString internal let isToMany = true internal let isOptional = true internal let isOrdered = true @@ -782,11 +774,7 @@ public enum RelationshipContainer { // MARK: RelationshipProtocol - /** - The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. - */ - public let keyPath: KeyPathString - + internal let keyPath: KeyPathString internal let isToMany = true internal let isOptional = true internal let isOrdered = false diff --git a/Sources/Transformable.swift b/Sources/Transformable.swift index 59dc823..0ce13af 100644 --- a/Sources/Transformable.swift +++ b/Sources/Transformable.swift @@ -192,16 +192,12 @@ public enum TransformableContainer { // MARK: AttributeProtocol - /** - The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. - */ - public let keyPath: KeyPathString - internal static var attributeType: NSAttributeType { return .transformableAttributeType } + internal let keyPath: KeyPathString internal let isOptional = false internal let isIndexed: Bool internal let isTransient: Bool @@ -423,11 +419,7 @@ public enum TransformableContainer { return .transformableAttributeType } - /** - The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. - */ - public let keyPath: KeyPathString - + internal let keyPath: KeyPathString internal let isOptional = true internal let isIndexed: Bool internal let isTransient: Bool diff --git a/Sources/Value.swift b/Sources/Value.swift index 46db6f1..c8b2231 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -195,11 +195,7 @@ public enum ValueContainer { return V.cs_rawAttributeType } - /** - The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. - */ - public let keyPath: KeyPathString - + internal let keyPath: KeyPathString internal let isOptional = false internal let isIndexed: Bool internal let isTransient: Bool @@ -424,11 +420,7 @@ public enum ValueContainer { return V.cs_rawAttributeType } - /** - The keyPath string represented by this property. Generally, there are more type-safe utilities for querying and other common tasks. - */ - public let keyPath: KeyPathString - + internal let keyPath: KeyPathString internal let isOptional = true internal let isIndexed: Bool internal let isTransient: Bool
SwiftObjective-C