├── .gitignore ├── .swift-version ├── .swiftlint.yml ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── LICENSE ├── Package.swift ├── README.md ├── Resources ├── Info.plist └── RxResponderChain.h ├── RxResponderChain.podspec ├── RxResponderChain.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── RxResponderChain.xcscheme ├── Sources └── ResponderChain+Rx.swift └── Tests ├── LinuxMain.swift └── RxResponderChainTests └── RxResponderChainTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | 67 | # macOS 68 | .DS_Store 69 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.1.0 2 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | included: 2 | - Sources/ 3 | 4 | line_length: 130 5 | file_length: 500 6 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" ~> 4.1 2 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ukitaka/RxResponderChain/3af17a0a376e0dcc3e32d5d6e191bc08cb57d8a0/Cartfile.private -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" "4.1.2" 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ukitaka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "RxResponderChain", 5 | dependencies: [ 6 | .Package(url: "git@github.com:ReactiveX/RxSwift.git", majorVersion: 3) 7 | ] 8 | ) 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxResponderChain 2 | `RxResponderChain` is an extension of `RxSwift`, `RxCocoa`. 3 | It provides the way to notify Rx events via responder chain. 4 | 5 | ## Usage 6 | First, you have to create struct / class / enum represents event you want to notify using `ResponderChainEvent` protocol. 7 | 8 | ```swift 9 | import RxResponder 10 | 11 | struct LikeTweetEvent: ResponderChainEvent { 12 | let tweetID: Int64 13 | } 14 | ``` 15 | 16 | Generate event object and pass it to `bindTo` (or, `on`, `onNext` … ). 17 | 18 | ```swift 19 | final class TweetCell: UITableViewCell { 20 | ... 21 | 22 | override func awakeFromNib() { 23 | super.awakeFromNib() 24 | 25 | likeButton.rx.tap 26 | .map { _ in LikeTweetEvent(tweetID: self.tweet.id) } 27 | .bind(to: self.rx.responderChain) 28 | .disposed(by: disposeBag) 29 | } 30 | } 31 | ``` 32 | 33 | Then you can receive events in classes that are on responder chain of the class sends events. 34 | For example, if you send event in `UITableViewCell` then you can receive them in `tableView`, `viewController.view`, `viewController`, `viewController.navigationController`, and so on. 35 | 36 | ```swift 37 | final class TweetListViewController: UIViewController { 38 | ... 39 | 40 | override func viewDidLoad() { 41 | super.viewDidLoad() 42 | 43 | self.rx.responderChain.event(LikeTweetEvent.self) 44 | .flatMapFirst { e in twitterService.like(tweetID: e.tweetID) } 45 | .subscribe() 46 | .disposed(by:disposeBag) 47 | } 48 | } 49 | ``` 50 | 51 | ## Requirements 52 | 53 | `RxResponderChain` requires / supports the following environments: 54 | 55 | + Swift 4.1 / Xcode 9.3 56 | + iOS 9.0 or later 57 | + RxSwift / RxCocoa ~> 4.1 58 | 59 | ## Installation 60 | 61 | ### Carthage 62 | 63 | ```ruby 64 | github "ukitaka/RxResponderChain" ~> 2.0 65 | ``` 66 | 67 | 68 | ### CocoaPods 69 | 70 | ```ruby 71 | use_frameworks! 72 | pod "RxResponderChain", "~> 2.0" 73 | ``` 74 | 75 | -------------------------------------------------------------------------------- /Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Resources/RxResponderChain.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxResponderChain.h 3 | // RxResponderChain 4 | // 5 | // Created by ST20841 on 2017/02/22. 6 | // Copyright © 2017年 waft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for RxResponderChain. 12 | FOUNDATION_EXPORT double RxResponderChainVersionNumber; 13 | 14 | //! Project version string for RxResponderChain. 15 | FOUNDATION_EXPORT const unsigned char RxResponderChainVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /RxResponderChain.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "RxResponderChain" 3 | s.version = "2.0.0" 4 | s.summary = "Notify Rx events via responder chain" 5 | s.description = <<-DESC 6 | `RxResponderChain` is an extension of `RxSwift`, `RxCocoa`. 7 | It provides the way to notify Rx events via responder chain. 8 | DESC 9 | 10 | s.homepage = "https://github.com/ukitaka/RxResponderChain" 11 | s.license = { :type => "MIT", :file => "LICENSE" } 12 | s.author = { "ukitaka" => "yuki.takahashi.1126@gmail.com" } 13 | 14 | s.platform = :ios, "9.0" 15 | s.source = { :git => "https://github.com/ukitaka/RxResponderChain.git", :tag => "#{s.version}" } 16 | s.source_files = "Sources/*.swift" 17 | 18 | s.requires_arc = true 19 | 20 | s.dependency "RxSwift", "~> 4.1" 21 | s.dependency "RxCocoa", "~> 4.1" 22 | end 23 | 24 | -------------------------------------------------------------------------------- /RxResponderChain.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 261463CC1E5D89B700699242 /* RxResponderChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 261463CA1E5D89B700699242 /* RxResponderChain.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 261463D71E5D8A1500699242 /* RxResponderChain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 261463BE1E5D884A00699242 /* RxResponderChain.framework */; }; 12 | 261463E01E5D8A5F00699242 /* RxResponderChainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261463DE1E5D8A5000699242 /* RxResponderChainTests.swift */; }; 13 | 261463E21E5D8AFF00699242 /* ResponderChain+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261463E11E5D8AFF00699242 /* ResponderChain+Rx.swift */; }; 14 | 261463E61E5D8B7200699242 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 261463E41E5D8B7200699242 /* RxCocoa.framework */; }; 15 | 261463E71E5D8B7200699242 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 261463E51E5D8B7200699242 /* RxSwift.framework */; }; 16 | E807502C1E5E597E00005DDB /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 261463E41E5D8B7200699242 /* RxCocoa.framework */; }; 17 | E807502D1E5E597E00005DDB /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 261463E51E5D8B7200699242 /* RxSwift.framework */; }; 18 | E80750301E5E597E00005DDB /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E807502E1E5E597E00005DDB /* RxBlocking.framework */; }; 19 | E80750311E5E597E00005DDB /* RxTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E807502F1E5E597E00005DDB /* RxTest.framework */; }; 20 | E8F330A31E5E607A00898A5F /* RxBlocking.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E807502E1E5E597E00005DDB /* RxBlocking.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 21 | E8F330A41E5E607A00898A5F /* RxTest.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E807502F1E5E597E00005DDB /* RxTest.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 22 | E8F330A51E5E607A00898A5F /* RxCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 261463E41E5D8B7200699242 /* RxCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 23 | E8F330A61E5E607A00898A5F /* RxSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 261463E51E5D8B7200699242 /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 261463D81E5D8A1500699242 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 261463B51E5D884A00699242 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 261463BD1E5D884A00699242; 32 | remoteInfo = RxResponderChain; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXCopyFilesBuildPhase section */ 37 | E8F330A21E5E606D00898A5F /* CopyFiles */ = { 38 | isa = PBXCopyFilesBuildPhase; 39 | buildActionMask = 2147483647; 40 | dstPath = ""; 41 | dstSubfolderSpec = 10; 42 | files = ( 43 | E8F330A31E5E607A00898A5F /* RxBlocking.framework in CopyFiles */, 44 | E8F330A41E5E607A00898A5F /* RxTest.framework in CopyFiles */, 45 | E8F330A51E5E607A00898A5F /* RxCocoa.framework in CopyFiles */, 46 | E8F330A61E5E607A00898A5F /* RxSwift.framework in CopyFiles */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXCopyFilesBuildPhase section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 261463BE1E5D884A00699242 /* RxResponderChain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxResponderChain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 261463C91E5D89B700699242 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Resources/Info.plist; sourceTree = SOURCE_ROOT; }; 55 | 261463CA1E5D89B700699242 /* RxResponderChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RxResponderChain.h; path = Resources/RxResponderChain.h; sourceTree = SOURCE_ROOT; }; 56 | 261463D21E5D8A1500699242 /* RxResponderChainTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxResponderChainTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 261463DE1E5D8A5000699242 /* RxResponderChainTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxResponderChainTests.swift; sourceTree = ""; }; 58 | 261463E11E5D8AFF00699242 /* ResponderChain+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ResponderChain+Rx.swift"; sourceTree = ""; }; 59 | 261463E41E5D8B7200699242 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; 60 | 261463E51E5D8B7200699242 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; 61 | E807502E1E5E597E00005DDB /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxBlocking.framework; path = Carthage/Build/iOS/RxBlocking.framework; sourceTree = ""; }; 62 | E807502F1E5E597E00005DDB /* RxTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxTest.framework; path = Carthage/Build/iOS/RxTest.framework; sourceTree = ""; }; 63 | /* End PBXFileReference section */ 64 | 65 | /* Begin PBXFrameworksBuildPhase section */ 66 | 261463BA1E5D884A00699242 /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | 261463E61E5D8B7200699242 /* RxCocoa.framework in Frameworks */, 71 | 261463E71E5D8B7200699242 /* RxSwift.framework in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | 261463CF1E5D8A1500699242 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | E80750301E5E597E00005DDB /* RxBlocking.framework in Frameworks */, 80 | E80750311E5E597E00005DDB /* RxTest.framework in Frameworks */, 81 | E807502C1E5E597E00005DDB /* RxCocoa.framework in Frameworks */, 82 | E807502D1E5E597E00005DDB /* RxSwift.framework in Frameworks */, 83 | 261463D71E5D8A1500699242 /* RxResponderChain.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | 261463B41E5D884A00699242 = { 91 | isa = PBXGroup; 92 | children = ( 93 | 261463CD1E5D89D800699242 /* Sources */, 94 | 261463C01E5D884A00699242 /* Resources */, 95 | 261463D31E5D8A1500699242 /* Tests */, 96 | 261463BF1E5D884A00699242 /* Products */, 97 | 261463E31E5D8B7100699242 /* Frameworks */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | 261463BF1E5D884A00699242 /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 261463BE1E5D884A00699242 /* RxResponderChain.framework */, 105 | 261463D21E5D8A1500699242 /* RxResponderChainTests.xctest */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | 261463C01E5D884A00699242 /* Resources */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 261463C91E5D89B700699242 /* Info.plist */, 114 | 261463CA1E5D89B700699242 /* RxResponderChain.h */, 115 | ); 116 | name = Resources; 117 | path = RxResponderChain; 118 | sourceTree = ""; 119 | }; 120 | 261463CD1E5D89D800699242 /* Sources */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 261463E11E5D8AFF00699242 /* ResponderChain+Rx.swift */, 124 | ); 125 | path = Sources; 126 | sourceTree = ""; 127 | }; 128 | 261463D31E5D8A1500699242 /* Tests */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 261463DD1E5D8A3D00699242 /* RxResponderChainTests */, 132 | ); 133 | path = Tests; 134 | sourceTree = ""; 135 | }; 136 | 261463DD1E5D8A3D00699242 /* RxResponderChainTests */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 261463DE1E5D8A5000699242 /* RxResponderChainTests.swift */, 140 | ); 141 | path = RxResponderChainTests; 142 | sourceTree = ""; 143 | }; 144 | 261463E31E5D8B7100699242 /* Frameworks */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | E807502E1E5E597E00005DDB /* RxBlocking.framework */, 148 | E807502F1E5E597E00005DDB /* RxTest.framework */, 149 | 261463E41E5D8B7200699242 /* RxCocoa.framework */, 150 | 261463E51E5D8B7200699242 /* RxSwift.framework */, 151 | ); 152 | name = Frameworks; 153 | sourceTree = ""; 154 | }; 155 | /* End PBXGroup section */ 156 | 157 | /* Begin PBXHeadersBuildPhase section */ 158 | 261463BB1E5D884A00699242 /* Headers */ = { 159 | isa = PBXHeadersBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 261463CC1E5D89B700699242 /* RxResponderChain.h in Headers */, 163 | ); 164 | runOnlyForDeploymentPostprocessing = 0; 165 | }; 166 | /* End PBXHeadersBuildPhase section */ 167 | 168 | /* Begin PBXNativeTarget section */ 169 | 261463BD1E5D884A00699242 /* RxResponderChain */ = { 170 | isa = PBXNativeTarget; 171 | buildConfigurationList = 261463C61E5D884A00699242 /* Build configuration list for PBXNativeTarget "RxResponderChain" */; 172 | buildPhases = ( 173 | 261463B91E5D884A00699242 /* Sources */, 174 | 261463BA1E5D884A00699242 /* Frameworks */, 175 | 261463BB1E5D884A00699242 /* Headers */, 176 | 261463BC1E5D884A00699242 /* Resources */, 177 | 26D537001E5E794400F4FC4E /* SwiftLint */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | ); 183 | name = RxResponderChain; 184 | productName = RxResponderChain; 185 | productReference = 261463BE1E5D884A00699242 /* RxResponderChain.framework */; 186 | productType = "com.apple.product-type.framework"; 187 | }; 188 | 261463D11E5D8A1500699242 /* RxResponderChainTests */ = { 189 | isa = PBXNativeTarget; 190 | buildConfigurationList = 261463DA1E5D8A1500699242 /* Build configuration list for PBXNativeTarget "RxResponderChainTests" */; 191 | buildPhases = ( 192 | 261463CE1E5D8A1500699242 /* Sources */, 193 | 261463CF1E5D8A1500699242 /* Frameworks */, 194 | 261463D01E5D8A1500699242 /* Resources */, 195 | E8F330A21E5E606D00898A5F /* CopyFiles */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | 261463D91E5D8A1500699242 /* PBXTargetDependency */, 201 | ); 202 | name = RxResponderChainTests; 203 | productName = RxResponderChainTests; 204 | productReference = 261463D21E5D8A1500699242 /* RxResponderChainTests.xctest */; 205 | productType = "com.apple.product-type.bundle.unit-test"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | 261463B51E5D884A00699242 /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | LastSwiftUpdateCheck = 0820; 214 | LastUpgradeCheck = 0930; 215 | ORGANIZATIONNAME = waft; 216 | TargetAttributes = { 217 | 261463BD1E5D884A00699242 = { 218 | CreatedOnToolsVersion = 8.2.1; 219 | DevelopmentTeam = G73SPYZ3Q7; 220 | LastSwiftMigration = 0930; 221 | ProvisioningStyle = Automatic; 222 | }; 223 | 261463D11E5D8A1500699242 = { 224 | CreatedOnToolsVersion = 8.2.1; 225 | DevelopmentTeam = G73SPYZ3Q7; 226 | LastSwiftMigration = 0930; 227 | ProvisioningStyle = Automatic; 228 | }; 229 | }; 230 | }; 231 | buildConfigurationList = 261463B81E5D884A00699242 /* Build configuration list for PBXProject "RxResponderChain" */; 232 | compatibilityVersion = "Xcode 3.2"; 233 | developmentRegion = English; 234 | hasScannedForEncodings = 0; 235 | knownRegions = ( 236 | en, 237 | ); 238 | mainGroup = 261463B41E5D884A00699242; 239 | productRefGroup = 261463BF1E5D884A00699242 /* Products */; 240 | projectDirPath = ""; 241 | projectRoot = ""; 242 | targets = ( 243 | 261463BD1E5D884A00699242 /* RxResponderChain */, 244 | 261463D11E5D8A1500699242 /* RxResponderChainTests */, 245 | ); 246 | }; 247 | /* End PBXProject section */ 248 | 249 | /* Begin PBXResourcesBuildPhase section */ 250 | 261463BC1E5D884A00699242 /* Resources */ = { 251 | isa = PBXResourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | 261463D01E5D8A1500699242 /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXResourcesBuildPhase section */ 265 | 266 | /* Begin PBXShellScriptBuildPhase section */ 267 | 26D537001E5E794400F4FC4E /* SwiftLint */ = { 268 | isa = PBXShellScriptBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | ); 272 | inputPaths = ( 273 | ); 274 | name = SwiftLint; 275 | outputPaths = ( 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | shellPath = /bin/sh; 279 | shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; 280 | }; 281 | /* End PBXShellScriptBuildPhase section */ 282 | 283 | /* Begin PBXSourcesBuildPhase section */ 284 | 261463B91E5D884A00699242 /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 261463E21E5D8AFF00699242 /* ResponderChain+Rx.swift in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | 261463CE1E5D8A1500699242 /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | 261463E01E5D8A5F00699242 /* RxResponderChainTests.swift in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXSourcesBuildPhase section */ 301 | 302 | /* Begin PBXTargetDependency section */ 303 | 261463D91E5D8A1500699242 /* PBXTargetDependency */ = { 304 | isa = PBXTargetDependency; 305 | target = 261463BD1E5D884A00699242 /* RxResponderChain */; 306 | targetProxy = 261463D81E5D8A1500699242 /* PBXContainerItemProxy */; 307 | }; 308 | /* End PBXTargetDependency section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 261463C41E5D884A00699242 /* Debug */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INFINITE_RECURSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 333 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 336 | CLANG_WARN_STRICT_PROTOTYPES = YES; 337 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 338 | CLANG_WARN_UNREACHABLE_CODE = YES; 339 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 341 | COPY_PHASE_STRIP = NO; 342 | CURRENT_PROJECT_VERSION = 1; 343 | DEBUG_INFORMATION_FORMAT = dwarf; 344 | ENABLE_STRICT_OBJC_MSGSEND = YES; 345 | ENABLE_TESTABILITY = YES; 346 | GCC_C_LANGUAGE_STANDARD = gnu99; 347 | GCC_DYNAMIC_NO_PIC = NO; 348 | GCC_NO_COMMON_BLOCKS = YES; 349 | GCC_OPTIMIZATION_LEVEL = 0; 350 | GCC_PREPROCESSOR_DEFINITIONS = ( 351 | "DEBUG=1", 352 | "$(inherited)", 353 | ); 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 361 | MTL_ENABLE_DEBUG_INFO = YES; 362 | ONLY_ACTIVE_ARCH = YES; 363 | SDKROOT = iphoneos; 364 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 365 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 366 | SWIFT_VERSION = ""; 367 | TARGETED_DEVICE_FAMILY = "1,2"; 368 | VERSIONING_SYSTEM = "apple-generic"; 369 | VERSION_INFO_PREFIX = ""; 370 | }; 371 | name = Debug; 372 | }; 373 | 261463C51E5D884A00699242 /* Release */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ALWAYS_SEARCH_USER_PATHS = NO; 377 | CLANG_ANALYZER_NONNULL = YES; 378 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 379 | CLANG_CXX_LIBRARY = "libc++"; 380 | CLANG_ENABLE_MODULES = YES; 381 | CLANG_ENABLE_OBJC_ARC = YES; 382 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 383 | CLANG_WARN_BOOL_CONVERSION = YES; 384 | CLANG_WARN_COMMA = YES; 385 | CLANG_WARN_CONSTANT_CONVERSION = YES; 386 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 389 | CLANG_WARN_EMPTY_BODY = YES; 390 | CLANG_WARN_ENUM_CONVERSION = YES; 391 | CLANG_WARN_INFINITE_RECURSION = YES; 392 | CLANG_WARN_INT_CONVERSION = YES; 393 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 394 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 395 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 397 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 398 | CLANG_WARN_STRICT_PROTOTYPES = YES; 399 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 400 | CLANG_WARN_UNREACHABLE_CODE = YES; 401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 402 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 403 | COPY_PHASE_STRIP = NO; 404 | CURRENT_PROJECT_VERSION = 1; 405 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 406 | ENABLE_NS_ASSERTIONS = NO; 407 | ENABLE_STRICT_OBJC_MSGSEND = YES; 408 | GCC_C_LANGUAGE_STANDARD = gnu99; 409 | GCC_NO_COMMON_BLOCKS = YES; 410 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 411 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 412 | GCC_WARN_UNDECLARED_SELECTOR = YES; 413 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 414 | GCC_WARN_UNUSED_FUNCTION = YES; 415 | GCC_WARN_UNUSED_VARIABLE = YES; 416 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 417 | MTL_ENABLE_DEBUG_INFO = NO; 418 | SDKROOT = iphoneos; 419 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 420 | SWIFT_VERSION = ""; 421 | TARGETED_DEVICE_FAMILY = "1,2"; 422 | VALIDATE_PRODUCT = YES; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | VERSION_INFO_PREFIX = ""; 425 | }; 426 | name = Release; 427 | }; 428 | 261463C71E5D884A00699242 /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | CLANG_ENABLE_MODULES = YES; 432 | CODE_SIGN_IDENTITY = ""; 433 | DEFINES_MODULE = YES; 434 | DEVELOPMENT_TEAM = G73SPYZ3Q7; 435 | DYLIB_COMPATIBILITY_VERSION = 1; 436 | DYLIB_CURRENT_VERSION = 1; 437 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 438 | FRAMEWORK_SEARCH_PATHS = ( 439 | "$(inherited)", 440 | "$(PROJECT_DIR)/Carthage/Build/iOS", 441 | ); 442 | INFOPLIST_FILE = "$(SRCROOT)/Resources/Info.plist"; 443 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 444 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 445 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 446 | PRODUCT_BUNDLE_IDENTIFIER = me.waft.RxResponderChain; 447 | PRODUCT_NAME = "$(TARGET_NAME)"; 448 | SKIP_INSTALL = YES; 449 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 450 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 451 | SWIFT_VERSION = 4.0; 452 | }; 453 | name = Debug; 454 | }; 455 | 261463C81E5D884A00699242 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | CLANG_ENABLE_MODULES = YES; 459 | CODE_SIGN_IDENTITY = ""; 460 | DEFINES_MODULE = YES; 461 | DEVELOPMENT_TEAM = G73SPYZ3Q7; 462 | DYLIB_COMPATIBILITY_VERSION = 1; 463 | DYLIB_CURRENT_VERSION = 1; 464 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 465 | FRAMEWORK_SEARCH_PATHS = ( 466 | "$(inherited)", 467 | "$(PROJECT_DIR)/Carthage/Build/iOS", 468 | ); 469 | INFOPLIST_FILE = "$(SRCROOT)/Resources/Info.plist"; 470 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 471 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 472 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 473 | PRODUCT_BUNDLE_IDENTIFIER = me.waft.RxResponderChain; 474 | PRODUCT_NAME = "$(TARGET_NAME)"; 475 | SKIP_INSTALL = YES; 476 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 477 | SWIFT_VERSION = 4.0; 478 | }; 479 | name = Release; 480 | }; 481 | 261463DB1E5D8A1500699242 /* Debug */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 485 | DEVELOPMENT_TEAM = G73SPYZ3Q7; 486 | FRAMEWORK_SEARCH_PATHS = ( 487 | "$(inherited)", 488 | "$(PROJECT_DIR)/Carthage/Build/iOS", 489 | ); 490 | INFOPLIST_FILE = Resources/Info.plist; 491 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 492 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 493 | PRODUCT_BUNDLE_IDENTIFIER = me.waft.RxResponderChainTests; 494 | PRODUCT_NAME = "$(TARGET_NAME)"; 495 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 496 | SWIFT_VERSION = 4.0; 497 | }; 498 | name = Debug; 499 | }; 500 | 261463DC1E5D8A1500699242 /* Release */ = { 501 | isa = XCBuildConfiguration; 502 | buildSettings = { 503 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 504 | DEVELOPMENT_TEAM = G73SPYZ3Q7; 505 | FRAMEWORK_SEARCH_PATHS = ( 506 | "$(inherited)", 507 | "$(PROJECT_DIR)/Carthage/Build/iOS", 508 | ); 509 | INFOPLIST_FILE = Resources/Info.plist; 510 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 511 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 512 | PRODUCT_BUNDLE_IDENTIFIER = me.waft.RxResponderChainTests; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 515 | SWIFT_VERSION = 4.0; 516 | }; 517 | name = Release; 518 | }; 519 | /* End XCBuildConfiguration section */ 520 | 521 | /* Begin XCConfigurationList section */ 522 | 261463B81E5D884A00699242 /* Build configuration list for PBXProject "RxResponderChain" */ = { 523 | isa = XCConfigurationList; 524 | buildConfigurations = ( 525 | 261463C41E5D884A00699242 /* Debug */, 526 | 261463C51E5D884A00699242 /* Release */, 527 | ); 528 | defaultConfigurationIsVisible = 0; 529 | defaultConfigurationName = Release; 530 | }; 531 | 261463C61E5D884A00699242 /* Build configuration list for PBXNativeTarget "RxResponderChain" */ = { 532 | isa = XCConfigurationList; 533 | buildConfigurations = ( 534 | 261463C71E5D884A00699242 /* Debug */, 535 | 261463C81E5D884A00699242 /* Release */, 536 | ); 537 | defaultConfigurationIsVisible = 0; 538 | defaultConfigurationName = Release; 539 | }; 540 | 261463DA1E5D8A1500699242 /* Build configuration list for PBXNativeTarget "RxResponderChainTests" */ = { 541 | isa = XCConfigurationList; 542 | buildConfigurations = ( 543 | 261463DB1E5D8A1500699242 /* Debug */, 544 | 261463DC1E5D8A1500699242 /* Release */, 545 | ); 546 | defaultConfigurationIsVisible = 0; 547 | defaultConfigurationName = Release; 548 | }; 549 | /* End XCConfigurationList section */ 550 | }; 551 | rootObject = 261463B51E5D884A00699242 /* Project object */; 552 | } 553 | -------------------------------------------------------------------------------- /RxResponderChain.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxResponderChain.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RxResponderChain.xcodeproj/xcshareddata/xcschemes/RxResponderChain.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Sources/ResponderChain+Rx.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIResponder+Rx.swift 3 | // RxResponderChain 4 | // 5 | // Created by Yuki Takahashi on 2017/02/22. 6 | // Copyright © 2017年 waft. All rights reserved. 7 | // 8 | 9 | #if os(iOS) 10 | 11 | import RxSwift 12 | import RxCocoa 13 | 14 | // MARK: - ResponderChainEvent 15 | 16 | public protocol ResponderChainEvent { 17 | } 18 | 19 | // MARK: - ResponderChain 20 | 21 | public struct ResponderChain: ObserverType { 22 | fileprivate let reactive: Reactive 23 | 24 | fileprivate init(_ reactive: Reactive) { 25 | self.reactive = reactive 26 | } 27 | 28 | public func on(_ event: RxSwift.Event) { 29 | var currentResponder: UIResponder? = reactive.base 30 | while let responder = currentResponder { 31 | responder.rx.responderChainEventObserver.on(event) 32 | currentResponder = responder.next 33 | } 34 | } 35 | 36 | public func event(_ type: E.Type) -> Observable { 37 | return reactive.responderChainEventObserver 38 | .filter { 39 | $0 is E 40 | } 41 | .map { 42 | castOrFatalError($0) as E 43 | } 44 | } 45 | } 46 | 47 | // MARK: - Reactive 48 | 49 | private var responderChainEventObserverContext: UInt8 = 0 50 | 51 | public extension Reactive where Base: UIResponder { 52 | public var responderChain: ResponderChain { 53 | return ResponderChain(self) 54 | } 55 | 56 | fileprivate var responderChainEventObserver: PublishSubject { 57 | if let observer = objc_getAssociatedObject(base, &responderChainEventObserverContext) 58 | as? PublishSubject { 59 | return observer 60 | } 61 | 62 | let observer = PublishSubject() 63 | 64 | objc_setAssociatedObject(base, &responderChainEventObserverContext, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 65 | return observer 66 | } 67 | } 68 | 69 | // MARK: - Bind 70 | 71 | public extension ObservableType where E: ResponderChainEvent { 72 | 73 | @available(*, deprecated, renamed: "bind(to:)") 74 | public func bindTo(_ observer: O) -> Disposable where O: ObserverType, O.E == Self.E { 75 | return bind(to: observer) 76 | } 77 | 78 | public func bind(to observer: O) -> Disposable where O.E == ResponderChainEvent { 79 | return self.map { $0 as ResponderChainEvent } 80 | .bind(to: observer) 81 | } 82 | } 83 | 84 | // MARK: - Utils 85 | 86 | private func castOrFatalError(_ value: Any!) -> T { 87 | let maybeResult: T? = value as? T 88 | guard let result = maybeResult else { 89 | fatalError("Failure converting from \(value) to \(T.self)") 90 | } 91 | return result 92 | } 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import RxResponderChainTests 3 | 4 | XCTMain([ 5 | testCase(RxResponderChainTests.allTests), 6 | ]) 7 | -------------------------------------------------------------------------------- /Tests/RxResponderChainTests/RxResponderChainTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if os(iOS) 4 | import UIKit 5 | import RxSwift 6 | import RxCocoa 7 | import RxTest 8 | #endif 9 | 10 | @testable import RxResponderChain 11 | 12 | class RxResponderChainTests: XCTestCase { 13 | static var allTests : [(String, (RxResponderChainTests) -> () throws -> Void)] { 14 | return [ 15 | ] 16 | } 17 | 18 | #if os(iOS) 19 | struct TestEvent: ResponderChainEvent, Equatable { 20 | let a: Int 21 | 22 | static func == (lhs: TestEvent, rhs: TestEvent) -> Bool { 23 | return lhs.a == rhs.a 24 | } 25 | } 26 | 27 | struct TestEvent2: ResponderChainEvent, Equatable { 28 | let a: Int 29 | 30 | static func == (lhs: TestEvent2, rhs: TestEvent2) -> Bool { 31 | return lhs.a == rhs.a 32 | } 33 | } 34 | 35 | let disposeBag = DisposeBag() 36 | 37 | func testResponderChainEvent() { 38 | let viewController = UIViewController() 39 | let view = viewController.view! 40 | let subview1 = UIView() 41 | let subview2 = UIView() 42 | view.addSubview(subview1) 43 | view.addSubview(subview2) 44 | 45 | 46 | let scheduler = TestScheduler(initialClock: 0) 47 | 48 | let xs = scheduler.createHotObservable([ 49 | next(100, TestEvent(a: 1)), 50 | next(200, TestEvent(a: 2)), 51 | next(300, TestEvent(a: 3)), 52 | next(400, TestEvent(a: 4)), 53 | ]) 54 | 55 | let observerForView = scheduler.createObserver(TestEvent.self) 56 | let observerForVC = scheduler.createObserver(TestEvent.self) 57 | 58 | let observerForSubview = scheduler.createObserver(TestEvent.self) 59 | let observerForTestEvent2 = scheduler.createObserver(TestEvent2.self) 60 | 61 | scheduler.scheduleAt(0) { 62 | view.rx.responderChain.event(TestEvent.self) 63 | .subscribe(observerForView) 64 | .disposed(by: self.disposeBag) 65 | 66 | viewController.rx.responderChain.event(TestEvent.self) 67 | .subscribe(observerForVC) 68 | .disposed(by:self.disposeBag) 69 | 70 | subview2.rx.responderChain.event(TestEvent.self) 71 | .subscribe(observerForSubview) 72 | .disposed(by:self.disposeBag) 73 | 74 | view.rx.responderChain.event(TestEvent2.self) 75 | .subscribe(observerForTestEvent2) 76 | .disposed(by:self.disposeBag) 77 | 78 | xs.bind(to:viewController.view.rx.responderChain) 79 | .disposed(by:self.disposeBag) 80 | } 81 | 82 | scheduler.start() 83 | 84 | let expectedEvent = [ 85 | next(100, TestEvent(a: 1)), 86 | next(200, TestEvent(a: 2)), 87 | next(300, TestEvent(a: 3)), 88 | next(400, TestEvent(a: 4)), 89 | ] 90 | 91 | XCTAssertEqual(observerForView.events, expectedEvent) 92 | XCTAssertEqual(observerForVC.events, expectedEvent) 93 | XCTAssertEqual(observerForSubview.events, []) 94 | XCTAssertEqual(observerForTestEvent2.events, []) 95 | 96 | } 97 | #endif 98 | } 99 | --------------------------------------------------------------------------------