This commit is contained in:
John Estropia
2019-10-26 10:58:33 +09:00
parent 998938490c
commit d830c90028
12 changed files with 220 additions and 184 deletions

View File

@@ -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 = "<group>"; };
B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.DiffResult.swift; sourceTree = "<group>"; };
B50E17602351FA66004F033C /* Internals.Closure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.Closure.swift; sourceTree = "<group>"; };
B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+DataSources.swift"; sourceTree = "<group>"; };
B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+Convenience.swift"; sourceTree = "<group>"; };
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEntityDescription+DynamicModel.swift"; sourceTree = "<group>"; };
B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.EntityIdentifier.swift; sourceTree = "<group>"; };
@@ -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 */,

View File

@@ -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);
}

View File

@@ -38,15 +38,21 @@ import CoreStore
class Animal: CoreStoreObject {
let species = Value.Required<String>("species", initial: "Swift")
@Value.Required("species")
var species: String = "Swift"
let master = Relationship.ToOne<Person>("master")
let color = Transformable.Optional<Color>("color")
}
class Dog: Animal {
let nickname = Value.Optional<String>("nickname")
let age = Value.Required<Int>("age", initial: 1)
@Value.Optional("nickname")
var nickname: String?
@Value.Required("age")
var age: Int = 1
let friends = Relationship.ToManyOrdered<Dog>("friends")
let friendedBy = Relationship.ToManyUnordered<Dog>("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<Animal>())
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<Animal>.Required<String>)
case String(keyPath: \Animal.master):
@@ -184,15 +190,15 @@ class DynamicModelTests: BaseTestDataTestCase {
}
let dog = transaction.create(Into<Dog>())
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<Animal>.Required<String>)
case String(keyPath: \Dog.master):
@@ -204,7 +210,7 @@ class DynamicModelTests: BaseTestDataTestCase {
case String(keyPath: \Dog.nickname):
XCTAssertTrue(property is ValueContainer<Dog>.Optional<String>)
case String(keyPath: \Dog.age):
case String(keyPath: \Dog.$age):
XCTAssertTrue(property is ValueContainer<Dog>.Required<Int>)
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<Animal>({ $0.species == "Sparrow" })
let p1 = Where<Animal>({ $0.$species == "Sparrow" })
XCTAssertEqual(p1.predicate, NSPredicate(format: "%K == %@", "species", "Sparrow"))
let bird = try transaction.fetchOne(From<Animal>(), p1)
XCTAssertNotNil(bird)
XCTAssertEqual(bird!.species.value, "Sparrow")
XCTAssertEqual(bird!.species, "Sparrow")
let p2 = Where<Dog>({ $0.nickname == "Spot" })
XCTAssertEqual(p2.predicate, NSPredicate(format: "%K == %@", "nickname", "Spot"))
@@ -351,46 +357,46 @@ class DynamicModelTests: BaseTestDataTestCase {
let dog = try transaction.fetchOne(From<Dog>().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<Person>())
XCTAssertNotNil(person)
XCTAssertEqual(person!.pets.value.first, dog)
let p3 = Where<Dog>({ $0.age == 10 })
let p3 = Where<Dog>({ $0.$age == 10 })
XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10))
let totalAge = try transaction.queryValue(From<Dog>().select(Int.self, .sum(\Dog.age)))
let totalAge = try transaction.queryValue(From<Dog>().select(Int.self, .sum(\Dog.$age)))
XCTAssertEqual(totalAge, 1)
_ = try transaction.fetchAll(
From<Dog>()
.where(\Animal.species == "Dog" && \Dog.age == 10)
.where(\Animal.$species == "Dog" && \Dog.$age == 10)
)
_ = try transaction.fetchAll(
From<Dog>()
.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<Dog>(),
Where<Dog>({ $0.age > 10 && $0.age <= 15 })
Where<Dog>({ $0.$age > 10 && $0.$age <= 15 })
)
_ = try transaction.fetchAll(
From<Dog>(),
Where<Dog>({ $0.species == "Dog" && $0.age == 10 })
Where<Dog>({ $0.$species == "Dog" && $0.$age == 10 })
)
_ = try transaction.fetchAll(
From<Dog>(),
Where<Dog>({ $0.age == 10 && $0.species == "Dog" })
Where<Dog>({ $0.$age == 10 && $0.$species == "Dog" })
)
_ = try transaction.fetchAll(
From<Dog>(),
Where<Dog>({ $0.age > 10 && $0.age <= 15 })
Where<Dog>({ $0.$age > 10 && $0.$age <= 15 })
)
_ = try transaction.fetchAll(
From<Dog>(),
(\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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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<Animal> = (\.master ~ \.pets ~ \.species).any() == dummy
let whereClause: Where<Animal> = (\.master ~ \.pets ~ \.$species).any() == dummy
let predicate = NSPredicate(format: "ANY master.pets.species == %@", dummy)
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
XCTAssertAllEqual(whereClause.predicate, predicate)
@@ -319,7 +319,7 @@ final class WhereTests: XCTestCase {
}
do {
let whereClause: Where<Animal> = (\.master ~ \.pets ~ \.species).all() == dummy
let whereClause: Where<Animal> = (\.master ~ \.pets ~ \.$species).all() == dummy
let predicate = NSPredicate(format: "ALL master.pets.species == %@", dummy)
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
XCTAssertAllEqual(whereClause.predicate, predicate)
@@ -337,7 +337,7 @@ final class WhereTests: XCTestCase {
}
do {
let whereClause: Where<Animal> = (\.master ~ \.pets ~ \.species).none() == dummy
let whereClause: Where<Animal> = (\.master ~ \.pets ~ \.$species).none() == dummy
let predicate = NSPredicate(format: "NONE master.pets.species == %@", dummy)
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
XCTAssertAllEqual(whereClause.predicate, predicate)

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -73,6 +73,7 @@ public enum ValueContainer<O: CoreStoreObject> {
```
- 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<V: ImportableAttributeType>: AttributeKeyPathStringConvertible, AttributeProtocol {
/**
@@ -110,7 +111,7 @@ public enum ValueContainer<O: CoreStoreObject> {
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject<O>`, 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<O>`, make sure to use `PartialObject<O>.setPrimitiveValue(_:for:)` instead of `PartialObject<O>.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<O: CoreStoreObject> {
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
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<O: CoreStoreObject> {
*/
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<O>) -> V)? = nil,
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
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<O>(object))
return customGetter(PartialObject<O>(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<O: CoreStoreObject> {
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<O>(object), newValue)
return customSetter(PartialObject<O>(rawObject), newValue)
}
return object.setValue(
return rawObject.setValue(
newValue.cs_toQueryableNativeType(),
forKey: self.keyPath
)
}
}
}
public var projectedValue: ValueContainer<O>.Required<V> {
return self
}
// MARK: AnyKeyPathStringConvertible
@@ -293,6 +336,7 @@ public enum ValueContainer<O: CoreStoreObject> {
```
- 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<V: ImportableAttributeType>: AttributeKeyPathStringConvertible, AttributeProtocol {
/**
@@ -333,7 +377,7 @@ public enum ValueContainer<O: CoreStoreObject> {
- 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<O: CoreStoreObject> {
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
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<O: CoreStoreObject> {
*/
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<O>) -> V?)? = nil,
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
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<O>(object))
return customGetter(PartialObject<O>(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<O: CoreStoreObject> {
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<O>(object), newValue)
return customSetter(PartialObject<O>(rawObject), newValue)
}
object.setValue(
rawObject.setValue(
newValue?.cs_toQueryableNativeType(),
forKey: self.keyPath
)
}
}
}
public var projectedValue: ValueContainer<O>.Optional<V> {
return self
}
// MARK: AnyKeyPathStringConvertible