diff --git a/Sources/Bindable.swift b/Sources/Bindable.swift index 499e855..53cf665 100644 --- a/Sources/Bindable.swift +++ b/Sources/Bindable.swift @@ -36,12 +36,14 @@ extension SignalProtocol { /// Establish a one-way binding between the source and the bindable /// and return a disposable that can cancel binding. + @discardableResult public func bind(to bindable: B) -> Disposable where B.Element == Element, B.Error == Error { return bindable.bind(signal: filterRecursiveEvents()) } /// Establish a one-way binding between the source and the bindable /// and return a disposable that can cancel binding. + @discardableResult public func bind(to bindable: B) -> Disposable where B.Element: OptionalProtocol, B.Element.Wrapped == Element, B.Error == Error { return self.map { B.Element($0) }.bind(to: bindable) } @@ -51,6 +53,7 @@ extension BindableProtocol where Self: SignalProtocol { /// Establish a two-way binding between the source and the bindable /// and return a disposable that can cancel binding. + @discardableResult public func bidirectionalBind(to bindable: B) -> Disposable where B: SignalProtocol, B.Element == Element, B.Error == Error { let d1 = self.bind(to: bindable) let d2 = bindable.bind(to: self) diff --git a/Sources/SignalProtocol.swift b/Sources/SignalProtocol.swift index d3ac0fd..ecc22b2 100644 --- a/Sources/SignalProtocol.swift +++ b/Sources/SignalProtocol.swift @@ -1119,8 +1119,7 @@ public extension SignalProtocol where Element: SignalProtocol, Element.Error == extension SignalProtocol { - /// Propagate events only from a signal that starts emitting first. - public func amb(with other: O) -> Signal where O.Element == Element, O.Error == Error { + fileprivate func _amb(with other: O) -> Signal where O.Element == Element, O.Error == Error { return Signal { observer in let lock = NSRecursiveLock(name: "amb") let disposable = (my: SerialDisposable(otherDisposable: nil), other: SerialDisposable(otherDisposable: nil)) @@ -1152,9 +1151,12 @@ extension SignalProtocol { } } - /// Emit a combination of latest elements from each signal. Starts when both signals emit at least one element, - /// and emits `.next` when either signal generates an element by calling `combine` on the two latest elements. - public func combineLatest(with other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { + /// Propagate events only from a signal that starts emitting first. + public func amb(with other: O) -> Signal where O.Element == Element, O.Error == Error { + return _amb(with: other) + } + + fileprivate func _combineLatest(with other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { return Signal { observer in let lock = NSRecursiveLock(name: "combineLatestWith") @@ -1209,10 +1211,16 @@ extension SignalProtocol { } } + /// Emit a combination of latest elements from each signal. Starts when both signals emit at least one element, + /// and emits `.next` when either signal generates an element by calling `combine` on the two latest elements. + public func combineLatest(with other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { + return _combineLatest(with: other, combine: combine) + } + /// Emit a pair of latest elements from each signal. Starts when both signals emit at least one element, /// and emits `.next` when either signal generates an element. public func combineLatest(with other: O) -> Signal<(Element, O.Element), Error> where O.Error == Error { - return combineLatest(with: other, combine: { ($0, $1) }) + return _combineLatest(with: other, combine: { ($0, $1) }) } /// Merge emissions from both the receiver and the other signal into one signal. @@ -1220,9 +1228,7 @@ extension SignalProtocol { return Signal.sequence([self.toSignal(), other.toSignal()]).merge() } - /// Emit elements from the receiver and the other signal in pairs. - /// This differs from `combineLatest` in that the combinations are produced from elements at same positions. - public func zip(with other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { + fileprivate func _zip(with other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { return Signal { observer in let lock = NSRecursiveLock(name: "zip") @@ -1279,15 +1285,19 @@ extension SignalProtocol { } } + /// Emit elements from the receiver and the other signal in pairs. + /// This differs from `combineLatest` in that the combinations are produced from elements at same positions. + public func zip(with other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { + return _zip(with: other, combine: combine) + } + /// Emit elements from the receiver and the other signal in pairs. /// This differs from `combineLatest` in that the pairs are produced from elements at same positions. public func zip(with other: O) -> Signal<(Element, O.Element), Error> where O.Error == Error { - return zip(with: other, combine: { ($0, $1) }) + return _zip(with: other, combine: { ($0, $1) }) } - /// Combines the receiver and the other signal into a signal of combinations of elements whenever the - /// receiver emits an element with the latest element from the other signal. - public func with(latestFrom other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { + fileprivate func _with(latestFrom other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { return Signal { observer in var latest: O.Element? = nil @@ -1321,10 +1331,16 @@ extension SignalProtocol { } } + /// Combines the receiver and the other signal into a signal of combinations of elements whenever the + /// receiver emits an element with the latest element from the other signal. + public func with(latestFrom other: O, combine: @escaping (Element, O.Element) -> U) -> Signal where O.Error == Error { + return _with(latestFrom: other, combine: combine) + } + /// Combines the receiver and the other signal into a signal of pairs of elements whenever the /// receiver emits an element with the latest element from the other signal. public func with(latestFrom other: O) -> Signal<(Element, O.Element), Error> where O.Error == Error { - return with(latestFrom: other, combine: { ($0, $1) }) + return _with(latestFrom: other, combine: { ($0, $1) }) } } @@ -1339,8 +1355,8 @@ extension SignalProtocol where Error == NoError { observer.next(element) case .completed: observer.completed() - case .failed: // will never happen because of NoError constraint - break + case .failed: + break // will never happen because of NoError constraint } } } @@ -1348,16 +1364,83 @@ extension SignalProtocol where Error == NoError { /// Map each event into a signal and then flatten inner signals. public func flatMapLatest(transform: @escaping (Element) -> O) -> Signal { - return castError().flatMapLatest(transform: transform) + return castError().map(transform).switchToLatest() } /// Map each event into a signal and then flatten inner signals. public func flatMapMerge(transform: @escaping (Element) -> O) -> Signal { - return castError().flatMapMerge(transform: transform) + return castError().map(transform).merge() } /// Map each event into a signal and then flatten inner signals. public func flatMapConcat(transform: @escaping (Element) -> O) -> Signal { - return castError().flatMapConcat(transform: transform) + return castError().map(transform).concat() + } + + /// Transform each element by applying `transform` on it. + public func tryMap(transform: @escaping (Element) -> Result) -> Signal { + return Signal { observer in + return self.observe { event in + switch event { + case .next(let element): + switch transform(element) { + case .success(let value): + observer.next(value) + case .failure(let error): + observer.failed(error) + } + case .failed: + break // will never happen because of NoError constraint + case .completed: + observer.completed() + } + } + } + } + + /// Propagate events only from a signal that starts emitting first. + public func amb(with other: O) -> Signal where O.Element == Element { + return castError()._amb(with: other) + } + + /// Emit a combination of latest elements from each signal. Starts when both signals emit at least one element, + /// and emits `.next` when either signal generates an element by calling `combine` on the two latest elements. + public func combineLatest(with other: O, combine: @escaping (Element, O.Element) -> U) -> Signal { + return castError()._combineLatest(with: other, combine: combine) + } + + /// Emit a pair of latest elements from each signal. Starts when both signals emit at least one element, + /// and emits `.next` when either signal generates an element. + public func combineLatest(with other: O) -> Signal<(Element, O.Element), O.Error> { + return castError()._combineLatest(with: other, combine: { ($0, $1) }) + } + + /// Merge emissions from both the receiver and the other signal into one signal. + public func merge(with other: O) -> Signal where O.Element == Element { + return Signal.sequence([toSignal().castError(), other.toSignal()]).merge() + } + + /// Emit elements from the receiver and the other signal in pairs. + /// This differs from `combineLatest` in that the combinations are produced from elements at same positions. + public func zip(with other: O, combine: @escaping (Element, O.Element) -> U) -> Signal { + return castError()._zip(with: other, combine: combine) + } + + /// Emit elements from the receiver and the other signal in pairs. + /// This differs from `combineLatest` in that the pairs are produced from elements at same positions. + public func zip(with other: O) -> Signal<(Element, O.Element), O.Error> { + return castError()._zip(with: other, combine: { ($0, $1) }) + } + + /// Combines the receiver and the other signal into a signal of combinations of elements whenever the + /// receiver emits an element with the latest element from the other signal. + public func with(latestFrom other: O, combine: @escaping (Element, O.Element) -> U) -> Signal { + return castError()._with(latestFrom: other, combine: combine) + } + + /// Combines the receiver and the other signal into a signal of pairs of elements whenever the + /// receiver emits an element with the latest element from the other signal. + public func with(latestFrom other: O) -> Signal<(Element, O.Element), O.Error> { + return castError()._with(latestFrom: other, combine: { ($0, $1) }) } }