From 050132dea2432b5d5d936a0a45a5775ecd8bf197 Mon Sep 17 00:00:00 2001 From: Matthew Crenshaw Date: Mon, 16 Jul 2018 13:19:59 -0400 Subject: [PATCH] added new bind(to: [ObserverType]) operator to bind an observable to a collection of obsevers --- RxSwiftExt.xcodeproj/project.pbxproj | 16 ++++++++ Source/RxCocoa/bindCollection+RxCocoa.swift | 23 +++++++++++ .../RxCocoa/BindCollectionTests+RxCocoa.swift | 39 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 Source/RxCocoa/bindCollection+RxCocoa.swift create mode 100644 Tests/RxCocoa/BindCollectionTests+RxCocoa.swift diff --git a/RxSwiftExt.xcodeproj/project.pbxproj b/RxSwiftExt.xcodeproj/project.pbxproj index a1b5437a..8d346e7b 100644 --- a/RxSwiftExt.xcodeproj/project.pbxproj +++ b/RxSwiftExt.xcodeproj/project.pbxproj @@ -25,6 +25,12 @@ 188C6DA31C47B4240092101A /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 188C6DA21C47B4240092101A /* RxSwift.framework */; }; 1A8741AC20745A91004BB762 /* UIViewPropertyAnimatorTests+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8741AB20745A91004BB762 /* UIViewPropertyAnimatorTests+Rx.swift */; }; 1AA8395B207451D6001C49ED /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1AA8395A207451D5001C49ED /* RxCocoa.framework */; }; + 29E5ADB520FCFFB7007384C2 /* bindCollection+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E5ADB420FCFFB7007384C2 /* bindCollection+RxCocoa.swift */; }; + 29E5ADB720FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E5ADB620FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift */; }; + 29E5ADB820FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E5ADB620FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift */; }; + 29E5ADB920FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E5ADB620FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift */; }; + 29E5ADBA20FD03B9007384C2 /* bindCollection+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E5ADB420FCFFB7007384C2 /* bindCollection+RxCocoa.swift */; }; + 29E5ADBB20FD03B9007384C2 /* bindCollection+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E5ADB420FCFFB7007384C2 /* bindCollection+RxCocoa.swift */; }; 3D11958B1FCAD9AE0095134B /* and.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBDE5FB1FBBAE3900DF47F9 /* and.swift */; }; 3D11958C1FCAD9AF0095134B /* and.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBDE5FB1FBBAE3900DF47F9 /* and.swift */; }; 3D638DEC1DC2B2D50089A590 /* RxSwiftExt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 188C6D911C47B2B20092101A /* RxSwiftExt.framework */; }; @@ -264,6 +270,8 @@ 188C6DA21C47B4240092101A /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = SOURCE_ROOT; }; 1A8741AB20745A91004BB762 /* UIViewPropertyAnimatorTests+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewPropertyAnimatorTests+Rx.swift"; sourceTree = ""; }; 1AA8395A207451D5001C49ED /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; + 29E5ADB420FCFFB7007384C2 /* bindCollection+RxCocoa.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "bindCollection+RxCocoa.swift"; sourceTree = ""; }; + 29E5ADB620FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BindCollectionTests+RxCocoa.swift"; sourceTree = ""; }; 3D638DE71DC2B2D40089A590 /* RxSwiftExtTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RxSwiftExtTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 3D638E1E1DC2B3A40089A590 /* RxTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxTest.framework; path = Carthage/Build/iOS/RxTest.framework; sourceTree = ""; }; 3DB034F61DC376D9002C6A26 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Tests/Info.plist; sourceTree = ""; }; @@ -453,6 +461,7 @@ 5386076E1E6F1C0A000361DE /* mapTo+RxCocoa.swift */, 538607EF1E6F589E000361DE /* not+RxCocoa.swift */, 4A73956B206D501300E2BE2D /* UIViewPropertyAnimator+Rx.swift */, + 29E5ADB420FCFFB7007384C2 /* bindCollection+RxCocoa.swift */, ); path = RxCocoa; sourceTree = ""; @@ -464,6 +473,7 @@ 538607711E6F1CFB000361DE /* MapToTests+RxCocoa.swift */, 53C79D5F1E6F5AAB00CD9B6A /* NotTests+RxCocoa.swift */, 1A8741AB20745A91004BB762 /* UIViewPropertyAnimatorTests+Rx.swift */, + 29E5ADB620FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift */, ); name = RxCocoa; path = Tests/RxCocoa; @@ -967,6 +977,7 @@ 538607B11E6F334B000361DE /* mapTo.swift in Sources */, 538607AA1E6F334B000361DE /* apply.swift in Sources */, C4D2153F20118A81009804AE /* ofType.swift in Sources */, + 29E5ADB520FCFFB7007384C2 /* bindCollection+RxCocoa.swift in Sources */, 538607B41E6F334B000361DE /* ObservableType+Weak.swift in Sources */, 3DBDE5FC1FBBAE3A00DF47F9 /* and.swift in Sources */, 8CF5F8B3202D6C5F00C1BA97 /* mapAt.swift in Sources */, @@ -1021,6 +1032,7 @@ 538607DD1E6F3692000361DE /* RepeatWithBehaviorTests.swift in Sources */, 538607EC1E6F36A9000361DE /* UnwrapTests.swift in Sources */, 538607DF1E6F36A9000361DE /* ApplyTests.swift in Sources */, + 29E5ADB720FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift in Sources */, 538607731E6F1D51000361DE /* MapToTests+RxCocoa.swift in Sources */, 538607E61E6F36A9000361DE /* MapToTests.swift in Sources */, 5A5FCE411ED5AEC60052A9B5 /* PausableBufferedTests.swift in Sources */, @@ -1062,6 +1074,7 @@ BF515CE81F3F3B0000492640 /* fromAsync.swift in Sources */, BF515CEA1F3F3B0300492640 /* curry.swift in Sources */, 62512C691F0EAF850083A89F /* not+RxCocoa.swift in Sources */, + 29E5ADBB20FD03B9007384C2 /* bindCollection+RxCocoa.swift in Sources */, 62512C6C1F0EAF950083A89F /* catchErrorJustComplete.swift in Sources */, 62512C751F0EAF950083A89F /* once.swift in Sources */, 62512C711F0EAF950083A89F /* mapTo.swift in Sources */, @@ -1095,6 +1108,7 @@ 780CB21E20A0EE8300FD3F39 /* MapManyTests.swift in Sources */, 62512C911F0EB17F0083A89F /* NotTests+RxCocoa.swift in Sources */, 62512C9A1F0EB1850083A89F /* Materialized+elementsTests.swift in Sources */, + 29E5ADB820FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift in Sources */, 62512C931F0EB1850083A89F /* CascadeTests.swift in Sources */, 62512C971F0EB1850083A89F /* IgnoreTests.swift in Sources */, 62512C941F0EB1850083A89F /* CatchErrorJustCompleteTests.swift in Sources */, @@ -1139,6 +1153,7 @@ BF515CE91F3F3B0100492640 /* fromAsync.swift in Sources */, BF515CEB1F3F3B0300492640 /* curry.swift in Sources */, E39C41DA1F18B086007F2ACD /* not+RxCocoa.swift in Sources */, + 29E5ADBA20FD03B9007384C2 /* bindCollection+RxCocoa.swift in Sources */, E39C41DD1F18B08A007F2ACD /* catchErrorJustComplete.swift in Sources */, E39C41E61F18B08A007F2ACD /* once.swift in Sources */, E39C41E21F18B08A007F2ACD /* mapTo.swift in Sources */, @@ -1172,6 +1187,7 @@ 780CB21F20A0EE8300FD3F39 /* MapManyTests.swift in Sources */, E39C420C1F18B13E007F2ACD /* PausableBufferedTests.swift in Sources */, E39C42041F18B13E007F2ACD /* IgnoreErrorsTests.swift in Sources */, + 29E5ADB920FD03AE007384C2 /* BindCollectionTests+RxCocoa.swift in Sources */, E39C41FE1F18B13A007F2ACD /* MapToTests+RxCocoa.swift in Sources */, E39C420A1F18B13E007F2ACD /* OnceTests.swift in Sources */, E39C42111F18B13E007F2ACD /* WeakTests.swift in Sources */, diff --git a/Source/RxCocoa/bindCollection+RxCocoa.swift b/Source/RxCocoa/bindCollection+RxCocoa.swift new file mode 100644 index 00000000..43a4edff --- /dev/null +++ b/Source/RxCocoa/bindCollection+RxCocoa.swift @@ -0,0 +1,23 @@ +// +// bindCollection+RxCocoa.swift +// RxSwiftExt +// +// Created by Matthew Crenshaw on 7/16/18. +// Copyright © 2018 RxSwift Community. All rights reserved. +// + +import RxSwift + +extension ObservableType { + /** + Creates new shared subscriptions and sends elements to collection of observers. + + - parameter to: Collection of observers that receives events. + - returns: Disposable object that can be used to unsubscribe the observers. + */ + func bind(to observers: [O]) -> Disposable where O: ObserverType, Self.E == O.E { + let shared = self.share() + let disposables = observers.map(shared.bind(to:)) + return CompositeDisposable(disposables: disposables) + } +} diff --git a/Tests/RxCocoa/BindCollectionTests+RxCocoa.swift b/Tests/RxCocoa/BindCollectionTests+RxCocoa.swift new file mode 100644 index 00000000..1b65492e --- /dev/null +++ b/Tests/RxCocoa/BindCollectionTests+RxCocoa.swift @@ -0,0 +1,39 @@ +// +// BindCollectionTests+RxCocoa.swift +// RxSwiftExt +// +// Created by Matthew Crenshaw on 7/16/18. +// Copyright © 2018 RxSwift Community. All rights reserved. +// + +@testable import RxSwiftExt + +import XCTest +import RxSwift +import RxTest + +class BindCollectionTests: XCTestCase { + func testBindCollection() { + let values = ["one", "two", "three", "four"] + + let scheduler = TestScheduler(initialClock: 0) + let observers = [scheduler.createObserver(String.self), + scheduler.createObserver(String.self), + scheduler.createObserver(String.self), + scheduler.createObserver(String.self)] + + _ = Observable.from(values).bind(to: observers) + + scheduler.start() + + let correct = [next(0, "one"), + next(0, "two"), + next(0, "three"), + next(0, "four"), + completed(0)] + + for observer in observers { + XCTAssertEqual(observer.events, correct) + } + } +}