From d830c900289319110e7e27a2d19db0cfe8fa2079 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Sat, 26 Oct 2019 10:58:33 +0900 Subject: [PATCH] trials --- CoreStore.xcodeproj/project.pbxproj | 10 -- CoreStoreTests/BridgingTests.m | 11 -- CoreStoreTests/DynamicModelTests.swift | 78 ++++---- CoreStoreTests/ListObserverTests.swift | 28 +-- CoreStoreTests/ObjectObserverTests.swift | 6 +- CoreStoreTests/TransactionTests.swift | 6 +- CoreStoreTests/WhereTests.swift | 22 +-- ...reStore+CustomDebugStringConvertible.swift | 7 +- Sources/CoreStoreError.swift | 9 +- Sources/CoreStoreObject+DataSources.swift | 29 --- Sources/KeyPathGenericBindings.swift | 30 ++-- Sources/Value.swift | 168 +++++++++++++----- 12 files changed, 220 insertions(+), 184 deletions(-) delete mode 100644 Sources/CoreStoreObject+DataSources.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index a881a74..4770278 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -125,10 +125,6 @@ B50E17622351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; }; B50E17632351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; }; B50E17642351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; }; - B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; }; - B50EE14323473C96009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; }; - B50EE14423473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; }; - B50EE14523473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; }; B512607F1E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */; }; B51260801E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */; }; B51260811E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */; }; @@ -902,7 +898,6 @@ B50E175623517DE4004F033C /* Differentiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Differentiable.swift; sourceTree = ""; }; B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.DiffResult.swift; sourceTree = ""; }; B50E17602351FA66004F033C /* Internals.Closure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.Closure.swift; sourceTree = ""; }; - B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+DataSources.swift"; sourceTree = ""; }; B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+Convenience.swift"; sourceTree = ""; }; B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEntityDescription+DynamicModel.swift"; sourceTree = ""; }; B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.EntityIdentifier.swift; sourceTree = ""; }; @@ -1604,7 +1599,6 @@ B5BF7FC0234D7B2E0070E741 /* ObjectPublisher.swift */, B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */, B55BB4D3235012AE00C33E34 /* ObjectRepresentation.swift */, - B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */, B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */, B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */, B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift */, @@ -2187,7 +2181,6 @@ B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */, B52F74451E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */, B5FAD6AC1B51285300714891 /* Internals.MigrationManager.swift in Sources */, - B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */, B5E84EF61AFF846E0064E85B /* DataStack+Transaction.swift in Sources */, B5FEC18E1C9166E200532541 /* NSPersistentStore+Setup.swift in Sources */, B5BF7FCB234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */, @@ -2366,7 +2359,6 @@ B549F6741E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */, B509C7F51E54511B0061C547 /* ImportableAttributeType.swift in Sources */, 82BA18B31C4BBD3900A0916E /* ImportableUniqueObject.swift in Sources */, - B50EE14323473C96009B8C47 /* CoreStoreObject+DataSources.swift in Sources */, B55BB4DA23503B9600C33E34 /* EnvironmentValues+DataSources.swift in Sources */, B5E1B5951CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */, 18166887232B9ED60097C275 /* String+KeyPaths.swift in Sources */, @@ -2590,7 +2582,6 @@ B549F6761E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */, B509C7F71E54511B0061C547 /* ImportableAttributeType.swift in Sources */, B5220E1F1D130810009BC71E /* CSListObserver.swift in Sources */, - B50EE14523473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */, B55BB4D823503B9500C33E34 /* EnvironmentValues+DataSources.swift in Sources */, B52DD1941BE1F92500949AFE /* CoreStore.swift in Sources */, 18166889232B9ED80097C275 /* String+KeyPaths.swift in Sources */, @@ -2814,7 +2805,6 @@ B549F6751E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */, B509C7F61E54511B0061C547 /* ImportableAttributeType.swift in Sources */, B5E1B5961CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */, - B50EE14423473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */, B55BB4D923503B9600C33E34 /* EnvironmentValues+DataSources.swift in Sources */, B5ECDC2C1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */, 18166888232B9ED70097C275 /* String+KeyPaths.swift in Sources */, diff --git a/CoreStoreTests/BridgingTests.m b/CoreStoreTests/BridgingTests.m index 8ed7650..ff16f8b 100644 --- a/CoreStoreTests/BridgingTests.m +++ b/CoreStoreTests/BridgingTests.m @@ -187,17 +187,6 @@ XCTAssertEqualObjects([[sqliteStorage class] storeType], [CSSQLiteStore storeType]); XCTAssertEqualObjects([[sqliteStorage class] storeType], NSSQLiteStoreType); XCTAssertNil(sqliteStorage.configuration); - NSDictionary *storeOptions; - if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, *)) { - - storeOptions = @{ NSSQLitePragmasOption: @{ @"journal_mode": @"WAL" }, - NSBinaryStoreInsecureDecodingCompatibilityOption: @YES }; - } - else { - - storeOptions = @{ NSSQLitePragmasOption: @{ @"journal_mode": @"WAL" }}; - } - XCTAssertEqualObjects(sqliteStorage.storeOptions, storeOptions); XCTAssertNil(sqliteError); } diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index 2b40a97..7e7095a 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -38,15 +38,21 @@ import CoreStore class Animal: CoreStoreObject { - let species = Value.Required("species", initial: "Swift") + @Value.Required("species") + var species: String = "Swift" + let master = Relationship.ToOne("master") let color = Transformable.Optional("color") } class Dog: Animal { - let nickname = Value.Optional("nickname") - let age = Value.Required("age", initial: 1) + @Value.Optional("nickname") + var nickname: String? + + @Value.Required("age") + var age: Int = 1 + let friends = Relationship.ToManyOrdered("friends") let friendedBy = Relationship.ToManyUnordered("friendedBy", inverse: { $0.friends }) } @@ -138,10 +144,10 @@ class DynamicModelTests: BaseTestDataTestCase { ) self.prepareStack(dataStack, configurations: [nil]) { (stack) in - let k1 = String(keyPath: \Animal.species) + let k1 = String(keyPath: \Animal.$species) XCTAssertEqual(k1, "species") - let k2 = String(keyPath: \Dog.species) + let k2 = String(keyPath: \Dog.$species) XCTAssertEqual(k2, "species") let k3 = String(keyPath: \Dog.nickname) @@ -156,11 +162,11 @@ class DynamicModelTests: BaseTestDataTestCase { asynchronous: { (transaction) in let animal = transaction.create(Into()) - XCTAssertEqual(animal.species.value, "Swift") - XCTAssertTrue(type(of: animal.species.value) == String.self) + XCTAssertEqual(animal.species, "Swift") + XCTAssertTrue(type(of: animal.species) == String.self) - animal.species .= "Sparrow" - XCTAssertEqual(animal.species.value, "Sparrow") + animal.species = "Sparrow" + XCTAssertEqual(animal.species, "Sparrow") animal.color .= .yellow XCTAssertEqual(animal.color.value, Color.yellow) @@ -169,7 +175,7 @@ class DynamicModelTests: BaseTestDataTestCase { switch property.keyPath { - case String(keyPath: \Animal.species): + case String(keyPath: \Animal.$species): XCTAssertTrue(property is ValueContainer.Required) case String(keyPath: \Animal.master): @@ -184,15 +190,15 @@ class DynamicModelTests: BaseTestDataTestCase { } let dog = transaction.create(Into()) - XCTAssertEqual(dog.species.value, "Swift") + XCTAssertEqual(dog.species, "Swift") XCTAssertEqual(dog.nickname.value, nil) - XCTAssertEqual(dog.age.value, 1) + XCTAssertEqual(dog.age, 1) for property in Dog.metaProperties(includeSuperclasses: true) { switch property.keyPath { - case String(keyPath: \Dog.species): + case String(keyPath: \Dog.$species): XCTAssertTrue(property is ValueContainer.Required) case String(keyPath: \Dog.master): @@ -204,7 +210,7 @@ class DynamicModelTests: BaseTestDataTestCase { case String(keyPath: \Dog.nickname): XCTAssertTrue(property is ValueContainer.Optional) - case String(keyPath: \Dog.age): + case String(keyPath: \Dog.$age): XCTAssertTrue(property is ValueContainer.Required) case String(keyPath: \Dog.friends): @@ -231,17 +237,17 @@ class DynamicModelTests: BaseTestDataTestCase { // // #endif - let didSetObserver = dog.species.observe(options: [.new, .old]) { (object, change) in + let didSetObserver = dog.$species.observe(options: [.new, .old]) { (object, change) in XCTAssertEqual(object, dog) XCTAssertEqual(change.kind, .setting) XCTAssertEqual(change.newValue, "Dog") XCTAssertEqual(change.oldValue, "Swift") XCTAssertFalse(change.isPrior) - XCTAssertEqual(object.species.value, "Dog") + XCTAssertEqual(object.species, "Dog") didSetObserverDone.fulfill() } - let willSetObserver = dog.species.observe(options: [.new, .old, .prior]) { (object, change) in + let willSetObserver = dog.$species.observe(options: [.new, .old, .prior]) { (object, change) in XCTAssertEqual(object, dog) XCTAssertEqual(change.kind, .setting) @@ -250,19 +256,19 @@ class DynamicModelTests: BaseTestDataTestCase { if change.isPrior { XCTAssertNil(change.newValue) - XCTAssertEqual(object.species.value, "Swift") + XCTAssertEqual(object.species, "Swift") willSetPriorObserverDone.fulfill() } else { XCTAssertEqual(change.newValue, "Dog") - XCTAssertEqual(object.species.value, "Dog") + XCTAssertEqual(object.species, "Dog") willSetNotPriorObserverDone.fulfill() } } - dog.species .= "Dog" - XCTAssertEqual(dog.species.value, "Dog") + dog.species = "Dog" + XCTAssertEqual(dog.species, "Dog") didSetObserver.invalidate() willSetObserver.invalidate() @@ -338,12 +344,12 @@ class DynamicModelTests: BaseTestDataTestCase { stack.perform( asynchronous: { (transaction) in - let p1 = Where({ $0.species == "Sparrow" }) + let p1 = Where({ $0.$species == "Sparrow" }) XCTAssertEqual(p1.predicate, NSPredicate(format: "%K == %@", "species", "Sparrow")) let bird = try transaction.fetchOne(From(), p1) XCTAssertNotNil(bird) - XCTAssertEqual(bird!.species.value, "Sparrow") + XCTAssertEqual(bird!.species, "Sparrow") let p2 = Where({ $0.nickname == "Spot" }) XCTAssertEqual(p2.predicate, NSPredicate(format: "%K == %@", "nickname", "Spot")) @@ -351,46 +357,46 @@ class DynamicModelTests: BaseTestDataTestCase { let dog = try transaction.fetchOne(From().where(\.nickname == "Spot")) XCTAssertNotNil(dog) XCTAssertEqual(dog!.nickname.value, "Spot") - XCTAssertEqual(dog!.species.value, "Dog") + XCTAssertEqual(dog!.species, "Dog") let person = try transaction.fetchOne(From()) XCTAssertNotNil(person) XCTAssertEqual(person!.pets.value.first, dog) - let p3 = Where({ $0.age == 10 }) + let p3 = Where({ $0.$age == 10 }) XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10)) - let totalAge = try transaction.queryValue(From().select(Int.self, .sum(\Dog.age))) + let totalAge = try transaction.queryValue(From().select(Int.self, .sum(\Dog.$age))) XCTAssertEqual(totalAge, 1) _ = try transaction.fetchAll( From() - .where(\Animal.species == "Dog" && \Dog.age == 10) + .where(\Animal.$species == "Dog" && \Dog.$age == 10) ) _ = try transaction.fetchAll( From() - .where(\Dog.age == 10 && \Animal.species == "Dog") - .orderBy(.ascending({ $0.species })) + .where(\Dog.$age == 10 && \Animal.$species == "Dog") + .orderBy(.ascending({ $0.$species })) ) _ = try transaction.fetchAll( From(), - Where({ $0.age > 10 && $0.age <= 15 }) + Where({ $0.$age > 10 && $0.$age <= 15 }) ) _ = try transaction.fetchAll( From(), - Where({ $0.species == "Dog" && $0.age == 10 }) + Where({ $0.$species == "Dog" && $0.$age == 10 }) ) _ = try transaction.fetchAll( From(), - Where({ $0.age == 10 && $0.species == "Dog" }) + Where({ $0.$age == 10 && $0.$species == "Dog" }) ) _ = try transaction.fetchAll( From(), - Where({ $0.age > 10 && $0.age <= 15 }) + Where({ $0.$age > 10 && $0.$age <= 15 }) ) _ = try transaction.fetchAll( From(), - (\Dog.age > 10 && \Dog.age <= 15) + (\Dog.$age > 10 && \Dog.$age <= 15) ) }, success: { _ in @@ -409,8 +415,8 @@ class DynamicModelTests: BaseTestDataTestCase { @objc dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() { - XCTAssertEqual(String(keyPath: \Animal.species), "species") - XCTAssertEqual(String(keyPath: \Dog.species), "species") + XCTAssertEqual(String(keyPath: \Animal.$species), "species") + XCTAssertEqual(String(keyPath: \Dog.$species), "species") } @nonobjc diff --git a/CoreStoreTests/ListObserverTests.swift b/CoreStoreTests/ListObserverTests.swift index 7257df9..ac1bb5a 100644 --- a/CoreStoreTests/ListObserverTests.swift +++ b/CoreStoreTests/ListObserverTests.swift @@ -53,7 +53,7 @@ class ListObserverTests: BaseTestDataTestCase { var events = 0 - let willChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -67,7 +67,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 0 } ) - let didInsertSectionExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertSection:toSectionIndex:"), object: observer, handler: { (note) -> Bool in @@ -87,7 +87,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 1 } ) - let didInsertObjectExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -119,7 +119,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 2 } ) - let didChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in @@ -184,7 +184,7 @@ class ListObserverTests: BaseTestDataTestCase { var events = 0 - let willChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -199,7 +199,7 @@ class ListObserverTests: BaseTestDataTestCase { } ) - let didUpdateObjectExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitor:didUpdateObject:atIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -250,7 +250,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 1 || events == 2 } ) - let didChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in @@ -329,7 +329,7 @@ class ListObserverTests: BaseTestDataTestCase { var events = 0 - let willChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -343,7 +343,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 0 } ) - let didMoveObjectExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -376,7 +376,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 1 } ) - let didChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in @@ -437,7 +437,7 @@ class ListObserverTests: BaseTestDataTestCase { var events = 0 - let willChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -451,7 +451,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 0 } ) - let didUpdateObjectExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteObject:fromIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -480,7 +480,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 1 || events == 2 } ) - let didDeleteSectionExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteSection:fromSectionIndex:"), object: observer, handler: { (note) -> Bool in @@ -508,7 +508,7 @@ class ListObserverTests: BaseTestDataTestCase { return events == 3 } ) - let didChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in diff --git a/CoreStoreTests/ObjectObserverTests.swift b/CoreStoreTests/ObjectObserverTests.swift index df92521..4072916 100644 --- a/CoreStoreTests/ObjectObserverTests.swift +++ b/CoreStoreTests/ObjectObserverTests.swift @@ -57,7 +57,7 @@ class ObjectObserverTests: BaseTestDataTestCase { var events = 0 - let willUpdateExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "objectMonitor:willUpdateObject:"), object: observer, handler: { (note) -> Bool in @@ -74,7 +74,7 @@ class ObjectObserverTests: BaseTestDataTestCase { return events == 0 } ) - let didUpdateExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"), object: observer, handler: { (note) -> Bool in @@ -154,7 +154,7 @@ class ObjectObserverTests: BaseTestDataTestCase { var events = 0 - let didDeleteExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "objectMonitor:didDeleteObject:"), object: observer, handler: { (note) -> Bool in diff --git a/CoreStoreTests/TransactionTests.swift b/CoreStoreTests/TransactionTests.swift index a26e15b..e91bfb7 100644 --- a/CoreStoreTests/TransactionTests.swift +++ b/CoreStoreTests/TransactionTests.swift @@ -400,7 +400,7 @@ final class TransactionTests: BaseTestCase { XCTAssertFalse(monitor.hasObjects()) var events = 0 - let willChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), object: observer, handler: { (note) -> Bool in @@ -414,7 +414,7 @@ final class TransactionTests: BaseTestCase { return events == 0 } ) - let didInsertObjectExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"), object: observer, handler: { (note) -> Bool in @@ -444,7 +444,7 @@ final class TransactionTests: BaseTestCase { return events == 1 } ) - let didChangeExpectation = self.expectation( + _ = self.expectation( forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), object: observer, handler: { (note) -> Bool in diff --git a/CoreStoreTests/WhereTests.swift b/CoreStoreTests/WhereTests.swift index d9b2ed5..91c2735 100644 --- a/CoreStoreTests/WhereTests.swift +++ b/CoreStoreTests/WhereTests.swift @@ -109,8 +109,8 @@ final class WhereTests: XCTestCase { ) XCTAssertAllEqual( "master.pets.species", - (\Animal.master ~ \.pets ~ \.species).description, - String(keyPath: \Animal.master ~ \.pets ~ \.species) + (\Animal.master ~ \.pets ~ \.$species).description, + String(keyPath: \Animal.master ~ \.pets ~ \.$species) ) XCTAssertAllEqual( "master.pets.master", @@ -167,8 +167,8 @@ final class WhereTests: XCTestCase { ) XCTAssertAllEqual( "ANY master.pets.species", - (\Animal.master ~ \.pets ~ \.species).any().description, - String(keyPath: (\Animal.master ~ \.pets ~ \.species).any()) + (\Animal.master ~ \.pets ~ \.$species).any().description, + String(keyPath: (\Animal.master ~ \.pets ~ \.$species).any()) ) } } @@ -196,8 +196,8 @@ final class WhereTests: XCTestCase { ) XCTAssertAllEqual( "ALL master.pets.species", - (\Animal.master ~ \.pets ~ \.species).all().description, - String(keyPath: (\Animal.master ~ \.pets ~ \.species).all()) + (\Animal.master ~ \.pets ~ \.$species).all().description, + String(keyPath: (\Animal.master ~ \.pets ~ \.$species).all()) ) } } @@ -225,8 +225,8 @@ final class WhereTests: XCTestCase { ) XCTAssertAllEqual( "NONE master.pets.species", - (\Animal.master ~ \.pets ~ \.species).none().description, - String(keyPath: (\Animal.master ~ \.pets ~ \.species).none()) + (\Animal.master ~ \.pets ~ \.$species).none().description, + String(keyPath: (\Animal.master ~ \.pets ~ \.$species).none()) ) } } @@ -301,7 +301,7 @@ final class WhereTests: XCTestCase { } do { - let whereClause: Where = (\.master ~ \.pets ~ \.species).any() == dummy + let whereClause: Where = (\.master ~ \.pets ~ \.$species).any() == dummy let predicate = NSPredicate(format: "ANY master.pets.species == %@", dummy) XCTAssertAllEqual(whereClause, Where(predicate)) XCTAssertAllEqual(whereClause.predicate, predicate) @@ -319,7 +319,7 @@ final class WhereTests: XCTestCase { } do { - let whereClause: Where = (\.master ~ \.pets ~ \.species).all() == dummy + let whereClause: Where = (\.master ~ \.pets ~ \.$species).all() == dummy let predicate = NSPredicate(format: "ALL master.pets.species == %@", dummy) XCTAssertAllEqual(whereClause, Where(predicate)) XCTAssertAllEqual(whereClause.predicate, predicate) @@ -337,7 +337,7 @@ final class WhereTests: XCTestCase { } do { - let whereClause: Where = (\.master ~ \.pets ~ \.species).none() == dummy + let whereClause: Where = (\.master ~ \.pets ~ \.$species).none() == dummy let predicate = NSPredicate(format: "NONE master.pets.species == %@", dummy) XCTAssertAllEqual(whereClause, Where(predicate)) XCTAssertAllEqual(whereClause.predicate, predicate) diff --git a/Sources/CoreStore+CustomDebugStringConvertible.swift b/Sources/CoreStore+CustomDebugStringConvertible.swift index d1e1564..40324bc 100644 --- a/Sources/CoreStore+CustomDebugStringConvertible.swift +++ b/Sources/CoreStore+CustomDebugStringConvertible.swift @@ -94,13 +94,14 @@ extension CoreStoreError: CustomDebugStringConvertible, CoreStoreDebugStringConv firstLine = ".progressiveMigrationRequired" info.append(("localStoreURL", localStoreURL)) - case .asynchronousMigrationRequired(let localStoreURL): + case .asynchronousMigrationRequired(let localStoreURL, let nsError): firstLine = ".asynchronousMigrationRequired" info.append(("localStoreURL", localStoreURL)) + info.append(("NSError", nsError)) - case .internalError(let NSError): + case .internalError(let nsError): firstLine = ".internalError" - info.append(("NSError", NSError)) + info.append(("NSError", nsError)) case .userError(error: let error): firstLine = ".userError" diff --git a/Sources/CoreStoreError.swift b/Sources/CoreStoreError.swift index ad19859..d5a03f5 100644 --- a/Sources/CoreStoreError.swift +++ b/Sources/CoreStoreError.swift @@ -197,14 +197,7 @@ public enum CoreStoreError: Error, CustomNSError, Hashable { return NSError1.isEqual(NSError2) case (.userError(let error1), .userError(let error2)): - switch (error1, error2) { - - case (let error1 as AnyHashable, let error2 as AnyHashable): - return error1 == error2 - - case (let error1 as NSError, let error2 as NSError): - return error1.isEqual(error2) - } + return error1.bridgeToSwift == error2.bridgeToSwift case (.userCancelled, .userCancelled): return true diff --git a/Sources/CoreStoreObject+DataSources.swift b/Sources/CoreStoreObject+DataSources.swift deleted file mode 100644 index 52155f4..0000000 --- a/Sources/CoreStoreObject+DataSources.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// CoreStoreObject+DataSources.swift -// CoreStore iOS -// -// Created by John Estropia on 2019/10/04. -// Copyright © 2019 John Rommel Estropia. All rights reserved. -// - -#if canImport(UIKit) || canImport(AppKit) - -#if canImport(Combine) -import Combine - -// MARK: - ListPublisher: ObservableObject - -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -extension CoreStoreObject: ObservableObject { - - // MARK: ObservableObject - - public var objectWillChange: ObservableObjectPublisher { - - return self.cs_toRaw().objectWillChange - } -} - -#endif - -#endif diff --git a/Sources/KeyPathGenericBindings.swift b/Sources/KeyPathGenericBindings.swift index 0439a34..a7cd483 100644 --- a/Sources/KeyPathGenericBindings.swift +++ b/Sources/KeyPathGenericBindings.swift @@ -106,47 +106,47 @@ extension Int64: AllowedObjectiveCKeyPathValue { extension NSData: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSData } extension NSDate: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSDate } extension NSManagedObject: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSManagedObject } extension NSNumber: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSNumber } extension NSString: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSString } extension NSSet: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSSet } extension NSOrderedSet: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSOrderedSet } extension NSURL: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSURL } extension NSUUID: AllowedOptionalObjectiveCKeyPathValue { - public typealias DestinationValueType = Self + public typealias DestinationValueType = NSUUID } extension String: AllowedOptionalObjectiveCKeyPathValue { @@ -240,32 +240,32 @@ extension Int64: AllowedObjectiveCAttributeKeyPathValue { extension NSData: AllowedObjectiveCAttributeKeyPathValue { - public typealias ReturnValueType = Self + public typealias ReturnValueType = NSData } extension NSDate: AllowedObjectiveCAttributeKeyPathValue { - public typealias ReturnValueType = Self + public typealias ReturnValueType = NSDate } extension NSNumber: AllowedObjectiveCAttributeKeyPathValue { - public typealias ReturnValueType = Self + public typealias ReturnValueType = NSNumber } extension NSString: AllowedObjectiveCAttributeKeyPathValue { - public typealias ReturnValueType = Self + public typealias ReturnValueType = NSString } extension NSURL: AllowedObjectiveCAttributeKeyPathValue { - public typealias ReturnValueType = Self + public typealias ReturnValueType = NSURL } extension NSUUID: AllowedObjectiveCAttributeKeyPathValue { - public typealias ReturnValueType = Self + public typealias ReturnValueType = NSUUID } extension String: AllowedObjectiveCAttributeKeyPathValue { diff --git a/Sources/Value.swift b/Sources/Value.swift index fec2e9a..3951192 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -73,6 +73,7 @@ public enum ValueContainer { ``` - Important: `Value.Required` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. */ + @propertyWrapper public final class Required: AttributeKeyPathStringConvertible, AttributeProtocol { /** @@ -110,7 +111,7 @@ public enum ValueContainer { - parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.setPrimitiveValue(_:for:)` instead of `PartialObject.setValue(_:for:)`, which would unintentionally execute the same closure again recursively. - 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( + public convenience init( _ keyPath: KeyPathString, initial: @autoclosure @escaping () -> V, isTransient: Bool = false, @@ -119,15 +120,17 @@ public enum ValueContainer { customGetter: ((_ partialObject: PartialObject) -> V)? = nil, customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { - - self.keyPath = keyPath - self.isTransient = isTransient - self.defaultValue = { initial().cs_toQueryableNativeType() } - self.versionHashModifier = versionHashModifier - self.renamingIdentifier = renamingIdentifier - self.customGetter = customGetter - self.customSetter = customSetter - self.affectedByKeyPaths = affectedByKeyPaths + + self.init( + wrappedValue: initial(), + keyPath, + isTransient: isTransient, + versionHashModifier: versionHashModifier(), + renamingIdentifier: renamingIdentifier(), + customGetter: customGetter, + customSetter: customSetter, + affectedByKeyPaths: affectedByKeyPaths() + ) } /** @@ -135,24 +138,59 @@ public enum ValueContainer { */ public var value: ReturnValueType { + get { + + return self.wrappedValue + } + set { + + self.wrappedValue = newValue + } + } + + + // MARK: @propertyWrapper + + public init( + wrappedValue initialValue: @autoclosure @escaping () -> V, + _ keyPath: KeyPathString, + isTransient: Bool = false, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, + customGetter: ((_ partialObject: PartialObject) -> V)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, + affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { + + self.keyPath = keyPath + self.isTransient = isTransient + self.defaultValue = { initialValue().cs_toQueryableNativeType() } + self.versionHashModifier = versionHashModifier + self.renamingIdentifier = renamingIdentifier + self.customGetter = customGetter + self.customSetter = customSetter + self.affectedByKeyPaths = affectedByKeyPaths + } + + public var wrappedValue: V { + get { Internals.assert( self.rawObject != nil, "Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." ) - return withExtendedLifetime(self.rawObject!) { (object) in - + return withExtendedLifetime(self.rawObject!) { (rawObject) in + Internals.assert( - object.isRunningInAllowedQueue() == true, + rawObject.isRunningInAllowedQueue() == true, "Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue." ) if let customGetter = self.customGetter { - - return customGetter(PartialObject(object)) + + return customGetter(PartialObject(rawObject)) } return V.cs_fromQueryableNativeType( - object.value(forKey: self.keyPath)! as! V.QueryableNativeType + rawObject.value(forKey: self.keyPath)! as! V.QueryableNativeType )! } } @@ -162,27 +200,32 @@ public enum ValueContainer { self.rawObject != nil, "Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." ) - return withExtendedLifetime(self.rawObject!) { (object) in - + return withExtendedLifetime(self.rawObject!) { (rawObject) in + Internals.assert( - object.isRunningInAllowedQueue() == true, + rawObject.isRunningInAllowedQueue() == true, "Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue." ) Internals.assert( - object.isEditableInContext() == true, + rawObject.isEditableInContext() == true, "Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction." ) if let customSetter = self.customSetter { - - return customSetter(PartialObject(object), newValue) + + return customSetter(PartialObject(rawObject), newValue) } - return object.setValue( + return rawObject.setValue( newValue.cs_toQueryableNativeType(), forKey: self.keyPath ) } } } + + public var projectedValue: ValueContainer.Required { + + return self + } // MARK: AnyKeyPathStringConvertible @@ -293,6 +336,7 @@ public enum ValueContainer { ``` - Important: `Value.Optional` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. */ + @propertyWrapper public final class Optional: AttributeKeyPathStringConvertible, AttributeProtocol { /** @@ -333,7 +377,7 @@ public enum ValueContainer { - parameter originalNewValue: the original new value - 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( + public convenience init( _ keyPath: KeyPathString, initial: @autoclosure @escaping () -> V? = nil, isTransient: Bool = false, @@ -343,14 +387,16 @@ public enum ValueContainer { customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { - self.keyPath = keyPath - self.isTransient = isTransient - self.defaultValue = { initial()?.cs_toQueryableNativeType() } - self.versionHashModifier = versionHashModifier - self.renamingIdentifier = renamingIdentifier - self.customGetter = customGetter - self.customSetter = customSetter - self.affectedByKeyPaths = affectedByKeyPaths + self.init( + wrappedValue: initial(), + keyPath, + isTransient: isTransient, + versionHashModifier: versionHashModifier(), + renamingIdentifier: renamingIdentifier(), + customGetter: customGetter, + customSetter: customSetter, + affectedByKeyPaths: affectedByKeyPaths() + ) } /** @@ -358,23 +404,58 @@ public enum ValueContainer { */ public var value: ReturnValueType { + get { + + return self.wrappedValue + } + set { + + self.wrappedValue = newValue + } + } + + + // MARK: @propertyWrapper + + public init( + wrappedValue initialValue: @autoclosure @escaping () -> V?, + _ keyPath: KeyPathString, + isTransient: Bool = false, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, + customGetter: ((_ partialObject: PartialObject) -> V?)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? = nil, + affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { + + self.keyPath = keyPath + self.isTransient = isTransient + self.defaultValue = { initialValue()?.cs_toQueryableNativeType() } + self.versionHashModifier = versionHashModifier + self.renamingIdentifier = renamingIdentifier + self.customGetter = customGetter + self.customSetter = customSetter + self.affectedByKeyPaths = affectedByKeyPaths + } + + public var wrappedValue: V? { + get { Internals.assert( self.rawObject != nil, "Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." ) - return withExtendedLifetime(self.rawObject!) { (object) in + return withExtendedLifetime(self.rawObject!) { (rawObject) in Internals.assert( - object.isRunningInAllowedQueue() == true, + rawObject.isRunningInAllowedQueue() == true, "Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue." ) if let customGetter = self.customGetter { - return customGetter(PartialObject(object)) + return customGetter(PartialObject(rawObject)) } - return (object.value(forKey: self.keyPath) as! V.QueryableNativeType?) + return (rawObject.value(forKey: self.keyPath) as! V.QueryableNativeType?) .flatMap(V.cs_fromQueryableNativeType) } } @@ -384,27 +465,32 @@ public enum ValueContainer { self.rawObject != nil, "Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." ) - return withExtendedLifetime(self.rawObject!) { (object) in + return withExtendedLifetime(self.rawObject!) { (rawObject) in Internals.assert( - object.isRunningInAllowedQueue() == true, + rawObject.isRunningInAllowedQueue() == true, "Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue." ) Internals.assert( - object.isEditableInContext() == true, + rawObject.isEditableInContext() == true, "Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction." ) if let customSetter = self.customSetter { - return customSetter(PartialObject(object), newValue) + return customSetter(PartialObject(rawObject), newValue) } - object.setValue( + rawObject.setValue( newValue?.cs_toQueryableNativeType(), forKey: self.keyPath ) } } } + + public var projectedValue: ValueContainer.Optional { + + return self + } // MARK: AnyKeyPathStringConvertible