杨锴
2024-08-14 909e20941e45f8712c012db602034b47da0bfdb0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//
//  ControlProperty.swift
//  RxCocoa
//
//  Created by Krunoslav Zaher on 8/28/15.
//  Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
 
import RxSwift
 
/// Protocol that enables extension of `ControlProperty`.
public protocol ControlPropertyType : ObservableType, ObserverType {
 
    /// - returns: `ControlProperty` interface
    func asControlProperty() -> ControlProperty<Element>
}
 
/**
    Trait for `Observable`/`ObservableType` that represents property of UI element.
 
    Sequence of values only represents initial control value and user initiated value changes.
    Programmatic value changes won't be reported.
 
    It's properties are:
 
    - `shareReplay(1)` behavior
        - it's stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced
    - it will `Complete` sequence on control being deallocated
    - it never errors out
    - it delivers events on `MainScheduler.instance`
 
    **The implementation of `ControlProperty` will ensure that sequence of values is being subscribed on main scheduler
    (`subscribe(on: ConcurrentMainScheduler.instance)` behavior).**
 
    **It is implementor's responsibility to make sure that that all other properties enumerated above are satisfied.**
 
    **If they aren't, then using this trait communicates wrong properties and could potentially break someone's code.**
 
    **In case `values` observable sequence that is being passed into initializer doesn't satisfy all enumerated
    properties, please don't use this trait.**
*/
public struct ControlProperty<PropertyType> : ControlPropertyType {
    public typealias Element = PropertyType
 
    let values: Observable<PropertyType>
    let valueSink: AnyObserver<PropertyType>
 
    /// Initializes control property with a observable sequence that represents property values and observer that enables
    /// binding values to property.
    ///
    /// - parameter values: Observable sequence that represents property values.
    /// - parameter valueSink: Observer that enables binding values to control property.
    /// - returns: Control property created with a observable sequence of values and an observer that enables binding values
    /// to property.
    public init<Values: ObservableType, Sink: ObserverType>(values: Values, valueSink: Sink) where Element == Values.Element, Element == Sink.Element {
        self.values = values.subscribe(on: ConcurrentMainScheduler.instance)
        self.valueSink = valueSink.asObserver()
    }
 
    /// Subscribes an observer to control property values.
    ///
    /// - parameter observer: Observer to subscribe to property values.
    /// - returns: Disposable object that can be used to unsubscribe the observer from receiving control property values.
    public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
        self.values.subscribe(observer)
    }
 
    /// `ControlEvent` of user initiated value changes. Every time user updates control value change event
    /// will be emitted from `changed` event.
    ///
    /// Programmatic changes to control value won't be reported.
    ///
    /// It contains all control property values except for first one.
    ///
    /// The name only implies that sequence element will be generated once user changes a value and not that
    /// adjacent sequence values need to be different (e.g. because of interaction between programmatic and user updates,
    /// or for any other reason).
    public var changed: ControlEvent<PropertyType> {
        ControlEvent(events: self.values.skip(1))
    }
 
    /// - returns: `Observable` interface.
    public func asObservable() -> Observable<Element> {
        self.values
    }
 
    /// - returns: `ControlProperty` interface.
    public func asControlProperty() -> ControlProperty<Element> {
        self
    }
 
    /// Binds event to user interface.
    ///
    /// - In case next element is received, it is being set to control value.
    /// - In case error is received, DEBUG builds raise fatal error, RELEASE builds log event to standard output.
    /// - In case sequence completes, nothing happens.
    public func on(_ event: Event<Element>) {
        switch event {
        case .error(let error):
            bindingError(error)
        case .next:
            self.valueSink.on(event)
        case .completed:
            self.valueSink.on(event)
        }
    }
}
 
extension ControlPropertyType where Element == String? {
    /// Transforms control property of type `String?` into control property of type `String`.
    public var orEmpty: ControlProperty<String> {
        let original: ControlProperty<String?> = self.asControlProperty()
 
        let values: Observable<String> = original.values.map { $0 ?? "" }
        let valueSink: AnyObserver<String> = original.valueSink.mapObserver { $0 }
        return ControlProperty<String>(values: values, valueSink: valueSink)
    }
}