add swiftUI code
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
extension ValueReducers {
|
||||
/// A `ValueReducer` that perform database fetches.
|
||||
public struct Fetch<Value>: ValueReducer {
|
||||
private let __fetch: (Database) throws -> Value
|
||||
|
||||
/// Creates a reducer which passes raw fetched values through.
|
||||
init(fetch: @escaping (Database) throws -> Value) {
|
||||
self.__fetch = fetch
|
||||
}
|
||||
|
||||
public func _fetch(_ db: Database) throws -> Value {
|
||||
assert(db.isInsideTransaction, "Fetching in a non-isolated way is illegal")
|
||||
return try __fetch(db)
|
||||
}
|
||||
|
||||
public func _value(_ fetched: Value) -> Value? {
|
||||
fetched
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
extension ValueObservation {
|
||||
/// Transforms all values from the upstream observation with a
|
||||
/// provided closure.
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// ```swift
|
||||
/// // Turn an observation of Player? into an observation of UIImage?
|
||||
/// let observation = ValueObservation
|
||||
/// .tracking { db in try Player.fetchOne(db, id: 42) }
|
||||
/// .map { player in player?.image }
|
||||
/// ```
|
||||
///
|
||||
/// The `transform` closure does not run on the main thread, and does not
|
||||
/// block any database access This makes the `map` operator a tool that
|
||||
/// helps reducing database contention
|
||||
/// (see <doc:ValueObservation#ValueObservation-Performance>).
|
||||
///
|
||||
/// - parameter transform: A closure that takes one value as its parameter
|
||||
/// and returns a new value.
|
||||
public func map<T>(_ transform: @escaping (Reducer.Value) throws -> T)
|
||||
-> ValueObservation<ValueReducers.Map<Reducer, T>>
|
||||
{
|
||||
mapReducer { ValueReducers.Map($0, transform) }
|
||||
}
|
||||
}
|
||||
|
||||
extension ValueReducers {
|
||||
/// A `ValueReducer` whose values consist of those in a `Base` reduced
|
||||
/// passed through a transform function.
|
||||
///
|
||||
/// See ``ValueObservation/map(_:)``.
|
||||
public struct Map<Base: _ValueReducer, Value>: _ValueReducer {
|
||||
private var base: Base
|
||||
private let transform: (Base.Value) throws -> Value
|
||||
|
||||
init(_ base: Base, _ transform: @escaping (Base.Value) throws -> Value) {
|
||||
self.base = base
|
||||
self.transform = transform
|
||||
}
|
||||
|
||||
public mutating func _value(_ fetched: Base.Fetched) throws -> Value? {
|
||||
guard let value = try base._value(fetched) else { return nil }
|
||||
return try transform(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ValueReducers.Map: ValueReducer where Base: ValueReducer {
|
||||
public func _fetch(_ db: Database) throws -> Base.Fetched {
|
||||
try base._fetch(db)
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
extension ValueObservation {
|
||||
/// Notifies only values that don’t match the previously observed value, as
|
||||
/// evaluated by a provided closure.
|
||||
///
|
||||
/// - parameter predicate: A closure to evaluate whether two values are
|
||||
/// equivalent, for purposes of filtering. Return true from this closure
|
||||
/// to indicate that the second element is a duplicate of the first.
|
||||
public func removeDuplicates(by predicate: @escaping (Reducer.Value, Reducer.Value) -> Bool)
|
||||
-> ValueObservation<ValueReducers.RemoveDuplicates<Reducer>>
|
||||
{
|
||||
mapReducer { ValueReducers.RemoveDuplicates($0, predicate: predicate) }
|
||||
}
|
||||
}
|
||||
|
||||
extension ValueObservation where Reducer.Value: Equatable {
|
||||
/// Notifies only values that don’t match the previously observed value.
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// ```swift
|
||||
/// // An observation of distinct Player?
|
||||
/// let observation = ValueObservation
|
||||
/// .tracking { db in try Player.fetchOne(db, id: 42) }
|
||||
/// .removeDuplicates()
|
||||
/// ```
|
||||
///
|
||||
/// > Tip: When the observed value does not adopt `Equatable`, and it is
|
||||
/// > impractical to provide a custom comparison function, you can observe
|
||||
/// > distinct raw database values such as ``Row`` or ``DatabaseValue``,
|
||||
/// > before converting them to the desired type. For example, the previous
|
||||
/// > observation can be rewritten as below:
|
||||
/// >
|
||||
/// > ```swift
|
||||
/// > // An observation of distinct `Player?`
|
||||
/// > let request = Player.filter(id: 42)
|
||||
/// > let observation = ValueObservation
|
||||
/// > .tracking { db in try Row.fetchOne(db, request) }
|
||||
/// > .removeDuplicates()
|
||||
/// > .map { row in try row.map(Player.init(row:)) }
|
||||
/// > ```
|
||||
/// >
|
||||
/// > This technique is also available for requests that
|
||||
/// > involve associations:
|
||||
/// >
|
||||
/// > ```swift
|
||||
/// > struct TeamInfo: Decodable, FetchableRecord {
|
||||
/// > var team: Team
|
||||
/// > var players: [Player]
|
||||
/// > }
|
||||
/// >
|
||||
/// > // An observation of distinct `[TeamInfo]`
|
||||
/// > let request = Team.including(all: Team.players)
|
||||
/// > let observation = ValueObservation
|
||||
/// > .tracking { db in try Row.fetchAll(db, request) }
|
||||
/// > .removeDuplicates() // Row adopts Equatable
|
||||
/// > .map { rows in try rows.map(TeamInfo.init(row:)) }
|
||||
/// > ```
|
||||
public func removeDuplicates()
|
||||
-> ValueObservation<ValueReducers.RemoveDuplicates<Reducer>>
|
||||
{
|
||||
mapReducer { ValueReducers.RemoveDuplicates($0, predicate: ==) }
|
||||
}
|
||||
}
|
||||
|
||||
extension ValueReducers {
|
||||
/// A `ValueReducer` that notifies only values that don’t match the
|
||||
/// previously observed value.
|
||||
///
|
||||
/// See ``ValueObservation/removeDuplicates()``.
|
||||
public struct RemoveDuplicates<Base: _ValueReducer>: _ValueReducer {
|
||||
private var base: Base
|
||||
private var previousValue: Base.Value?
|
||||
private var predicate: (Base.Value, Base.Value) -> Bool
|
||||
|
||||
init(_ base: Base, predicate: @escaping (Base.Value, Base.Value) -> Bool) {
|
||||
self.base = base
|
||||
self.predicate = predicate
|
||||
}
|
||||
|
||||
public mutating func _value(_ fetched: Base.Fetched) throws -> Base.Value? {
|
||||
guard let value = try base._value(fetched) else {
|
||||
return nil
|
||||
}
|
||||
if let previousValue, predicate(previousValue, value) {
|
||||
// Don't notify consecutive identical values
|
||||
return nil
|
||||
}
|
||||
self.previousValue = value
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ValueReducers.RemoveDuplicates: ValueReducer where Base: ValueReducer {
|
||||
public func _fetch(_ db: Database) throws -> Base.Fetched {
|
||||
try base._fetch(db)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
extension ValueReducers {
|
||||
// swiftlint:disable line_length
|
||||
/// A `ValueReducer` that handles ``ValueObservation`` events.
|
||||
///
|
||||
/// See ``ValueObservation/handleEvents(willStart:willFetch:willTrackRegion:databaseDidChange:didReceiveValue:didFail:didCancel:)``
|
||||
/// and ``ValueObservation/print(_:to:)``.
|
||||
public struct Trace<Base: _ValueReducer>: _ValueReducer {
|
||||
var base: Base
|
||||
let willFetch: () -> Void
|
||||
let didReceiveValue: (Base.Value) -> Void
|
||||
|
||||
public mutating func _value(_ fetched: Base.Fetched) throws -> Base.Value? {
|
||||
guard let value = try base._value(fetched) else {
|
||||
return nil
|
||||
}
|
||||
didReceiveValue(value)
|
||||
return value
|
||||
}
|
||||
}
|
||||
// swiftlint:enable line_length
|
||||
}
|
||||
|
||||
extension ValueReducers.Trace: ValueReducer where Base: ValueReducer {
|
||||
public func _fetch(_ db: Database) throws -> Base.Fetched {
|
||||
willFetch()
|
||||
return try base._fetch(db)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/// Implementation details of `ValueReducer`.
|
||||
public protocol _ValueReducer {
|
||||
/// The type of fetched database values
|
||||
associatedtype Fetched
|
||||
|
||||
/// The type of observed values
|
||||
associatedtype Value
|
||||
|
||||
/// Transforms a fetched value into an eventual observed value. Returns nil
|
||||
/// when observer should not be notified.
|
||||
///
|
||||
/// This method runs in some unspecified dispatch queue.
|
||||
///
|
||||
/// ValueReducer semantics require that the first invocation of this
|
||||
/// method returns a non-nil value:
|
||||
///
|
||||
/// let reducer = MyReducer()
|
||||
/// reducer._value(...) // MUST NOT be nil
|
||||
/// reducer._value(...) // MAY be nil
|
||||
/// reducer._value(...) // MAY be nil
|
||||
mutating func _value(_ fetched: Fetched) throws -> Value?
|
||||
}
|
||||
|
||||
/// `ValueReducer` supports ``ValueObservation``.
|
||||
///
|
||||
/// A `ValueReducer` fetches and transforms the database values
|
||||
/// observed by a ``ValueObservation``.
|
||||
///
|
||||
/// ## Topics
|
||||
///
|
||||
/// ### Support
|
||||
///
|
||||
/// - ``ValueReducers``
|
||||
public protocol ValueReducer: _ValueReducer {
|
||||
/// Fetches database values upon changes in an observed database region.
|
||||
///
|
||||
/// This method must does not depend on the state of the reducer.
|
||||
func _fetch(_ db: Database) throws -> Fetched
|
||||
}
|
||||
|
||||
/// A namespace for concrete types that adopt the ``ValueReducer`` protocol.
|
||||
public enum ValueReducers { }
|
||||
Reference in New Issue
Block a user