diff --git a/RxCocoa/Common/Observable+Bind.swift b/RxCocoa/Common/Observable+Bind.swift index 2dea03038..c2fae58de 100644 --- a/RxCocoa/Common/Observable+Bind.swift +++ b/RxCocoa/Common/Observable+Bind.swift @@ -6,40 +6,47 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // - import RxSwift extension ObservableType { - /** - Creates new subscription and sends elements to observer. - - In this form it's equivalent to `subscribe` method, but it communicates intent better, and enables - writing more consistent binding code. - - - parameter to: Observer that receives events. - - returns: Disposable object that can be used to unsubscribe the observer. - */ - public func bind(to observer: O) -> Disposable where O.E == E { - return self.subscribe(observer) + Creates new subscription and sends elements to observer(s). + In this form, it's equivalent to the `subscribe` method, but it better conveys intent, and enables + writing more consistent binding code. + - parameter to: Observers to receives events. + - returns: Disposable object that can be used to unsubscribe the observers. + */ + public func bind(to observers: O...) -> Disposable where O.E == E { + return self.bind(to: observers) } /** - Creates new subscription and sends elements to observer. - - In this form it's equivalent to `subscribe` method, but it communicates intent better, and enables + Creates new subscription and sends elements to observer(s). + In this form, it's equivalent to the `subscribe` method, but it better conveys intent, and enables writing more consistent binding code. + - parameter to: Observers to receives events. + - returns: Disposable object that can be used to unsubscribe the observers. + */ + public func bind(to observers: O...) -> Disposable where O.E == E? { + return self.map { $0 as E? }.bind(to: observers) + } - - parameter to: Observer that receives events. - - returns: Disposable object that can be used to unsubscribe the observer. + /** + Creates new subscription and sends elements to observer(s). + In this form, it's equivalent to the `subscribe` method, but it better conveys intent, and enables + writing more consistent binding code. + - parameter to: Observers to receives events. + - returns: Disposable object that can be used to unsubscribe the observers. */ - public func bind(to observer: O) -> Disposable where O.E == E? { - return self.map { $0 }.subscribe(observer) + private func bind(to observers: [O]) -> Disposable where O.E == E { + return self.subscribe { event in + observers.forEach { $0.on(event) } + } } - + /** Subscribes to observable sequence using custom binder function. - + - parameter to: Function used to bind elements from `self`. - returns: Object representing subscription. */ @@ -50,11 +57,11 @@ extension ObservableType { /** Subscribes to observable sequence using custom binder function and final parameter passed to binder function after `self` is passed. - + public func bind(to binder: Self -> R1 -> R2, curriedArgument: R1) -> R2 { return binder(self)(curriedArgument) } - + - parameter to: Function used to bind elements from `self`. - parameter curriedArgument: Final argument passed to `binder` to finish binding process. - returns: Object representing subscription. @@ -62,14 +69,13 @@ extension ObservableType { public func bind(to binder: (Self) -> (R1) -> R2, curriedArgument: R1) -> R2 { return binder(self)(curriedArgument) } - - - /** - Subscribes an element handler to an observable sequence. + + /** + Subscribes an element handler to an observable sequence. In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - + - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ diff --git a/RxRelay/Observable+Bind.swift b/RxRelay/Observable+Bind.swift index 0f9a80f0b..cc5667404 100644 --- a/RxRelay/Observable+Bind.swift +++ b/RxRelay/Observable+Bind.swift @@ -10,19 +10,43 @@ import RxSwift extension ObservableType { /** - Creates new subscription and sends elements to publish relay. + Creates new subscription and sends elements to publish relay(s). + In case error occurs in debug mode, `fatalError` will be raised. + In case error occurs in release mode, `error` will be logged. + - parameter to: Target publish relays for sequence elements. + - returns: Disposable object that can be used to unsubscribe the observer. + */ + public func bind(to relays: PublishRelay...) -> Disposable { + return bind(to: relays) + } + + /** + Creates new subscription and sends elements to publish relay(s). In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - - parameter to: Target publish relay for sequence elements. + - parameter to: Target publish relays for sequence elements. + - returns: Disposable object that can be used to unsubscribe the observer. + */ + public func bind(to relays: PublishRelay...) -> Disposable { + return self.map { $0 as E? }.bind(to: relays) + } + + /** + Creates new subscription and sends elements to publish relay(s). + In case error occurs in debug mode, `fatalError` will be raised. + In case error occurs in release mode, `error` will be logged. + - parameter to: Target publish relays for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer. */ - public func bind(to relay: PublishRelay) -> Disposable { - return self.subscribe { e in + private func bind(to relays: [PublishRelay]) -> Disposable { + return subscribe { e in switch e { case let .next(element): - relay.accept(element) + relays.forEach { + $0.accept(element) + } case let .error(error): rxFatalErrorInDebug("Binding error to publish relay: \(error)") case .completed: @@ -32,20 +56,18 @@ extension ObservableType { } /** - Creates new subscription and sends elements to publish relay. - + Creates new subscription and sends elements to behavior relay(s). In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - - - parameter to: Target publish relay for sequence elements. + - parameter to: Target behavior relay for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer. */ - public func bind(to relay: PublishRelay) -> Disposable { - return self.map { $0 as E? }.bind(to: relay) + public func bind(to relays: BehaviorRelay...) -> Disposable { + return self.bind(to: relays) } /** - Creates new subscription and sends elements to behavior relay. + Creates new subscription and sends elements to behavior relay(s). In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. @@ -53,29 +75,29 @@ extension ObservableType { - parameter to: Target behavior relay for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer. */ - public func bind(to relay: BehaviorRelay) -> Disposable { - return self.subscribe { e in - switch e { - case let .next(element): - relay.accept(element) - case let .error(error): - rxFatalErrorInDebug("Binding error to behavior relay: \(error)") - case .completed: - break - } - } + public func bind(to relays: BehaviorRelay...) -> Disposable { + return self.map { $0 as E? }.bind(to: relays) } /** - Creates new subscription and sends elements to behavior relay. - + Creates new subscription and sends elements to behavior relay(s). In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - - parameter to: Target behavior relay for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer. */ - public func bind(to relay: BehaviorRelay) -> Disposable { - return self.map { $0 as E? }.bind(to: relay) + private func bind(to relays: [BehaviorRelay]) -> Disposable { + return subscribe { e in + switch e { + case let .next(element): + relays.forEach { + $0.accept(element) + } + case let .error(error): + rxFatalErrorInDebug("Binding error to behavior relay: \(error)") + case .completed: + break + } + } } } diff --git a/Sources/AllTestz/main.swift b/Sources/AllTestz/main.swift index bbfd55b68..edd1a615c 100644 --- a/Sources/AllTestz/main.swift +++ b/Sources/AllTestz/main.swift @@ -1139,10 +1139,14 @@ final class ObservableRelayBindTest_ : ObservableRelayBindTest, RxTestCase { static var allTests: [(String, (ObservableRelayBindTest_) -> () -> Void)] { return [ ("testBindToPublishRelay", ObservableRelayBindTest.testBindToPublishRelay), + ("testBindToPublishRelays", ObservableRelayBindTest.testBindToPublishRelays), ("testBindToOptionalPublishRelay", ObservableRelayBindTest.testBindToOptionalPublishRelay), + ("testBindToOptionalPublishRelays", ObservableRelayBindTest.testBindToOptionalPublishRelays), ("testBindToPublishRelayNoAmbiguity", ObservableRelayBindTest.testBindToPublishRelayNoAmbiguity), ("testBindToBehaviorRelay", ObservableRelayBindTest.testBindToBehaviorRelay), + ("testBindToBehaviorRelays", ObservableRelayBindTest.testBindToBehaviorRelays), ("testBindToOptionalBehaviorRelay", ObservableRelayBindTest.testBindToOptionalBehaviorRelay), + ("testBindToOptionalBehaviorRelays", ObservableRelayBindTest.testBindToOptionalBehaviorRelays), ("testBindToBehaviorRelayNoAmbiguity", ObservableRelayBindTest.testBindToBehaviorRelayNoAmbiguity), ] } } diff --git a/Tests/RxCocoaTests/Observable+BindTests.swift b/Tests/RxCocoaTests/Observable+BindTests.swift index e957a3cef..80fd49e2c 100644 --- a/Tests/RxCocoaTests/Observable+BindTests.swift +++ b/Tests/RxCocoaTests/Observable+BindTests.swift @@ -33,6 +33,31 @@ extension ObservableBindTest { ]) } + func testBindToObservers() { + var events1: [Recorded>] = [] + var events2: [Recorded>] = [] + + let observer1: AnyObserver = AnyObserver { event in + events1.append(Recorded(time: 0, value: event)) + } + + let observer2: AnyObserver = AnyObserver { event in + events2.append(Recorded(time: 0, value: event)) + } + + _ = Observable.just(1).bind(to: observer1, observer2) + + XCTAssertEqual(events1, [ + .next(1), + .completed() + ]) + + XCTAssertEqual(events2, [ + .next(1), + .completed() + ]) + } + func testBindToOptionalObserver() { var events: [Recorded>] = [] @@ -49,6 +74,31 @@ extension ObservableBindTest { } } + func testBindToOptionalObservers() { + var events1: [Recorded>] = [] + var events2: [Recorded>] = [] + + let observer1: AnyObserver = AnyObserver { event in + events1.append(Recorded(time: 0, value: event)) + } + + let observer2: AnyObserver = AnyObserver { event in + events2.append(Recorded(time: 0, value: event)) + } + + _ = (Observable.just(1) as Observable).bind(to: observer1, observer2) + + XCTAssertEqual(events1, [ + .next(1), + .completed() + ]) + + XCTAssertEqual(events2, [ + .next(1), + .completed() + ]) + } + func testBindToOptionalObserverNoAmbiguity() { var events: [Recorded>] = [] @@ -102,5 +152,4 @@ extension ObservableBindTest { XCTAssertEqual(result, 4) d.dispose() } - } diff --git a/Tests/RxRelayTests/Observable+RelayBindTests.swift b/Tests/RxRelayTests/Observable+RelayBindTests.swift index 7f952f6c7..4038d95fd 100644 --- a/Tests/RxRelayTests/Observable+RelayBindTests.swift +++ b/Tests/RxRelayTests/Observable+RelayBindTests.swift @@ -33,12 +33,38 @@ extension ObservableRelayBindTest { ]) } + func testBindToPublishRelays() { + var events1: [Recorded>] = [] + var events2: [Recorded>] = [] + + let relay1 = PublishRelay() + let relay2 = PublishRelay() + + _ = relay1.subscribe { event in + events1.append(Recorded(time: 0, value: event)) + } + + _ = relay2.subscribe { event in + events2.append(Recorded(time: 0, value: event)) + } + + _ = Observable.just(1).bind(to: relay1, relay2) + + XCTAssertEqual(events1, [ + .next(1) + ]) + + XCTAssertEqual(events2, [ + .next(1) + ]) + } + func testBindToOptionalPublishRelay() { var events: [Recorded>] = [] let relay = PublishRelay() - _ = relay.subscribe{ event in + _ = relay.subscribe { event in events.append(Recorded(time: 0, value: event)) } @@ -49,12 +75,38 @@ extension ObservableRelayBindTest { ]) } + func testBindToOptionalPublishRelays() { + var events1: [Recorded>] = [] + var events2: [Recorded>] = [] + + let relay1 = PublishRelay() + let relay2 = PublishRelay() + + _ = relay1.subscribe { event in + events1.append(Recorded(time: 0, value: event)) + } + + _ = relay2.subscribe { event in + events2.append(Recorded(time: 0, value: event)) + } + + _ = (Observable.just(1) as Observable).bind(to: relay1, relay2) + + XCTAssertEqual(events1, [ + .next(1) + ]) + + XCTAssertEqual(events2, [ + .next(1) + ]) + } + func testBindToPublishRelayNoAmbiguity() { var events: [Recorded>] = [] let relay = PublishRelay() - _ = relay.subscribe{ event in + _ = relay.subscribe { event in events.append(Recorded(time: 0, value: event)) } @@ -76,6 +128,16 @@ extension ObservableRelayBindTest { XCTAssertEqual(relay.value, 1) } + func testBindToBehaviorRelays() { + let relay1 = BehaviorRelay(value: 0) + let relay2 = BehaviorRelay(value: 0) + + _ = Observable.just(1).bind(to: relay1, relay2) + + XCTAssertEqual(relay1.value, 1) + XCTAssertEqual(relay2.value, 1) + } + func testBindToOptionalBehaviorRelay() { let relay = BehaviorRelay(value: 0) @@ -84,6 +146,16 @@ extension ObservableRelayBindTest { XCTAssertEqual(relay.value, 1) } + func testBindToOptionalBehaviorRelays() { + let relay1 = BehaviorRelay(value: 0) + let relay2 = BehaviorRelay(value: 0) + + _ = (Observable.just(1) as Observable).bind(to: relay1, relay2) + + XCTAssertEqual(relay1.value, 1) + XCTAssertEqual(relay2.value, 1) + } + func testBindToBehaviorRelayNoAmbiguity() { let relay = BehaviorRelay(value: 0)