diff --git a/CHANGELOG.md b/CHANGELOG.md index f3b48f3145..a981779636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/). - [#443](https://github.com/groue/GRDB.swift/pull/443): In place record update - [#444](https://github.com/groue/GRDB.swift/pull/444): Combine Value Observations - [#451](https://github.com/groue/GRDB.swift/pull/451): ValueObservation.compactMap +- [#452](https://github.com/groue/GRDB.swift/pull/452): ValueObservation.mapReducer - [#445](https://github.com/groue/GRDB.swift/pull/445): Quality of service and target dispatch queue - ValueObservation methods which used to accept a variadic list of observed regions now also accept an array. - ValueReducer, the protocol that fuels ValueObservation, is flagged [**:fire: EXPERIMENTAL**](README.md#what-are-experimental-features). It will remain so until more experience has been acquired. @@ -111,7 +112,11 @@ GRDB adheres to [Semantic Versioning](https://semver.org/). + func compactMap(_ transform: @escaping (Reducer.Value) -> T?) + -> ValueObservation> +} - + ++extension ValueObservation { ++ func mapReducer(_ transform: @escaping (Database, Reducer) throws -> R) ++ -> ValueObservation ++} struct Configuration { + var qos: DispatchQoS diff --git a/GRDB.xcodeproj/project.pbxproj b/GRDB.xcodeproj/project.pbxproj index 64db67dbc2..ca19ec9f61 100755 --- a/GRDB.xcodeproj/project.pbxproj +++ b/GRDB.xcodeproj/project.pbxproj @@ -211,6 +211,9 @@ 564CE4E721B2E06800652B19 /* ValueObservationCompactMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4E521B2E06700652B19 /* ValueObservationCompactMapTests.swift */; }; 564CE4E921B2E06F00652B19 /* ValueObservationMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4E821B2E06F00652B19 /* ValueObservationMapTests.swift */; }; 564CE4EA21B2E06F00652B19 /* ValueObservationMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4E821B2E06F00652B19 /* ValueObservationMapTests.swift */; }; + 564CE52021B3129A00652B19 /* ValueObservation+MapReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE51F21B3129900652B19 /* ValueObservation+MapReducer.swift */; }; + 564CE52121B3129A00652B19 /* ValueObservation+MapReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE51F21B3129900652B19 /* ValueObservation+MapReducer.swift */; }; + 564CE52221B3129A00652B19 /* ValueObservation+MapReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE51F21B3129900652B19 /* ValueObservation+MapReducer.swift */; }; 564E73DF203D50B9000C443C /* JoinSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E73DE203D50B9000C443C /* JoinSupportTests.swift */; }; 564E73E0203D50B9000C443C /* JoinSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E73DE203D50B9000C443C /* JoinSupportTests.swift */; }; 564F9C1E1F069B4E00877A00 /* DatabaseAggregateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564F9C1D1F069B4E00877A00 /* DatabaseAggregateTests.swift */; }; @@ -974,6 +977,7 @@ 564CE4D521B2DEB500652B19 /* ValueObservation+CompactMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ValueObservation+CompactMap.swift"; sourceTree = ""; }; 564CE4E521B2E06700652B19 /* ValueObservationCompactMapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueObservationCompactMapTests.swift; sourceTree = ""; }; 564CE4E821B2E06F00652B19 /* ValueObservationMapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueObservationMapTests.swift; sourceTree = ""; }; + 564CE51F21B3129900652B19 /* ValueObservation+MapReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ValueObservation+MapReducer.swift"; sourceTree = ""; }; 564E73DE203D50B9000C443C /* JoinSupportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinSupportTests.swift; sourceTree = ""; }; 564F9C1D1F069B4E00877A00 /* DatabaseAggregateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseAggregateTests.swift; sourceTree = ""; }; 564F9C2C1F075DD200877A00 /* DatabaseFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFunction.swift; sourceTree = ""; }; @@ -1403,6 +1407,7 @@ 5613ED4F21A95C6D00DC7A68 /* ValueObservation+DatabaseValueConvertible.swift */, 5613ED4B21A95C4300DC7A68 /* ValueObservation+FetchableRecord.swift */, 5613ED3421A95A5C00DC7A68 /* ValueObservation+Map.swift */, + 564CE51F21B3129900652B19 /* ValueObservation+MapReducer.swift */, 5613ED4721A95C1200DC7A68 /* ValueObservation+Row.swift */, 564CE43021AA901800652B19 /* ValueObserver.swift */, 5613ED4321A95B2C00DC7A68 /* ValueReducer.swift */, @@ -2508,6 +2513,7 @@ 565490C51D5AE236005622CB /* SerializedDatabase.swift in Sources */, 56F3E7691E67F8C100BF0F01 /* Fixits-0.101.1.swift in Sources */, 5698AC7E1DA37DCB0056AF8C /* VirtualTableModule.swift in Sources */, + 564CE52221B3129A00652B19 /* ValueObservation+MapReducer.swift in Sources */, 5653EB1A20944C7C00F46237 /* ForeignKey.swift in Sources */, 5698AC3D1D9E5A590056AF8C /* FTS3Pattern.swift in Sources */, 563EF45D2163309F007DAACD /* Inflections.swift in Sources */, @@ -2658,6 +2664,7 @@ 56D51D031EA789FA0074638A /* FetchableRecord+TableRecord.swift in Sources */, 5653EB1620944C7C00F46237 /* AssociationQuery.swift in Sources */, 560D924C1C672C4B00F4F92B /* TableRecord.swift in Sources */, + 564CE52121B3129A00652B19 /* ValueObservation+MapReducer.swift in Sources */, 5653EC132098738B00F46237 /* SQLGenerationContext.swift in Sources */, 5657AB121D10899D006283EF /* URL.swift in Sources */, 5616AAF2207CD45E00AC3664 /* RequestProtocols.swift in Sources */, @@ -3118,6 +3125,7 @@ 566B91131FA4C3F50012D5B0 /* DatabaseCollation.swift in Sources */, 5605F1591C672E4000235C62 /* CGFloat.swift in Sources */, 5674A7031F307FCD0095F066 /* DatabaseValueConvertible+ReferenceConvertible.swift in Sources */, + 564CE52021B3129A00652B19 /* ValueObservation+MapReducer.swift in Sources */, 5659F4A01EA8D997004A4992 /* Result.swift in Sources */, 5605F1631C672E4000235C62 /* Date.swift in Sources */, 5605F1711C672E4000235C62 /* DatabaseValueConvertible+RawRepresentable.swift in Sources */, diff --git a/GRDB/ValueObservation/ValueObservation+Combine.swift b/GRDB/ValueObservation/ValueObservation+Combine.swift index 64979a307b..da06efc207 100644 --- a/GRDB/ValueObservation/ValueObservation+Combine.swift +++ b/GRDB/ValueObservation/ValueObservation+Combine.swift @@ -12,7 +12,7 @@ extension ValueObservation where Reducer == Void { tracking: { try DatabaseRegion.union( o1.observedRegion($0), o2.observedRegion($0)) }, - reducer: { try ValueReducers.combine( + reducer: { try _combine( o1.makeReducer($0), o2.makeReducer($0)) }) } @@ -32,7 +32,7 @@ extension ValueObservation where Reducer == Void { o1.observedRegion($0), o2.observedRegion($0), o3.observedRegion($0)) }, - reducer: { try ValueReducers.combine( + reducer: { try _combine( o1.makeReducer($0), o2.makeReducer($0), o3.makeReducer($0)) }) @@ -55,7 +55,7 @@ extension ValueObservation where Reducer == Void { o2.observedRegion($0), o3.observedRegion($0), o4.observedRegion($0)) }, - reducer: { try ValueReducers.combine( + reducer: { try _combine( o1.makeReducer($0), o2.makeReducer($0), o3.makeReducer($0), @@ -81,7 +81,7 @@ extension ValueObservation where Reducer == Void { o3.observedRegion($0), o4.observedRegion($0), o5.observedRegion($0)) }, - reducer: { try ValueReducers.combine( + reducer: { try _combine( o1.makeReducer($0), o2.makeReducer($0), o3.makeReducer($0), @@ -90,186 +90,184 @@ extension ValueObservation where Reducer == Void { } } -extension ValueReducers { - static func combine( - _ r1: R1, - _ r2: R2) - -> AnyValueReducer< - (R1.Fetched, R2.Fetched), - (R1.Value, R2.Value)> - { - var r1 = r1 - var r2 = r2 - var prev1: R1.Value? - var prev2: R2.Value? - func fetch(db: Database) throws -> (R1.Fetched, R2.Fetched) { - return try ( - r1.fetch(db), - r2.fetch(db)) +private func _combine( + _ r1: R1, + _ r2: R2) + -> AnyValueReducer< + (R1.Fetched, R2.Fetched), + (R1.Value, R2.Value)> +{ + var r1 = r1 + var r2 = r2 + var prev1: R1.Value? + var prev2: R2.Value? + func fetch(db: Database) throws -> (R1.Fetched, R2.Fetched) { + return try ( + r1.fetch(db), + r2.fetch(db)) + } + func value(tuple: (R1.Fetched, R2.Fetched)) -> (R1.Value, R2.Value)? { + let v1 = r1.value(tuple.0) + let v2 = r2.value(tuple.1) + defer { + if let v1 = v1 { prev1 = v1 } + if let v2 = v2 { prev2 = v2 } } - func value(tuple: (R1.Fetched, R2.Fetched)) -> (R1.Value, R2.Value)? { - let v1 = r1.value(tuple.0) - let v2 = r2.value(tuple.1) - defer { - if let v1 = v1 { prev1 = v1 } - if let v2 = v2 { prev2 = v2 } - } - if v1 != nil || v2 != nil, - let c1 = v1 ?? prev1, - let c2 = v2 ?? prev2 - { - return (c1, c2) - } else { - return nil - } + if v1 != nil || v2 != nil, + let c1 = v1 ?? prev1, + let c2 = v2 ?? prev2 + { + return (c1, c2) + } else { + return nil } - return AnyValueReducer(fetch: fetch, value: value) } - - static func combine( - _ r1: R1, - _ r2: R2, - _ r3: R3) - -> AnyValueReducer< - (R1.Fetched, R2.Fetched, R3.Fetched), - (R1.Value, R2.Value, R3.Value)> - { - var r1 = r1 - var r2 = r2 - var r3 = r3 - var prev1: R1.Value? - var prev2: R2.Value? - var prev3: R3.Value? - func fetch(db: Database) throws -> (R1.Fetched, R2.Fetched, R3.Fetched) { - return try ( - r1.fetch(db), - r2.fetch(db), - r3.fetch(db)) + return AnyValueReducer(fetch: fetch, value: value) +} + +private func _combine( + _ r1: R1, + _ r2: R2, + _ r3: R3) + -> AnyValueReducer< + (R1.Fetched, R2.Fetched, R3.Fetched), + (R1.Value, R2.Value, R3.Value)> +{ + var r1 = r1 + var r2 = r2 + var r3 = r3 + var prev1: R1.Value? + var prev2: R2.Value? + var prev3: R3.Value? + func fetch(db: Database) throws -> (R1.Fetched, R2.Fetched, R3.Fetched) { + return try ( + r1.fetch(db), + r2.fetch(db), + r3.fetch(db)) + } + func value(tuple: (R1.Fetched, R2.Fetched, R3.Fetched)) -> (R1.Value, R2.Value, R3.Value)? { + let v1 = r1.value(tuple.0) + let v2 = r2.value(tuple.1) + let v3 = r3.value(tuple.2) + defer { + if let v1 = v1 { prev1 = v1 } + if let v2 = v2 { prev2 = v2 } + if let v3 = v3 { prev3 = v3 } } - func value(tuple: (R1.Fetched, R2.Fetched, R3.Fetched)) -> (R1.Value, R2.Value, R3.Value)? { - let v1 = r1.value(tuple.0) - let v2 = r2.value(tuple.1) - let v3 = r3.value(tuple.2) - defer { - if let v1 = v1 { prev1 = v1 } - if let v2 = v2 { prev2 = v2 } - if let v3 = v3 { prev3 = v3 } - } - if v1 != nil || v2 != nil || v3 != nil, - let c1 = v1 ?? prev1, - let c2 = v2 ?? prev2, - let c3 = v3 ?? prev3 - { - return (c1, c2, c3) - } else { - return nil - } + if v1 != nil || v2 != nil || v3 != nil, + let c1 = v1 ?? prev1, + let c2 = v2 ?? prev2, + let c3 = v3 ?? prev3 + { + return (c1, c2, c3) + } else { + return nil } - return AnyValueReducer(fetch: fetch, value: value) } - - static func combine( - _ r1: R1, - _ r2: R2, - _ r3: R3, - _ r4: R4) - -> AnyValueReducer< - (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched), - (R1.Value, R2.Value, R3.Value, R4.Value)> - { - var r1 = r1 - var r2 = r2 - var r3 = r3 - var r4 = r4 - var prev1: R1.Value? - var prev2: R2.Value? - var prev3: R3.Value? - var prev4: R4.Value? - func fetch(db: Database) throws -> (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched) { - return try ( - r1.fetch(db), - r2.fetch(db), - r3.fetch(db), - r4.fetch(db)) + return AnyValueReducer(fetch: fetch, value: value) +} + +private func _combine( + _ r1: R1, + _ r2: R2, + _ r3: R3, + _ r4: R4) + -> AnyValueReducer< + (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched), + (R1.Value, R2.Value, R3.Value, R4.Value)> +{ + var r1 = r1 + var r2 = r2 + var r3 = r3 + var r4 = r4 + var prev1: R1.Value? + var prev2: R2.Value? + var prev3: R3.Value? + var prev4: R4.Value? + func fetch(db: Database) throws -> (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched) { + return try ( + r1.fetch(db), + r2.fetch(db), + r3.fetch(db), + r4.fetch(db)) + } + func value(tuple: (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched)) -> (R1.Value, R2.Value, R3.Value, R4.Value)? { + let v1 = r1.value(tuple.0) + let v2 = r2.value(tuple.1) + let v3 = r3.value(tuple.2) + let v4 = r4.value(tuple.3) + defer { + if let v1 = v1 { prev1 = v1 } + if let v2 = v2 { prev2 = v2 } + if let v3 = v3 { prev3 = v3 } + if let v4 = v4 { prev4 = v4 } } - func value(tuple: (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched)) -> (R1.Value, R2.Value, R3.Value, R4.Value)? { - let v1 = r1.value(tuple.0) - let v2 = r2.value(tuple.1) - let v3 = r3.value(tuple.2) - let v4 = r4.value(tuple.3) - defer { - if let v1 = v1 { prev1 = v1 } - if let v2 = v2 { prev2 = v2 } - if let v3 = v3 { prev3 = v3 } - if let v4 = v4 { prev4 = v4 } - } - if v1 != nil || v2 != nil || v3 != nil || v4 != nil, - let c1 = v1 ?? prev1, - let c2 = v2 ?? prev2, - let c3 = v3 ?? prev3, - let c4 = v4 ?? prev4 - { - return (c1, c2, c3, c4) - } else { - return nil - } + if v1 != nil || v2 != nil || v3 != nil || v4 != nil, + let c1 = v1 ?? prev1, + let c2 = v2 ?? prev2, + let c3 = v3 ?? prev3, + let c4 = v4 ?? prev4 + { + return (c1, c2, c3, c4) + } else { + return nil } - return AnyValueReducer(fetch: fetch, value: value) } - - static func combine( - _ r1: R1, - _ r2: R2, - _ r3: R3, - _ r4: R4, - _ r5: R5) - -> AnyValueReducer< - (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched, R5.Fetched), - (R1.Value, R2.Value, R3.Value, R4.Value, R5.Value)> - { - var r1 = r1 - var r2 = r2 - var r3 = r3 - var r4 = r4 - var r5 = r5 - var prev1: R1.Value? - var prev2: R2.Value? - var prev3: R3.Value? - var prev4: R4.Value? - var prev5: R5.Value? - func fetch(db: Database) throws -> (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched, R5.Fetched) { - return try ( - r1.fetch(db), - r2.fetch(db), - r3.fetch(db), - r4.fetch(db), - r5.fetch(db)) + return AnyValueReducer(fetch: fetch, value: value) +} + +private func _combine( + _ r1: R1, + _ r2: R2, + _ r3: R3, + _ r4: R4, + _ r5: R5) + -> AnyValueReducer< + (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched, R5.Fetched), + (R1.Value, R2.Value, R3.Value, R4.Value, R5.Value)> +{ + var r1 = r1 + var r2 = r2 + var r3 = r3 + var r4 = r4 + var r5 = r5 + var prev1: R1.Value? + var prev2: R2.Value? + var prev3: R3.Value? + var prev4: R4.Value? + var prev5: R5.Value? + func fetch(db: Database) throws -> (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched, R5.Fetched) { + return try ( + r1.fetch(db), + r2.fetch(db), + r3.fetch(db), + r4.fetch(db), + r5.fetch(db)) + } + func value(tuple: (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched, R5.Fetched)) -> (R1.Value, R2.Value, R3.Value, R4.Value, R5.Value)? { + let v1 = r1.value(tuple.0) + let v2 = r2.value(tuple.1) + let v3 = r3.value(tuple.2) + let v4 = r4.value(tuple.3) + let v5 = r5.value(tuple.4) + defer { + if let v1 = v1 { prev1 = v1 } + if let v2 = v2 { prev2 = v2 } + if let v3 = v3 { prev3 = v3 } + if let v4 = v4 { prev4 = v4 } + if let v5 = v5 { prev5 = v5 } } - func value(tuple: (R1.Fetched, R2.Fetched, R3.Fetched, R4.Fetched, R5.Fetched)) -> (R1.Value, R2.Value, R3.Value, R4.Value, R5.Value)? { - let v1 = r1.value(tuple.0) - let v2 = r2.value(tuple.1) - let v3 = r3.value(tuple.2) - let v4 = r4.value(tuple.3) - let v5 = r5.value(tuple.4) - defer { - if let v1 = v1 { prev1 = v1 } - if let v2 = v2 { prev2 = v2 } - if let v3 = v3 { prev3 = v3 } - if let v4 = v4 { prev4 = v4 } - if let v5 = v5 { prev5 = v5 } - } - if v1 != nil || v2 != nil || v3 != nil || v4 != nil || v5 != nil, - let c1 = v1 ?? prev1, - let c2 = v2 ?? prev2, - let c3 = v3 ?? prev3, - let c4 = v4 ?? prev4, - let c5 = v5 ?? prev5 - { - return (c1, c2, c3, c4, c5) - } else { - return nil - } + if v1 != nil || v2 != nil || v3 != nil || v4 != nil || v5 != nil, + let c1 = v1 ?? prev1, + let c2 = v2 ?? prev2, + let c3 = v3 ?? prev3, + let c4 = v4 ?? prev4, + let c5 = v5 ?? prev5 + { + return (c1, c2, c3, c4, c5) + } else { + return nil } - return AnyValueReducer(fetch: fetch, value: value) } + return AnyValueReducer(fetch: fetch, value: value) } diff --git a/GRDB/ValueObservation/ValueObservation+CompactMap.swift b/GRDB/ValueObservation/ValueObservation+CompactMap.swift index 66edf990e5..5778b5ab5c 100644 --- a/GRDB/ValueObservation/ValueObservation+CompactMap.swift +++ b/GRDB/ValueObservation/ValueObservation+CompactMap.swift @@ -5,18 +5,13 @@ extension ValueObservation where Reducer: ValueReducer { public func compactMap(_ transform: @escaping (Reducer.Value) -> T?) -> ValueObservation> { - let makeReducer = self.makeReducer - var observation = ValueObservation>( - tracking: observedRegion, - reducer: { db in try makeReducer(db).compactMap(transform) }) - observation.extent = extent - observation.scheduling = scheduling - observation.requiresWriteAccess = requiresWriteAccess - return observation + return mapReducer { $1.compactMap(transform) } } } extension ValueReducer { + /// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) + /// /// Returns a reducer which outputs the non-nil results of calling the given /// transformation which each element emitted by this reducer. public func compactMap(_ transform: @escaping (Value) -> T?) -> CompactMapValueReducer { @@ -24,6 +19,8 @@ extension ValueReducer { } } +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// /// See ValueReducer.compactMap(_:) /// /// :nodoc: diff --git a/GRDB/ValueObservation/ValueObservation+Count.swift b/GRDB/ValueObservation/ValueObservation+Count.swift index f455db68f5..fec32c8627 100644 --- a/GRDB/ValueObservation/ValueObservation+Count.swift +++ b/GRDB/ValueObservation/ValueObservation+Count.swift @@ -26,7 +26,7 @@ extension ValueObservation where Reducer == Void { /// - parameter request: the observed request. /// - returns: a ValueObservation. public static func trackingCount(_ request: Request) - -> ValueObservation> + -> ValueObservation> { return ValueObservation.tracking(request, fetchDistinct: request.fetchCount) } diff --git a/GRDB/ValueObservation/ValueObservation+DatabaseValueConvertible.swift b/GRDB/ValueObservation/ValueObservation+DatabaseValueConvertible.swift index f92572f05e..c32634532e 100644 --- a/GRDB/ValueObservation/ValueObservation+DatabaseValueConvertible.swift +++ b/GRDB/ValueObservation/ValueObservation+DatabaseValueConvertible.swift @@ -27,12 +27,12 @@ extension ValueObservation where Reducer == Void { /// - parameter request: the observed request. /// - returns: a ValueObservation. public static func trackingAll(_ request: Request) - -> ValueObservation> + -> ValueObservation> where Request.RowDecoder: DatabaseValueConvertible { - return ValueObservation>.tracking( - request, - reducer: { _ in ValueReducers.Values { try DatabaseValue.fetchAll($0, request) } }) + return ValueObservation>.tracking(request, reducer: { _ in + DatabaseValuesReducer(request: request) } + ) } /// Creates a ValueObservation which observes *request*, and notifies a @@ -59,12 +59,11 @@ extension ValueObservation where Reducer == Void { /// - parameter request: the observed request. /// - returns: a ValueObservation. public static func trackingOne(_ request: Request) - -> ValueObservation> + -> ValueObservation> where Request.RowDecoder: DatabaseValueConvertible { - return ValueObservation>.tracking( - request, - reducer: { _ in ValueReducers.Value { try DatabaseValue.fetchOne($0, request) } }) + return ValueObservation>.tracking(request, reducer: { _ in DatabaseValueReducer(request: request) + }) } /// Creates a ValueObservation which observes *request*, and notifies @@ -92,108 +91,120 @@ extension ValueObservation where Reducer == Void { /// - parameter request: the observed request. /// - returns: a ValueObservation. public static func trackingAll(_ request: Request) - -> ValueObservation> + -> ValueObservation> where Request.RowDecoder: _OptionalProtocol, Request.RowDecoder._Wrapped: DatabaseValueConvertible { - return ValueObservation>.tracking( - request, - reducer: { _ in ValueReducers.OptionalValues { try DatabaseValue.fetchAll($0, request) } }) + return ValueObservation>.tracking(request, reducer: { _ in + OptionalDatabaseValuesReducer(request: request) + }) } } -extension ValueReducers { - /// A reducer which outputs arrays of values, filtering out consecutive - /// identical database values. - public struct Values: ValueReducer { - private let _fetch: (Database) throws -> [DatabaseValue] - private var previousDbValues: [DatabaseValue]? - - init(_ fetch: @escaping (Database) throws -> [DatabaseValue]) { - self._fetch = fetch - } - - /// :nodoc: - public func fetch(_ db: Database) throws -> [DatabaseValue] { - return try _fetch(db) +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// +/// A reducer which outputs arrays of values, filtering out consecutive +/// identical database values. +/// +/// :nodoc: +public struct DatabaseValuesReducer: ValueReducer + where Request.RowDecoder: DatabaseValueConvertible +{ + public let request: Request + private var previousDbValues: [DatabaseValue]? + + init(request: Request) { + self.request = request + } + + public func fetch(_ db: Database) throws -> [DatabaseValue] { + return try DatabaseValue.fetchAll(db, request) + } + + public mutating func value(_ dbValues: [DatabaseValue]) -> [Request.RowDecoder]? { + if let previousDbValues = previousDbValues, previousDbValues == dbValues { + // Don't notify consecutive identical dbValue arrays + return nil } - - /// :nodoc: - public mutating func value(_ dbValues: [DatabaseValue]) -> [T]? { - if let previousDbValues = previousDbValues, previousDbValues == dbValues { - // Don't notify consecutive identical dbValue arrays - return nil - } - self.previousDbValues = dbValues - return dbValues.map { - T.decode(from: $0, conversionContext: nil) - } + self.previousDbValues = dbValues + return dbValues.map { + Request.RowDecoder.decode(from: $0, conversionContext: nil) } } +} + +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// +/// A reducer which outputs optional values, filtering out consecutive +/// identical database values. +/// +/// :nodoc: +public struct DatabaseValueReducer: ValueReducer + where Request.RowDecoder: DatabaseValueConvertible +{ + public let request: Request + private var previousDbValue: DatabaseValue?? + private var previousValueWasNil = false - /// A reducer which outputs optional values, filtering out consecutive - /// identical database values. - public struct Value: ValueReducer { - private let _fetch: (Database) throws -> DatabaseValue? - private var previousDbValue: DatabaseValue?? - private var previousValueWasNil = false - - init(_ fetch: @escaping (Database) throws -> DatabaseValue?) { - self._fetch = fetch - } - - /// :nodoc: - public func fetch(_ db: Database) throws -> DatabaseValue? { - return try _fetch(db) + init(request: Request) { + self.request = request + } + + public func fetch(_ db: Database) throws -> DatabaseValue? { + return try DatabaseValue.fetchOne(db, request) + } + + public mutating func value(_ dbValue: DatabaseValue?) -> Request.RowDecoder?? { + if let previousDbValue = previousDbValue, previousDbValue == dbValue { + // Don't notify consecutive identical dbValue + return nil } - - /// :nodoc: - public mutating func value(_ dbValue: DatabaseValue?) -> T?? { - if let previousDbValue = previousDbValue, previousDbValue == dbValue { - // Don't notify consecutive identical dbValue - return nil - } - self.previousDbValue = dbValue - if let dbValue = dbValue, - let value = T.decodeIfPresent(from: dbValue, conversionContext: nil) - { - previousValueWasNil = false - return .some(value) - } else if previousValueWasNil { - // Don't notify consecutive nil values - return nil - } else { - previousValueWasNil = true - return .some(nil) - } + self.previousDbValue = dbValue + if let dbValue = dbValue, + let value = Request.RowDecoder.decodeIfPresent(from: dbValue, conversionContext: nil) + { + previousValueWasNil = false + return .some(value) + } else if previousValueWasNil { + // Don't notify consecutive nil values + return nil + } else { + previousValueWasNil = true + return .some(nil) } } +} + +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// +/// A reducer which outputs arrays of optional values, filtering out consecutive +/// identical database values. +/// +/// :nodoc: +public struct OptionalDatabaseValuesReducer: ValueReducer + where + Request.RowDecoder: _OptionalProtocol, + Request.RowDecoder._Wrapped: DatabaseValueConvertible +{ + public let request: Request + private var previousDbValues: [DatabaseValue]? + + init(request: Request) { + self.request = request + } - /// A reducer which outputs arrays of optional values, filtering out consecutive - /// identical database values. - public struct OptionalValues: ValueReducer { - private let _fetch: (Database) throws -> [DatabaseValue] - private var previousDbValues: [DatabaseValue]? - - init(_ fetch: @escaping (Database) throws -> [DatabaseValue]) { - self._fetch = fetch - } - - /// :nodoc: - public func fetch(_ db: Database) throws -> [DatabaseValue] { - return try _fetch(db) + public func fetch(_ db: Database) throws -> [DatabaseValue] { + return try DatabaseValue.fetchAll(db, request) + } + + public mutating func value(_ dbValues: [DatabaseValue]) -> [Request.RowDecoder._Wrapped?]? { + if let previousDbValues = previousDbValues, previousDbValues == dbValues { + // Don't notify consecutive identical dbValue arrays + return nil } - - /// :nodoc: - public mutating func value(_ dbValues: [DatabaseValue]) -> [T?]? { - if let previousDbValues = previousDbValues, previousDbValues == dbValues { - // Don't notify consecutive identical dbValue arrays - return nil - } - self.previousDbValues = dbValues - return dbValues.map { - T.decodeIfPresent(from: $0, conversionContext: nil) - } + self.previousDbValues = dbValues + return dbValues.map { + Request.RowDecoder._Wrapped.decodeIfPresent(from: $0, conversionContext: nil) } } } diff --git a/GRDB/ValueObservation/ValueObservation+FetchableRecord.swift b/GRDB/ValueObservation/ValueObservation+FetchableRecord.swift index 6b30f1ce11..1f18722fd8 100644 --- a/GRDB/ValueObservation/ValueObservation+FetchableRecord.swift +++ b/GRDB/ValueObservation/ValueObservation+FetchableRecord.swift @@ -1,5 +1,5 @@ extension ValueObservation where Reducer == Void { - + // MARK: - FetchableRecord Observation /// Creates a ValueObservation which observes *request*, and notifies @@ -27,12 +27,12 @@ extension ValueObservation where Reducer == Void { /// - parameter request: the observed request. /// - returns: a ValueObservation. public static func trackingAll(_ request: Request) - -> ValueObservation> + -> ValueObservation> where Request.RowDecoder: FetchableRecord { - return ValueObservation>.tracking( - request, - reducer: { _ in ValueReducers.Records { try Row.fetchAll($0, request) } }) + return ValueObservation>.tracking(request, reducer: { _ in + FetchableRecordsReducer(request: request) + }) } /// Creates a ValueObservation which observes *request*, and notifies a @@ -59,65 +59,71 @@ extension ValueObservation where Reducer == Void { /// - parameter request: the observed request. /// - returns: a ValueObservation. public static func trackingOne(_ request: Request) -> - ValueObservation> + ValueObservation> where Request.RowDecoder: FetchableRecord { - return ValueObservation>.tracking( - request, - reducer: { _ in ValueReducers.Record { try Row.fetchOne($0, request) } }) + return ValueObservation>.tracking(request, reducer: { _ in + FetchableRecordReducer(request: request) + }) } } -extension ValueReducers { - /// A reducer which outputs arrays of records, filtering out consecutive - /// identical database rows. - public struct Records: ValueReducer { - private let _fetch: (Database) throws -> [Row] - private var previousRows: [Row]? - - init(_ fetch: @escaping (Database) throws -> [Row]) { - self._fetch = fetch - } - - /// :nodoc: - public func fetch(_ db: Database) throws -> [Row] { - return try _fetch(db) - } - - /// :nodoc: - public mutating func value(_ rows: [Row]) -> [Record]? { - if let previousRows = previousRows, previousRows == rows { - // Don't notify consecutive identical row arrays - return nil - } - self.previousRows = rows - return rows.map(Record.init(row:)) - } +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// +/// A reducer which outputs arrays of records, filtering out consecutive +/// identical database rows. +/// +/// :nodoc: +public struct FetchableRecordsReducer: ValueReducer + where Request.RowDecoder: FetchableRecord +{ + public let request: Request + private var previousRows: [Row]? + + init(request: Request) { + self.request = request } - /// A reducer which outputs optional records, filtering out consecutive - /// identical database rows. - public struct Record: ValueReducer { - private let _fetch: (Database) throws -> Row? - private var previousRow: Row?? - - init(_ fetch: @escaping (Database) throws -> Row?) { - self._fetch = fetch - } - - /// :nodoc: - public func fetch(_ db: Database) throws -> Row? { - return try _fetch(db) + public func fetch(_ db: Database) throws -> [Row] { + return try Row.fetchAll(db, request) + } + + public mutating func value(_ rows: [Row]) -> [Request.RowDecoder]? { + if let previousRows = previousRows, previousRows == rows { + // Don't notify consecutive identical row arrays + return nil } - - /// :nodoc: - public mutating func value(_ row: Row?) -> Record?? { - if let previousRow = previousRow, previousRow == row { - // Don't notify consecutive identical rows - return nil - } - self.previousRow = row - return .some(row.map(Record.init(row:))) + self.previousRows = rows + return rows.map(Request.RowDecoder.init(row:)) + } +} + +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// +/// A reducer which outputs optional records, filtering out consecutive +/// identical database rows. +/// +/// :nodoc: +public struct FetchableRecordReducer: ValueReducer + where Request.RowDecoder: FetchableRecord +{ + public let request: Request + private var previousRow: Row?? + + init(request: Request) { + self.request = request + } + + public func fetch(_ db: Database) throws -> Row? { + return try Row.fetchOne(db, request) + } + + public mutating func value(_ row: Row?) -> Request.RowDecoder?? { + if let previousRow = previousRow, previousRow == row { + // Don't notify consecutive identical rows + return nil } + self.previousRow = row + return .some(row.map(Request.RowDecoder.init(row:))) } } diff --git a/GRDB/ValueObservation/ValueObservation+Map.swift b/GRDB/ValueObservation/ValueObservation+Map.swift index c9e510b7e2..cefd648b51 100644 --- a/GRDB/ValueObservation/ValueObservation+Map.swift +++ b/GRDB/ValueObservation/ValueObservation+Map.swift @@ -5,18 +5,13 @@ extension ValueObservation where Reducer: ValueReducer { public func map(_ transform: @escaping (Reducer.Value) -> T) -> ValueObservation> { - let makeReducer = self.makeReducer - var observation = ValueObservation>( - tracking: observedRegion, - reducer: { db in try makeReducer(db).map(transform) }) - observation.extent = extent - observation.scheduling = scheduling - observation.requiresWriteAccess = requiresWriteAccess - return observation + return mapReducer { $1.map(transform) } } } extension ValueReducer { + /// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) + /// /// Returns a reducer which outputs the results of calling the given /// transformation which each element emitted by this reducer. public func map(_ transform: @escaping (Value) -> T) -> MapValueReducer { @@ -24,6 +19,8 @@ extension ValueReducer { } } +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// /// A ValueReducer whose values consist of those in a Base ValueReducer passed /// through a transform function. /// diff --git a/GRDB/ValueObservation/ValueObservation+MapReducer.swift b/GRDB/ValueObservation/ValueObservation+MapReducer.swift new file mode 100644 index 0000000000..baea470b8c --- /dev/null +++ b/GRDB/ValueObservation/ValueObservation+MapReducer.swift @@ -0,0 +1,15 @@ +extension ValueObservation { + /// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) + /// + /// Returns a ValueObservation with a transformed reducer. + public func mapReducer(_ transform: @escaping (Database, Reducer) throws -> R) -> ValueObservation { + let makeReducer = self.makeReducer + var observation = ValueObservation( + tracking: observedRegion, + reducer: { db in try transform(db, makeReducer(db)) }) + observation.extent = extent + observation.scheduling = scheduling + observation.requiresWriteAccess = requiresWriteAccess + return observation + } +} diff --git a/GRDB/ValueObservation/ValueObservation+Row.swift b/GRDB/ValueObservation/ValueObservation+Row.swift index 35dee402fe..e041f2e066 100644 --- a/GRDB/ValueObservation/ValueObservation+Row.swift +++ b/GRDB/ValueObservation/ValueObservation+Row.swift @@ -26,10 +26,12 @@ extension ValueObservation where Reducer == Void { /// - parameter request: the observed request. /// - returns: a ValueObservation. public static func trackingAll(_ request: Request) - -> ValueObservation> + -> ValueObservation> where Request.RowDecoder == Row { - return ValueObservation.tracking(request, fetchDistinct: request.fetchAll) + return ValueObservation>.tracking(request, reducer: { _ in + RowsReducer(request: request) + }) } /// Creates a ValueObservation which observes *request*, and notifies a @@ -56,9 +58,71 @@ extension ValueObservation where Reducer == Void { /// - parameter request: the observed request. /// - returns: a ValueObservation. public static func trackingOne(_ request: Request) - -> ValueObservation> + -> ValueObservation> where Request.RowDecoder == Row { - return ValueObservation.tracking(request, fetchDistinct: request.fetchOne) + return ValueObservation>.tracking(request, reducer: { _ in + RowReducer(request: request) + }) + } +} + +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// +/// A reducer which outputs arrays of database rows, filtering out +/// consecutive identical arrays. +/// +/// :nodoc: +public struct RowsReducer: ValueReducer + where Request.RowDecoder == Row +{ + public let request: Request + private var previousRows: [Row]? + + init(request: Request) { + self.request = request + } + + public func fetch(_ db: Database) throws -> [Row] { + return try request.fetchAll(db) + } + + public mutating func value(_ rows: [Row]) -> [Row]? { + if let previousRows = previousRows, previousRows == rows { + // Don't notify consecutive identical row arrays + return nil + } + self.previousRows = rows + return rows + } +} + +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// +/// A reducer which outputs optional records, filtering out consecutive +/// identical database rows. +/// +/// :nodoc: +public struct RowReducer: ValueReducer + where Request.RowDecoder == Row +{ + public let request: Request + private var previousRow: Row?? + + init(request: Request) { + self.request = request + } + + public func fetch(_ db: Database) throws -> Row? { + return try request.fetchOne(db) + } + + public mutating func value(_ row: Row?) -> Row?? { + if let previousRow = previousRow, previousRow == row { + // Don't notify consecutive identical rows + return nil + } + self.previousRow = row + return row } } diff --git a/GRDB/ValueObservation/ValueObservation.swift b/GRDB/ValueObservation/ValueObservation.swift index e6727c8a92..73a40a4832 100644 --- a/GRDB/ValueObservation/ValueObservation.swift +++ b/GRDB/ValueObservation/ValueObservation.swift @@ -326,7 +326,7 @@ extension ValueObservation where Reducer == Void { public static func tracking( _ regions: DatabaseRegionConvertible..., fetch: @escaping (Database) throws -> Value) - -> ValueObservation> + -> ValueObservation> { return ValueObservation.tracking(regions, fetch: fetch) } @@ -359,11 +359,11 @@ extension ValueObservation where Reducer == Void { public static func tracking( _ regions: [DatabaseRegionConvertible], fetch: @escaping (Database) throws -> Value) - -> ValueObservation> + -> ValueObservation> { - return ValueObservation>( + return ValueObservation>( tracking: DatabaseRegion.union(regions), - reducer: { _ in ValueReducers.Raw(fetch) }) + reducer: { _ in RawValueReducer(fetch) }) } /// Creates a ValueObservation which observes *regions*, and notifies the @@ -395,7 +395,7 @@ extension ValueObservation where Reducer == Void { public static func tracking( _ regions: DatabaseRegionConvertible..., fetchDistinct fetch: @escaping (Database) throws -> Value) - -> ValueObservation> + -> ValueObservation> where Value: Equatable { return ValueObservation.tracking(regions, fetchDistinct: fetch) @@ -430,11 +430,11 @@ extension ValueObservation where Reducer == Void { public static func tracking( _ regions: [DatabaseRegionConvertible], fetchDistinct fetch: @escaping (Database) throws -> Value) - -> ValueObservation> + -> ValueObservation> where Value: Equatable { - return ValueObservation>( + return ValueObservation>( tracking: DatabaseRegion.union(regions), - reducer: { _ in ValueReducers.Distinct(fetch) }) + reducer: { _ in DistinctValueReducer(fetch) }) } } diff --git a/GRDB/ValueObservation/ValueReducer.swift b/GRDB/ValueObservation/ValueReducer.swift index b2fcc9b12b..6828451de8 100644 --- a/GRDB/ValueObservation/ValueReducer.swift +++ b/GRDB/ValueObservation/ValueReducer.swift @@ -70,49 +70,49 @@ public struct AnyValueReducer: ValueReducer { } /// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) -public enum ValueReducers { - /// A reducer which outputs raw database values, without any processing. - public struct Raw: ValueReducer { - private let _fetch: (Database) throws -> Value - - init(_ fetch: @escaping (Database) throws -> Value) { - self._fetch = fetch - } - - /// :nodoc: - public func fetch(_ db: Database) throws -> Value { - return try _fetch(db) - } - - /// :nodoc: - public func value(_ fetched: Value) -> Value? { - return fetched - } +/// +/// A reducer which outputs raw values. +/// +/// :nodoc: +public struct RawValueReducer: ValueReducer { + private let _fetch: (Database) throws -> Value + + public init(_ fetch: @escaping (Database) throws -> Value) { + self._fetch = fetch } - /// A reducer which outputs raw database values, filtering out consecutive - /// values that are equal. - public struct Distinct: ValueReducer { - private let _fetch: (Database) throws -> Value - private var previousValue: Value?? - - init(_ fetch: @escaping (Database) throws -> Value) { - self._fetch = fetch - } - - /// :nodoc: - public func fetch(_ db: Database) throws -> Value { - return try _fetch(db) - } - - /// :nodoc: - public mutating func value(_ value: Value) -> Value? { - if let previousValue = previousValue, previousValue == value { - // Don't notify consecutive identical values - return nil - } - self.previousValue = value - return value + public func fetch(_ db: Database) throws -> Value { + return try _fetch(db) + } + + public func value(_ fetched: Value) -> Value? { + return fetched + } +} + +/// [**Experimental**](http://github.com/groue/GRDB.swift#what-are-experimental-features) +/// +/// A reducer which filters out consecutive values that are equal. +/// +/// :nodoc: +public struct DistinctValueReducer: ValueReducer { + private let _fetch: (Database) throws -> Value + private var previousValue: Value?? + + public init(_ fetch: @escaping (Database) throws -> Value) { + self._fetch = fetch + } + + public func fetch(_ db: Database) throws -> Value { + return try _fetch(db) + } + + public mutating func value(_ value: Value) -> Value? { + if let previousValue = previousValue, previousValue == value { + // Don't notify consecutive identical values + return nil } + self.previousValue = value + return value } } diff --git a/GRDBCipher.xcodeproj/project.pbxproj b/GRDBCipher.xcodeproj/project.pbxproj index a1843a183b..b218fa0cbe 100755 --- a/GRDBCipher.xcodeproj/project.pbxproj +++ b/GRDBCipher.xcodeproj/project.pbxproj @@ -272,6 +272,8 @@ 564CE4F221B2E08A00652B19 /* ValueObservationMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4F021B2E08A00652B19 /* ValueObservationMapTests.swift */; }; 564CE4F321B2E08A00652B19 /* ValueObservationMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4F021B2E08A00652B19 /* ValueObservationMapTests.swift */; }; 564CE4F421B2E08A00652B19 /* ValueObservationMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4F021B2E08A00652B19 /* ValueObservationMapTests.swift */; }; + 564CE52621B312B500652B19 /* ValueObservation+MapReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE51A21B3128400652B19 /* ValueObservation+MapReducer.swift */; }; + 564CE52721B312B700652B19 /* ValueObservation+MapReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE51A21B3128400652B19 /* ValueObservation+MapReducer.swift */; }; 564E73EF203DA2A2000C443C /* JoinSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E73EB203DA29B000C443C /* JoinSupportTests.swift */; }; 564E73F0203DA2A3000C443C /* JoinSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E73EB203DA29B000C443C /* JoinSupportTests.swift */; }; 564E73F1203DA2A3000C443C /* JoinSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E73EB203DA29B000C443C /* JoinSupportTests.swift */; }; @@ -1087,6 +1089,7 @@ 564CE4DC21B2DEF700652B19 /* ValueObservation+CompactMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ValueObservation+CompactMap.swift"; sourceTree = ""; }; 564CE4EB21B2E08200652B19 /* ValueObservationCompactMapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueObservationCompactMapTests.swift; sourceTree = ""; }; 564CE4F021B2E08A00652B19 /* ValueObservationMapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueObservationMapTests.swift; sourceTree = ""; }; + 564CE51A21B3128400652B19 /* ValueObservation+MapReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ValueObservation+MapReducer.swift"; sourceTree = ""; }; 564E73EB203DA29B000C443C /* JoinSupportTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinSupportTests.swift; sourceTree = ""; }; 564F9C1D1F069B4E00877A00 /* DatabaseAggregateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseAggregateTests.swift; sourceTree = ""; }; 564F9C2C1F075DD200877A00 /* DatabaseFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFunction.swift; sourceTree = ""; }; @@ -1486,6 +1489,7 @@ 5613ED7821A95E8400DC7A68 /* ValueObservation+DatabaseValueConvertible.swift */, 5613ED7721A95E8400DC7A68 /* ValueObservation+FetchableRecord.swift */, 5613ED7521A95E8400DC7A68 /* ValueObservation+Map.swift */, + 564CE51A21B3128400652B19 /* ValueObservation+MapReducer.swift */, 5613ED7621A95E8400DC7A68 /* ValueObservation+Row.swift */, 564CE43F21AA957000652B19 /* ValueObserver.swift */, 5613ED7921A95E8400DC7A68 /* ValueReducer.swift */, @@ -2331,6 +2335,7 @@ 560FC51F1CB003810014AA8E /* CGFloat.swift in Sources */, 5674A7091F307FCD0095F066 /* DatabaseValueConvertible+ReferenceConvertible.swift in Sources */, 5653EC17209873A400F46237 /* SQLExpression+QueryInterface.swift in Sources */, + 564CE52621B312B500652B19 /* ValueObservation+MapReducer.swift in Sources */, 5659F4A11EA8D997004A4992 /* Result.swift in Sources */, 560FC5201CB003810014AA8E /* Date.swift in Sources */, 560FC5221CB003810014AA8E /* DatabaseValueConvertible+RawRepresentable.swift in Sources */, @@ -2814,6 +2819,7 @@ 56AFC9F31CB1A8BB00F48B96 /* Database.swift in Sources */, 5674A7061F307FCD0095F066 /* DatabaseValueConvertible+ReferenceConvertible.swift in Sources */, 5653EC18209873A400F46237 /* SQLExpression+QueryInterface.swift in Sources */, + 564CE52721B312B700652B19 /* ValueObservation+MapReducer.swift in Sources */, 5659F4A41EA8D997004A4992 /* Result.swift in Sources */, 56AFC9F41CB1A8BB00F48B96 /* CGFloat.swift in Sources */, 56AFC9F51CB1A8BB00F48B96 /* Date.swift in Sources */, diff --git a/GRDBCustom.xcodeproj/project.pbxproj b/GRDBCustom.xcodeproj/project.pbxproj index 9bfc3b181d..3efd7d6390 100755 --- a/GRDBCustom.xcodeproj/project.pbxproj +++ b/GRDBCustom.xcodeproj/project.pbxproj @@ -103,6 +103,8 @@ 564CE4E121B2E04500652B19 /* ValueObservationCompactMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4DF21B2E04500652B19 /* ValueObservationCompactMapTests.swift */; }; 564CE4E321B2E05400652B19 /* ValueObservationMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4E221B2E05400652B19 /* ValueObservationMapTests.swift */; }; 564CE4E421B2E05400652B19 /* ValueObservationMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE4E221B2E05400652B19 /* ValueObservationMapTests.swift */; }; + 564CE52421B312AD00652B19 /* ValueObservation+MapReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE52321B312AC00652B19 /* ValueObservation+MapReducer.swift */; }; + 564CE52521B312AD00652B19 /* ValueObservation+MapReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564CE52321B312AC00652B19 /* ValueObservation+MapReducer.swift */; }; 564E73F3203DA2AC000C443C /* JoinSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E73E7203DA278000C443C /* JoinSupportTests.swift */; }; 564E73F4203DA2AD000C443C /* JoinSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E73E7203DA278000C443C /* JoinSupportTests.swift */; }; 564F9C211F069B4E00877A00 /* DatabaseAggregateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564F9C1D1F069B4E00877A00 /* DatabaseAggregateTests.swift */; }; @@ -724,6 +726,7 @@ 564CE4D921B2DEEB00652B19 /* ValueObservation+CompactMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ValueObservation+CompactMap.swift"; sourceTree = ""; }; 564CE4DF21B2E04500652B19 /* ValueObservationCompactMapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueObservationCompactMapTests.swift; sourceTree = ""; }; 564CE4E221B2E05400652B19 /* ValueObservationMapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueObservationMapTests.swift; sourceTree = ""; }; + 564CE52321B312AC00652B19 /* ValueObservation+MapReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ValueObservation+MapReducer.swift"; sourceTree = ""; }; 564E73E7203DA278000C443C /* JoinSupportTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinSupportTests.swift; sourceTree = ""; }; 564F9C1D1F069B4E00877A00 /* DatabaseAggregateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseAggregateTests.swift; sourceTree = ""; }; 564F9C2C1F075DD200877A00 /* DatabaseFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFunction.swift; sourceTree = ""; }; @@ -1089,6 +1092,7 @@ 5613ED5E21A95E6100DC7A68 /* ValueObservation+DatabaseValueConvertible.swift */, 5613ED5D21A95E6100DC7A68 /* ValueObservation+FetchableRecord.swift */, 5613ED5B21A95E6100DC7A68 /* ValueObservation+Map.swift */, + 564CE52321B312AC00652B19 /* ValueObservation+MapReducer.swift */, 5613ED5C21A95E6100DC7A68 /* ValueObservation+Row.swift */, 564CE43B21AA955B00652B19 /* ValueObserver.swift */, 5613ED5F21A95E6100DC7A68 /* ValueReducer.swift */, @@ -1970,6 +1974,7 @@ F3BA80181CFB2876003DC1BA /* SerializedDatabase.swift in Sources */, 5674A7051F307FCD0095F066 /* DatabaseValueConvertible+ReferenceConvertible.swift in Sources */, 5659F4A51EA8D997004A4992 /* Result.swift in Sources */, + 564CE52521B312AD00652B19 /* ValueObservation+MapReducer.swift in Sources */, F3BA800D1CFB2871003DC1BA /* DatabasePool.swift in Sources */, F3BA800B1CFB286D003DC1BA /* Database.swift in Sources */, F3BA80341CFB28A4003DC1BA /* Record.swift in Sources */, @@ -2274,6 +2279,7 @@ F3BA80741CFB2E55003DC1BA /* SerializedDatabase.swift in Sources */, 5674A7041F307FCD0095F066 /* DatabaseValueConvertible+ReferenceConvertible.swift in Sources */, 5659F4A21EA8D997004A4992 /* Result.swift in Sources */, + 564CE52421B312AD00652B19 /* ValueObservation+MapReducer.swift in Sources */, F3BA80691CFB2E55003DC1BA /* DatabasePool.swift in Sources */, F3BA80671CFB2E55003DC1BA /* Database.swift in Sources */, F3BA80901CFB2E7A003DC1BA /* Record.swift in Sources */, diff --git a/Tests/GRDBTests/ValueObservationDatabaseValueConvertibleTests.swift b/Tests/GRDBTests/ValueObservationDatabaseValueConvertibleTests.swift index 50edc1864a..bc996d8582 100644 --- a/Tests/GRDBTests/ValueObservationDatabaseValueConvertibleTests.swift +++ b/Tests/GRDBTests/ValueObservationDatabaseValueConvertibleTests.swift @@ -174,8 +174,8 @@ class ValueObservationDatabaseValueConvertibleTests: GRDBTestCase { results.append(names) notificationExpectation.fulfill() } - let valueObserver = transactionObserver as! ValueObserver> - XCTAssertEqual(valueObserver.region.description, "t(id,name)") + let valueObserver = transactionObserver as! ValueObserver>> + XCTAssertEqual(valueObserver.region.description, "t(id,name)") // view is not tracked // Test view observation try dbQueue.inDatabase { db in