├── .gitignore ├── .travis.yml ├── Example └── RxBinding Example │ ├── Podfile │ ├── Podfile.lock │ ├── RxBinding Example.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── RxBinding Example.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── RxBinding Example │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ └── LaunchScreen.storyboard │ ├── Info.plist │ ├── ViewController.swift │ └── ViewModel.swift ├── LICENSE ├── Package.swift ├── README.md ├── RxBinding.podspec ├── Sources └── RxBinding │ ├── DisposedBy.swift │ ├── Drive.swift │ ├── Emit.swift │ ├── OneWayBind.swift │ └── TwoWayBind.swift └── Tests ├── LinuxMain.swift └── RxBindingTests ├── RxBindingTests.swift └── XCTestManifests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | Pods/ 38 | /Podfile.lock 39 | 40 | # IDEA 41 | .idea/ 42 | 43 | .build 44 | DerivedData 45 | /.previous-build 46 | xcuserdata 47 | .DS_Store 48 | *~ 49 | \#* 50 | .\#* 51 | .*.sw[nop] 52 | *.xcscmblueprint 53 | /default.profraw 54 | Utilities/Docker/*.tar.gz 55 | .swiftpm 56 | Package.resolved 57 | /build 58 | *.pyc 59 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode12.3 6 | language: objective-c 7 | 8 | before_install: 9 | - gem install cocoapods 10 | - pod repo update 11 | - pod install --project-directory=Example/RxBinding\ Example 12 | 13 | script: 14 | - set -o pipefail && xcodebuild -workspace Example/RxBinding\ Example/RxBinding\ Example.xcworkspace -scheme RxBinding\ Example -sdk iphonesimulator build CODE_SIGNING_REQUIRED=NO | xcpretty -c 15 | -------------------------------------------------------------------------------- /Example/RxBinding Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | platform :ios, '10.0' 3 | 4 | target 'RxBinding Example' do 5 | pod 'RxBinding', :path => '../../' 6 | pod 'SnapKit' 7 | end -------------------------------------------------------------------------------- /Example/RxBinding Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - RxBinding (0.5): 3 | - RxCocoa (~> 6) 4 | - RxSwift (~> 6) 5 | - RxCocoa (6.0.0): 6 | - RxRelay (= 6.0.0) 7 | - RxSwift (= 6.0.0) 8 | - RxRelay (6.0.0): 9 | - RxSwift (= 6.0.0) 10 | - RxSwift (6.0.0) 11 | - SnapKit (5.0.1) 12 | 13 | DEPENDENCIES: 14 | - RxBinding (from `../../`) 15 | - SnapKit 16 | 17 | SPEC REPOS: 18 | trunk: 19 | - RxCocoa 20 | - RxRelay 21 | - RxSwift 22 | - SnapKit 23 | 24 | EXTERNAL SOURCES: 25 | RxBinding: 26 | :path: "../../" 27 | 28 | SPEC CHECKSUMS: 29 | RxBinding: e3c76d02d0ee3f1a306a0fb8e8ef6f2eda65a375 30 | RxCocoa: 3f79328fafa3645b34600f37c31e64c73ae3a80e 31 | RxRelay: 8d593be109c06ea850df027351beba614b012ffb 32 | RxSwift: c14e798c59b9f6e9a2df8fd235602e85cc044295 33 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb 34 | 35 | PODFILE CHECKSUM: 3dc791eedabb6208496ff999c24c1b9d6b616432 36 | 37 | COCOAPODS: 1.10.1 38 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0C50054F245DB520005F68F1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C50054E245DB520005F68F1 /* AppDelegate.swift */; }; 11 | 0C500553245DB520005F68F1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C500552245DB520005F68F1 /* ViewController.swift */; }; 12 | 0C500558245DB521005F68F1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C500557245DB521005F68F1 /* Assets.xcassets */; }; 13 | 0C50055B245DB521005F68F1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C500559245DB521005F68F1 /* LaunchScreen.storyboard */; }; 14 | 0C500563245DB615005F68F1 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C500562245DB615005F68F1 /* ViewModel.swift */; }; 15 | E39CB3486BB52048F455E2A7 /* Pods_RxBinding_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B301674551EDF0B750C27BC /* Pods_RxBinding_Example.framework */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 0C50054B245DB520005F68F1 /* RxBinding Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RxBinding Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 0C50054E245DB520005F68F1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | 0C500552245DB520005F68F1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 22 | 0C500557245DB521005F68F1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 23 | 0C50055A245DB521005F68F1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 24 | 0C50055C245DB521005F68F1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 25 | 0C500562245DB615005F68F1 /* ViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 26 | 2B301674551EDF0B750C27BC /* Pods_RxBinding_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxBinding_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 90A51AF33219D5BBA0AC9CFD /* Pods-RxBinding Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxBinding Example.debug.xcconfig"; path = "Target Support Files/Pods-RxBinding Example/Pods-RxBinding Example.debug.xcconfig"; sourceTree = ""; }; 28 | AE896CCF68D6174B32B08CB6 /* Pods-RxBinding Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxBinding Example.release.xcconfig"; path = "Target Support Files/Pods-RxBinding Example/Pods-RxBinding Example.release.xcconfig"; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 0C500548245DB520005F68F1 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | E39CB3486BB52048F455E2A7 /* Pods_RxBinding_Example.framework in Frameworks */, 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 0C500542245DB520005F68F1 = { 44 | isa = PBXGroup; 45 | children = ( 46 | 0C50054D245DB520005F68F1 /* RxBinding Example */, 47 | 0C50054C245DB520005F68F1 /* Products */, 48 | 7164AD5D77CA4900F57ECFE9 /* Pods */, 49 | 685B8F667EBE448DF4998E65 /* Frameworks */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | 0C50054C245DB520005F68F1 /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 0C50054B245DB520005F68F1 /* RxBinding Example.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | 0C50054D245DB520005F68F1 /* RxBinding Example */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 0C50054E245DB520005F68F1 /* AppDelegate.swift */, 65 | 0C500552245DB520005F68F1 /* ViewController.swift */, 66 | 0C500562245DB615005F68F1 /* ViewModel.swift */, 67 | 0C500557245DB521005F68F1 /* Assets.xcassets */, 68 | 0C500559245DB521005F68F1 /* LaunchScreen.storyboard */, 69 | 0C50055C245DB521005F68F1 /* Info.plist */, 70 | ); 71 | path = "RxBinding Example"; 72 | sourceTree = ""; 73 | }; 74 | 685B8F667EBE448DF4998E65 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 2B301674551EDF0B750C27BC /* Pods_RxBinding_Example.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | 7164AD5D77CA4900F57ECFE9 /* Pods */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 90A51AF33219D5BBA0AC9CFD /* Pods-RxBinding Example.debug.xcconfig */, 86 | AE896CCF68D6174B32B08CB6 /* Pods-RxBinding Example.release.xcconfig */, 87 | ); 88 | path = Pods; 89 | sourceTree = ""; 90 | }; 91 | /* End PBXGroup section */ 92 | 93 | /* Begin PBXNativeTarget section */ 94 | 0C50054A245DB520005F68F1 /* RxBinding Example */ = { 95 | isa = PBXNativeTarget; 96 | buildConfigurationList = 0C50055F245DB521005F68F1 /* Build configuration list for PBXNativeTarget "RxBinding Example" */; 97 | buildPhases = ( 98 | FB6E38A9A69BFFD6033C036C /* [CP] Check Pods Manifest.lock */, 99 | 0C500547245DB520005F68F1 /* Sources */, 100 | 0C500548245DB520005F68F1 /* Frameworks */, 101 | 0C500549245DB520005F68F1 /* Resources */, 102 | 7562AF4CD513E3D5FF96D35F /* [CP] Embed Pods Frameworks */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = "RxBinding Example"; 109 | productName = "RxBinding Example"; 110 | productReference = 0C50054B245DB520005F68F1 /* RxBinding Example.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | 0C500543245DB520005F68F1 /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | LastSwiftUpdateCheck = 1140; 120 | LastUpgradeCheck = 1140; 121 | ORGANIZATIONNAME = CocoaPods; 122 | TargetAttributes = { 123 | 0C50054A245DB520005F68F1 = { 124 | CreatedOnToolsVersion = 11.4.1; 125 | }; 126 | }; 127 | }; 128 | buildConfigurationList = 0C500546245DB520005F68F1 /* Build configuration list for PBXProject "RxBinding Example" */; 129 | compatibilityVersion = "Xcode 9.3"; 130 | developmentRegion = en; 131 | hasScannedForEncodings = 0; 132 | knownRegions = ( 133 | en, 134 | Base, 135 | ); 136 | mainGroup = 0C500542245DB520005F68F1; 137 | productRefGroup = 0C50054C245DB520005F68F1 /* Products */; 138 | projectDirPath = ""; 139 | projectRoot = ""; 140 | targets = ( 141 | 0C50054A245DB520005F68F1 /* RxBinding Example */, 142 | ); 143 | }; 144 | /* End PBXProject section */ 145 | 146 | /* Begin PBXResourcesBuildPhase section */ 147 | 0C500549245DB520005F68F1 /* Resources */ = { 148 | isa = PBXResourcesBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | 0C50055B245DB521005F68F1 /* LaunchScreen.storyboard in Resources */, 152 | 0C500558245DB521005F68F1 /* Assets.xcassets in Resources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXResourcesBuildPhase section */ 157 | 158 | /* Begin PBXShellScriptBuildPhase section */ 159 | 7562AF4CD513E3D5FF96D35F /* [CP] Embed Pods Frameworks */ = { 160 | isa = PBXShellScriptBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | ); 164 | inputFileListPaths = ( 165 | "${PODS_ROOT}/Target Support Files/Pods-RxBinding Example/Pods-RxBinding Example-frameworks-${CONFIGURATION}-input-files.xcfilelist", 166 | ); 167 | name = "[CP] Embed Pods Frameworks"; 168 | outputFileListPaths = ( 169 | "${PODS_ROOT}/Target Support Files/Pods-RxBinding Example/Pods-RxBinding Example-frameworks-${CONFIGURATION}-output-files.xcfilelist", 170 | ); 171 | runOnlyForDeploymentPostprocessing = 0; 172 | shellPath = /bin/sh; 173 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RxBinding Example/Pods-RxBinding Example-frameworks.sh\"\n"; 174 | showEnvVarsInLog = 0; 175 | }; 176 | FB6E38A9A69BFFD6033C036C /* [CP] Check Pods Manifest.lock */ = { 177 | isa = PBXShellScriptBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | ); 181 | inputFileListPaths = ( 182 | ); 183 | inputPaths = ( 184 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 185 | "${PODS_ROOT}/Manifest.lock", 186 | ); 187 | name = "[CP] Check Pods Manifest.lock"; 188 | outputFileListPaths = ( 189 | ); 190 | outputPaths = ( 191 | "$(DERIVED_FILE_DIR)/Pods-RxBinding Example-checkManifestLockResult.txt", 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | shellPath = /bin/sh; 195 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 196 | showEnvVarsInLog = 0; 197 | }; 198 | /* End PBXShellScriptBuildPhase section */ 199 | 200 | /* Begin PBXSourcesBuildPhase section */ 201 | 0C500547245DB520005F68F1 /* Sources */ = { 202 | isa = PBXSourcesBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | 0C500553245DB520005F68F1 /* ViewController.swift in Sources */, 206 | 0C50054F245DB520005F68F1 /* AppDelegate.swift in Sources */, 207 | 0C500563245DB615005F68F1 /* ViewModel.swift in Sources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXSourcesBuildPhase section */ 212 | 213 | /* Begin PBXVariantGroup section */ 214 | 0C500559245DB521005F68F1 /* LaunchScreen.storyboard */ = { 215 | isa = PBXVariantGroup; 216 | children = ( 217 | 0C50055A245DB521005F68F1 /* Base */, 218 | ); 219 | name = LaunchScreen.storyboard; 220 | sourceTree = ""; 221 | }; 222 | /* End PBXVariantGroup section */ 223 | 224 | /* Begin XCBuildConfiguration section */ 225 | 0C50055D245DB521005F68F1 /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | ALWAYS_SEARCH_USER_PATHS = NO; 229 | CLANG_ANALYZER_NONNULL = YES; 230 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 231 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 232 | CLANG_CXX_LIBRARY = "libc++"; 233 | CLANG_ENABLE_MODULES = YES; 234 | CLANG_ENABLE_OBJC_ARC = YES; 235 | CLANG_ENABLE_OBJC_WEAK = YES; 236 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 237 | CLANG_WARN_BOOL_CONVERSION = YES; 238 | CLANG_WARN_COMMA = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 242 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 243 | CLANG_WARN_EMPTY_BODY = YES; 244 | CLANG_WARN_ENUM_CONVERSION = YES; 245 | CLANG_WARN_INFINITE_RECURSION = YES; 246 | CLANG_WARN_INT_CONVERSION = YES; 247 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 248 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 249 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 250 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 251 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 252 | CLANG_WARN_STRICT_PROTOTYPES = YES; 253 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 254 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 255 | CLANG_WARN_UNREACHABLE_CODE = YES; 256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 257 | COPY_PHASE_STRIP = NO; 258 | DEBUG_INFORMATION_FORMAT = dwarf; 259 | ENABLE_STRICT_OBJC_MSGSEND = YES; 260 | ENABLE_TESTABILITY = YES; 261 | GCC_C_LANGUAGE_STANDARD = gnu11; 262 | GCC_DYNAMIC_NO_PIC = NO; 263 | GCC_NO_COMMON_BLOCKS = YES; 264 | GCC_OPTIMIZATION_LEVEL = 0; 265 | GCC_PREPROCESSOR_DEFINITIONS = ( 266 | "DEBUG=1", 267 | "$(inherited)", 268 | ); 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 276 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 277 | MTL_FAST_MATH = YES; 278 | ONLY_ACTIVE_ARCH = YES; 279 | SDKROOT = iphoneos; 280 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 281 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 282 | }; 283 | name = Debug; 284 | }; 285 | 0C50055E245DB521005F68F1 /* Release */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ALWAYS_SEARCH_USER_PATHS = NO; 289 | CLANG_ANALYZER_NONNULL = YES; 290 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 291 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 292 | CLANG_CXX_LIBRARY = "libc++"; 293 | CLANG_ENABLE_MODULES = YES; 294 | CLANG_ENABLE_OBJC_ARC = YES; 295 | CLANG_ENABLE_OBJC_WEAK = YES; 296 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 297 | CLANG_WARN_BOOL_CONVERSION = YES; 298 | CLANG_WARN_COMMA = YES; 299 | CLANG_WARN_CONSTANT_CONVERSION = YES; 300 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 301 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 302 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN_ENUM_CONVERSION = YES; 305 | CLANG_WARN_INFINITE_RECURSION = YES; 306 | CLANG_WARN_INT_CONVERSION = YES; 307 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 308 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 309 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 315 | CLANG_WARN_UNREACHABLE_CODE = YES; 316 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 317 | COPY_PHASE_STRIP = NO; 318 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 319 | ENABLE_NS_ASSERTIONS = NO; 320 | ENABLE_STRICT_OBJC_MSGSEND = YES; 321 | GCC_C_LANGUAGE_STANDARD = gnu11; 322 | GCC_NO_COMMON_BLOCKS = YES; 323 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 324 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 325 | GCC_WARN_UNDECLARED_SELECTOR = YES; 326 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 327 | GCC_WARN_UNUSED_FUNCTION = YES; 328 | GCC_WARN_UNUSED_VARIABLE = YES; 329 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 330 | MTL_ENABLE_DEBUG_INFO = NO; 331 | MTL_FAST_MATH = YES; 332 | SDKROOT = iphoneos; 333 | SWIFT_COMPILATION_MODE = wholemodule; 334 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 335 | VALIDATE_PRODUCT = YES; 336 | }; 337 | name = Release; 338 | }; 339 | 0C500560245DB521005F68F1 /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | baseConfigurationReference = 90A51AF33219D5BBA0AC9CFD /* Pods-RxBinding Example.debug.xcconfig */; 342 | buildSettings = { 343 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 344 | CODE_SIGN_STYLE = Automatic; 345 | INFOPLIST_FILE = "RxBinding Example/Info.plist"; 346 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 347 | LD_RUNPATH_SEARCH_PATHS = ( 348 | "$(inherited)", 349 | "@executable_path/Frameworks", 350 | ); 351 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.RxBinding-Example"; 352 | PRODUCT_NAME = "$(TARGET_NAME)"; 353 | SWIFT_VERSION = 5.0; 354 | TARGETED_DEVICE_FAMILY = "1,2"; 355 | }; 356 | name = Debug; 357 | }; 358 | 0C500561245DB521005F68F1 /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | baseConfigurationReference = AE896CCF68D6174B32B08CB6 /* Pods-RxBinding Example.release.xcconfig */; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | CODE_SIGN_STYLE = Automatic; 364 | INFOPLIST_FILE = "RxBinding Example/Info.plist"; 365 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 366 | LD_RUNPATH_SEARCH_PATHS = ( 367 | "$(inherited)", 368 | "@executable_path/Frameworks", 369 | ); 370 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.RxBinding-Example"; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | SWIFT_VERSION = 5.0; 373 | TARGETED_DEVICE_FAMILY = "1,2"; 374 | }; 375 | name = Release; 376 | }; 377 | /* End XCBuildConfiguration section */ 378 | 379 | /* Begin XCConfigurationList section */ 380 | 0C500546245DB520005F68F1 /* Build configuration list for PBXProject "RxBinding Example" */ = { 381 | isa = XCConfigurationList; 382 | buildConfigurations = ( 383 | 0C50055D245DB521005F68F1 /* Debug */, 384 | 0C50055E245DB521005F68F1 /* Release */, 385 | ); 386 | defaultConfigurationIsVisible = 0; 387 | defaultConfigurationName = Release; 388 | }; 389 | 0C50055F245DB521005F68F1 /* Build configuration list for PBXNativeTarget "RxBinding Example" */ = { 390 | isa = XCConfigurationList; 391 | buildConfigurations = ( 392 | 0C500560245DB521005F68F1 /* Debug */, 393 | 0C500561245DB521005F68F1 /* Release */, 394 | ); 395 | defaultConfigurationIsVisible = 0; 396 | defaultConfigurationName = Release; 397 | }; 398 | /* End XCConfigurationList section */ 399 | }; 400 | rootObject = 0C500543245DB520005F68F1 /* Project object */; 401 | } 402 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RxBinding 4 | // 5 | // Created by Meng Li on 03/14/2019. 6 | // Copyright (c) 2019 MuShare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | window = UIWindow() 18 | window?.rootViewController = ViewController(viewModel: ViewModel()) 19 | window?.makeKeyAndVisible() 20 | return true 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 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 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RxBinding 4 | // 5 | // Created by Meng Li on 03/14/2019. 6 | // Copyright (c) 2019 MuShare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | import RxSwift 12 | import RxBinding 13 | 14 | class ViewController: UIViewController { 15 | 16 | private let textFeild: UITextField = { 17 | let textField = UITextField() 18 | textField.textAlignment = .center 19 | textField.placeholder = "Input text here." 20 | textField.backgroundColor = UIColor(white: 0.9, alpha: 1) 21 | textField.layer.cornerRadius = 5 22 | textField.layer.masksToBounds = true 23 | textField.becomeFirstResponder() 24 | return textField 25 | }() 26 | 27 | private let label = UILabel() 28 | 29 | private let characterCountLabel1 = UILabel() 30 | private let characterCountLabel2 = UILabel() 31 | 32 | private let viewModel: ViewModel 33 | private let disposeBag = DisposeBag() 34 | 35 | init(viewModel: ViewModel) { 36 | self.viewModel = viewModel 37 | super.init(nibName: nil, bundle: nil) 38 | } 39 | 40 | required init?(coder aDecoder: NSCoder) { 41 | fatalError("init(coder:) has not been implemented") 42 | } 43 | 44 | override func viewDidLoad() { 45 | super.viewDidLoad() 46 | 47 | view.backgroundColor = .white 48 | view.addSubview(textFeild) 49 | view.addSubview(label) 50 | view.addSubview(characterCountLabel1) 51 | view.addSubview(characterCountLabel2) 52 | createConstraints() 53 | 54 | disposeBag ~ [ 55 | viewModel.text <~> textFeild.rx.text, 56 | viewModel.uppercaseText ~> label.rx.text, 57 | viewModel.charactersCount ~> [characterCountLabel1, characterCountLabel2].map { $0.rx.text } 58 | ] 59 | 60 | /** 61 | disposeBag 62 | ~ viewModel.text <~> textFeild.rx.text 63 | ~ viewModel.uppercaseText ~> label.rx.text 64 | ~ viewModel.charactersCount ~> [characterCountLabel1, characterCountLabel2].map { $0.rx.text } 65 | */ 66 | 67 | /** 68 | viewModel.text <~> textFeild.rx.text ~ 69 | viewModel.uppercaseText ~> label.rx.text ~ 70 | viewModel.charactersCount ~> [characterCountLabel1, characterCountLabel2].map { $0.rx.text } 71 | ~ disposeBag 72 | */ 73 | 74 | /** The demo to use the ~> operator for drivers. 75 | viewModel.uppercaseText.asDriver(onErrorJustReturn: "") ~> label.rx.text ~ disposeBag 76 | viewModel.charactersCount.asDriver(onErrorJustReturn: "0") ~> [characterCountLabel1, characterCountLabel2].map { $0.rx.text } ~ disposeBag 77 | */ 78 | } 79 | 80 | private func createConstraints() { 81 | 82 | textFeild.snp.makeConstraints { 83 | $0.left.equalToSuperview().offset(15) 84 | $0.right.equalToSuperview().offset(-15) 85 | $0.top.equalToSuperview().offset(60) 86 | $0.height.equalTo(50) 87 | } 88 | 89 | label.snp.makeConstraints { 90 | $0.centerX.equalToSuperview() 91 | $0.top.equalTo(textFeild.snp.bottom).offset(10) 92 | } 93 | 94 | characterCountLabel1.snp.makeConstraints { 95 | $0.centerX.equalToSuperview() 96 | $0.top.equalTo(label.snp.bottom).offset(10) 97 | } 98 | 99 | characterCountLabel2.snp.makeConstraints { 100 | $0.centerX.equalToSuperview() 101 | $0.top.equalTo(characterCountLabel1.snp.bottom).offset(10) 102 | } 103 | 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /Example/RxBinding Example/RxBinding Example/ViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModel.swift 3 | // RxBinding_Example 4 | // 5 | // Created by Meng Li on 2019/03/19. 6 | // Copyright © 2019 MuShare. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import RxCocoa 11 | 12 | class ViewModel { 13 | 14 | let text = BehaviorRelay(value: nil) 15 | 16 | var uppercaseText: Observable { 17 | return text.map { 18 | guard let text = $0 else { 19 | return nil 20 | } 21 | return text.uppercased() 22 | } 23 | } 24 | 25 | var charactersCount: Observable { 26 | return text.map { 27 | guard let text = $0 else { 28 | return "0" 29 | } 30 | return String(text.count) 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 lm2343635 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "RxBinding", 8 | platforms: [ 9 | .iOS(.v10) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 13 | .library( 14 | name: "RxBinding", 15 | targets: ["RxBinding"]), 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.0.0")), 20 | ], 21 | targets: [ 22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 23 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 24 | .target( 25 | name: "RxBinding", 26 | dependencies: [ 27 | "RxSwift", 28 | .product(name: "RxCocoa", package: "RxSwift") 29 | ]), 30 | .testTarget( 31 | name: "RxBindingTests", 32 | dependencies: ["RxBinding"]), 33 | ] 34 | ) 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxBinding 2 | 3 | [![CI Status](https://img.shields.io/travis/RxSwiftCommunity/RxBinding.svg?style=flat)](https://travis-ci.org/RxSwiftCommunity/RxBinding) 4 | [![Version](https://img.shields.io/cocoapods/v/RxBinding.svg?style=flat)](https://cocoapods.org/pods/RxBinding) 5 | [![License](https://img.shields.io/cocoapods/l/RxBinding.svg?style=flat)](https://cocoapods.org/pods/RxBinding) 6 | [![Platform](https://img.shields.io/cocoapods/p/RxBinding.svg?style=flat)](https://cocoapods.org/pods/RxBinding) 7 | [![swift](https://img.shields.io/badge/swift-5.0-orange.svg)](https://github.com/RxSwiftCommunity/RxBinding/releases) 8 | 9 | RxBinding provides `~>`, `<~>` and `~` operators for data binding using RxSwift, to replace the `bind(to:)` and `disposed(by:)` method in RxSwift. 10 | 11 | RxBinding is inspired by the following operators. 12 | 13 | - The `<->` operator in RxBiBinding (https://github.com/RxSwiftCommunity/RxBiBinding) 14 | - The `<~` operator in ReactiveCocoa (https://github.com/ReactiveCocoa/ReactiveCocoa) 15 | 16 | ## Documentation 17 | 18 | RxBinding is available through [CocoaPods](https://cocoapods.org). To install 19 | it, simply add the following line to your Podfile: 20 | 21 | ```ruby 22 | pod 'RxBinding' 23 | ``` 24 | 25 | With `@_expoerted import`, the operators can be used in the all file of the project. 26 | 27 | ```Swift 28 | @_exported import RxBinding 29 | ``` 30 | 31 | #### Usage of `~>` 32 | 33 | The type of `text` is `Observable` and the type of `label.rx.text` is `Binder`. 34 | RxSwfit provides the following method for the one way data binding between them. 35 | 36 | ```Swift 37 | viewModel.text.bind(to: label.rx.text).disposed(by: disposeBag) 38 | ``` 39 | 40 | With the operators `~>` (`bind(to:)`) and `~` (`disposed(by:)`) in RxBinding, we can bind with the following simple code. 41 | 42 | ```Swift 43 | viewModel.text ~> label.rx.text ~ disposeBag 44 | ``` 45 | 46 | Bind an observable object to multiple binders. 47 | 48 | ```Swift 49 | viewModel.text ~> [label1, label2].map { $0.rx.text } ~ disposeBag 50 | ``` 51 | 52 | #### Usage of `<~>` 53 | 54 | The type of `text` is `BehaviorRelay` and the type of `textFeild.rx.text` is `ControlProperty`. 55 | To apply the two way data binding between them, we need the following code by RxSwift. 56 | 57 | ```Swift 58 | viewModel.text.bind(to: textFeild.rx.text).disposed(by: disposeBag) 59 | textFeild.rx.text.bind(to: viewModel.text).disposed(by: disposeBag) 60 | ``` 61 | 62 | With the `<~>`, a simple two way bind operator, and `~` (`disposed(by:)`) in RxBinding, we can do the same thing with the following simple code. 63 | 64 | ```Swift 65 | viewModel.text <~> textFeild.rx.text ~ disposeBag 66 | ``` 67 | 68 | #### Multiple Bindings 69 | 70 | RxBinding supports using a single `disposeBag` for multiple binding operators like this: 71 | 72 | ```Swift 73 | disposeBag ~ [ 74 | viewModel.text <~> textFeild.rx.text, 75 | viewModel.uppercaseText ~> label.rx.text, 76 | viewModel.charactersCount ~> [characterCountLabel1, characterCountLabel2].map { $0.rx.text } 77 | ] 78 | ``` 79 | or this: 80 | 81 | ```Swift 82 | viewModel.text <~> textFeild.rx.text ~ 83 | viewModel.uppercaseText ~> label.rx.text ~ 84 | viewModel.charactersCount ~> [characterCountLabel1, characterCountLabel2].map { $0.rx.text } 85 | ~ disposeBag 86 | ``` 87 | 88 | ## RxCocoa 89 | 90 | RxBinding also supports `Driver` and `Signal` of the RxCocoa module. 91 | You can use `~>` operator to replace the `drive()` and `emit(to:)` method. 92 | 93 | ## NEED YOUR HELP 94 | 95 | **I am considering how to remove the operator ~ after the Binder or the ControlEvent property.** 96 | 97 | ```Swift 98 | viewModel.text ~> label.rx.text 99 | ``` 100 | 101 | If anyone has a good idea about this, please contact me here https://github.com/RxSwiftCommunity/RxBinding/issues/1 or create a PR. 102 | Thanks. 103 | 104 | The operator `~>` is equal to `bind(to:)`. 105 | 106 | ```swift 107 | viewModel.text ~> label.rx.text 108 | ``` 109 | is euqals to 110 | ```swift 111 | viewModel.text.bind(to: label.rx.text) 112 | ```` 113 | 114 | I mean how to combine the method `disposed(by:)` into the operator `~>`. 115 | 116 | ## Example 117 | 118 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 119 | 120 | ## Author 121 | 122 | lm2343635, lm2343635@126.com 123 | 124 | ## License 125 | 126 | RxBinding is available under the MIT license. See the LICENSE file for more info. 127 | -------------------------------------------------------------------------------- /RxBinding.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint RxBinding.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'RxBinding' 11 | s.version = '0.5' 12 | s.summary = 'Simple data binding operators for RxSwift.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | RxBinding provodes '~>', '<~>' and '~' operators for data binding using RxSwift. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/RxSwiftCommunity/RxBinding' 25 | s.license = { :type => 'MIT', :file => 'LICENSE' } 26 | s.author = { 'lm2343635' => 'lm2343635@126.com' } 27 | s.source = { :git => 'https://github.com/RxSwiftCommunity/RxBinding.git', :tag => s.version.to_s } 28 | s.social_media_url = 'https://fczm.site' 29 | 30 | s.ios.deployment_target = '10.0' 31 | s.swift_version = '5.1' 32 | 33 | s.source_files = 'Sources/RxBinding/**/*' 34 | 35 | s.dependency 'RxSwift', '~> 6' 36 | s.dependency 'RxCocoa', '~> 6' 37 | end 38 | -------------------------------------------------------------------------------- /Sources/RxBinding/DisposedBy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisposedBy.swift 3 | // RxBinding 4 | // 5 | // Created by Meng Li on 03/30/2019. 6 | // Copyright (c) 2019 MuShare. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | import RxSwift 27 | 28 | precedencegroup DisposePrecedence { 29 | associativity: left 30 | 31 | lowerThan: DefaultPrecedence 32 | } 33 | 34 | infix operator ~ : DisposePrecedence 35 | 36 | extension DisposeBag { 37 | 38 | public static func ~ (disposable: Disposable, disposeBag: DisposeBag) { 39 | disposable.disposed(by: disposeBag) 40 | } 41 | 42 | @discardableResult 43 | public static func ~ (disposeBag: DisposeBag, disposable: Disposable) -> DisposeBag { 44 | disposable.disposed(by: disposeBag) 45 | return disposeBag 46 | } 47 | 48 | public static func ~ (disposeBag: DisposeBag, disposables: [Any]) { 49 | disposables.map { obj -> [Disposable] in 50 | switch obj.self { 51 | case is Disposable: 52 | return [obj as! Disposable] 53 | case is [Disposable]: 54 | return obj as! [Disposable] 55 | default: 56 | return [] 57 | } 58 | }.reduce([]) { $0 + $1 }.forEach { 59 | print($0) 60 | $0.disposed(by: disposeBag) 61 | } 62 | } 63 | 64 | public static func ~ (disposables: [Any], disposeBag: DisposeBag) { 65 | disposeBag ~ disposables 66 | } 67 | 68 | } 69 | 70 | extension Array where Element == Disposable { 71 | 72 | public static func ~ (disposables: Array, disposeBag: DisposeBag) { 73 | disposables.forEach { $0.disposed(by: disposeBag) } 74 | } 75 | 76 | public static func ~ (disposeBag: DisposeBag, disposables: Array) { 77 | disposables.forEach { $0.disposed(by: disposeBag) } 78 | } 79 | 80 | public static func ~ (disposables: Array, disposable: Disposable) -> [Disposable] { 81 | return disposables + [disposable] 82 | } 83 | 84 | public static func ~ (disposables1: Array, disposables2: Array) -> [Disposable] { 85 | return disposables1 + disposables2 86 | } 87 | 88 | } 89 | 90 | public func ~ (disposable1: Disposable, disposable2: Disposable) -> [Disposable] { 91 | return Array(arrayLiteral: disposable1, disposable2) 92 | } 93 | -------------------------------------------------------------------------------- /Sources/RxBinding/Drive.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Drive.swift 3 | // RxBinding 4 | // 5 | // Created by Meng Li on 04/18/2019. 6 | // Copyright (c) 2019 MuShare. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | import RxSwift 27 | import RxCocoa 28 | 29 | infix operator ~> : DefaultPrecedence 30 | 31 | // Drive the observer, the relay or the binder. 32 | extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { 33 | 34 | public static func ~> (observable: Self, observer: O) -> Disposable where O : ObserverType, Element == O.Element { 35 | return observable.drive(observer) 36 | } 37 | 38 | public static func ~> (observable: Self, observer: O) -> Disposable where O : ObserverType, O.Element == Element? { 39 | return observable.drive(observer) 40 | } 41 | 42 | public static func ~> (observable: Self, relay: BehaviorRelay) -> Disposable { 43 | return observable.drive(relay) 44 | } 45 | 46 | public static func ~> (observable: Self, relay: BehaviorRelay) -> Disposable { 47 | return observable.drive(relay) 48 | } 49 | 50 | public static func ~> (observable: Self, transformation: (Observable) -> R) -> R { 51 | return observable.drive(transformation) 52 | } 53 | 54 | } 55 | 56 | // Drive the array of observer, relay or binder. 57 | extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { 58 | 59 | public static func ~> (observable: Self, observers: [O]) -> [Disposable] where O : ObserverType, Element == O.Element { 60 | return observers.map { observable.drive($0) } 61 | } 62 | 63 | public static func ~> (observable: Self, observers: [O]) -> [Disposable] where O : ObserverType, O.Element == Element? { 64 | return observers.map { observable.drive($0) } 65 | } 66 | 67 | public static func ~> (observable: Self, relays: [BehaviorRelay]) -> [Disposable] { 68 | return relays.map { observable.drive($0) } 69 | } 70 | 71 | public static func ~> (observable: Self, relays: [BehaviorRelay]) -> [Disposable] { 72 | return relays.map { observable.drive($0) } 73 | } 74 | 75 | public static func ~> (observable: Self, transformations: [(Observable) -> R]) -> [R] { 76 | return transformations.map { observable.drive($0) } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /Sources/RxBinding/Emit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Emit.swift 3 | // RxBinding 4 | // 5 | // Created by Meng Li on 05/25/2020. 6 | // Copyright (c) 2020 MuShare. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | import RxSwift 27 | import RxCocoa 28 | 29 | infix operator ~> : DefaultPrecedence 30 | 31 | // Emit the observer, the relay or the binder. 32 | extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingStrategy { 33 | 34 | public static func ~> (observable: Self, observer: O) -> Disposable where O : ObserverType, Self.Element == O.Element { 35 | return observable.emit(to: observer) 36 | } 37 | 38 | public static func ~> (observable: Self, observer: O) -> Disposable where O : ObserverType, O.Element == Self.Element? { 39 | return observable.emit(to: observer) 40 | } 41 | 42 | public static func ~> (observable: Self, relay: PublishRelay) -> Disposable { 43 | return observable.emit(to: relay) 44 | } 45 | 46 | public static func ~> (observable: Self, relay: PublishRelay) -> Disposable { 47 | return observable.emit(to: relay) 48 | } 49 | 50 | public static func ~> (observable: Self, relay: BehaviorRelay) -> Disposable { 51 | return observable.emit(to: relay) 52 | } 53 | 54 | public static func ~> (observable: Self, relay: BehaviorRelay) -> Disposable { 55 | return observable.emit(to: relay) 56 | } 57 | 58 | } 59 | 60 | // Emit the array of observer, relay or binder. 61 | extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingStrategy { 62 | 63 | public static func ~> (observable: Self, observers: [O]) -> [Disposable] where O : ObserverType, Self.Element == O.Element { 64 | return observers.map { observable.emit(to: $0) } 65 | } 66 | 67 | public static func ~> (observable: Self, observers: [O]) -> [Disposable] where O : ObserverType, O.Element == Self.Element? { 68 | return observers.map { observable.emit(to: $0) } 69 | } 70 | 71 | public static func ~> (observable: Self, relays: [PublishRelay]) -> [Disposable] { 72 | return relays.map { observable.emit(to: $0) } 73 | } 74 | 75 | public static func ~> (observable: Self, relays: [PublishRelay]) -> [Disposable] { 76 | return relays.map { observable.emit(to: $0) } 77 | } 78 | 79 | public static func ~> (observable: Self, relays: [BehaviorRelay]) -> [Disposable] { 80 | return relays.map { observable.emit(to: $0) } 81 | } 82 | 83 | public static func ~> (observable: Self, relays: [BehaviorRelay]) -> [Disposable] { 84 | return relays.map { observable.emit(to: $0) } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Sources/RxBinding/OneWayBind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OneWayBind.swift 3 | // RxBinding 4 | // 5 | // Created by Meng Li on 03/30/2019. 6 | // Copyright (c) 2019 MuShare. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | import RxSwift 27 | import RxCocoa 28 | 29 | infix operator ~> : DefaultPrecedence 30 | 31 | // Bind to the observer, the relay or the binder. 32 | extension ObservableType { 33 | 34 | public static func ~> (observable: Self, observer: O) -> Disposable where O: ObserverType, O.Element == Element { 35 | return observable.bind(to: observer) 36 | } 37 | 38 | public static func ~> (observable: Self, observer: O) -> Disposable where O : ObserverType, O.Element == Element? { 39 | return observable.bind(to: observer) 40 | } 41 | 42 | public static func ~> (observable: Self, relay: PublishRelay) -> Disposable { 43 | return observable.bind(to: relay) 44 | } 45 | 46 | public static func ~> (observable: Self, relay: PublishRelay) -> Disposable { 47 | return observable.bind(to: relay) 48 | } 49 | 50 | public static func ~> (observable: Self, relay: BehaviorRelay) -> Disposable { 51 | return observable.bind(to: relay) 52 | } 53 | 54 | public static func ~> (observable: Self, relay: BehaviorRelay) -> Disposable { 55 | return observable.bind(to: relay) 56 | } 57 | 58 | public static func ~> (observable: Self, binder: (Self) -> R) -> R { 59 | return observable.bind(to: binder) 60 | } 61 | 62 | public static func ~> (observable: Self, binder: (Self) -> Disposable) -> Disposable { 63 | return observable.bind(to: binder) 64 | } 65 | 66 | } 67 | 68 | // Bind to the array of observer, relay or binder. 69 | extension ObservableType { 70 | 71 | public static func ~> (observable: Self, observers: [O]) -> [Disposable] where O: ObserverType, O.Element == Element { 72 | return observers.map { observable.bind(to: $0) } 73 | } 74 | 75 | public static func ~> (observable: Self, observers: [O]) -> [Disposable] where O : ObserverType, O.Element == Element? { 76 | return observers.map { observable.bind(to: $0) } 77 | } 78 | 79 | public static func ~> (observable: Self, relays: [PublishRelay]) -> [Disposable] { 80 | return relays.map { observable.bind(to: $0) } 81 | } 82 | 83 | public static func ~> (observable: Self, relays: [PublishRelay]) -> [Disposable] { 84 | return relays.map { observable.bind(to: $0) } 85 | } 86 | 87 | public static func ~> (observable: Self, relays: [BehaviorRelay]) -> [Disposable] { 88 | return relays.map { observable.bind(to: $0) } 89 | } 90 | 91 | public static func ~> (observable: Self, relays: [BehaviorRelay]) -> [Disposable] { 92 | return relays.map { observable.bind(to: $0) } 93 | } 94 | 95 | public static func ~> (observable: Self, binders: [(Self) -> R]) -> [R] { 96 | return binders.map { observable.bind(to: $0) } 97 | } 98 | 99 | } 100 | 101 | 102 | -------------------------------------------------------------------------------- /Sources/RxBinding/TwoWayBind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TwoWayBind.swift 3 | // RxBinding 4 | // 5 | // Created by Meng Li on 03/31/2019. 6 | // Copyright (c) 2019 MuShare. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | import UIKit 27 | import RxSwift 28 | import RxCocoa 29 | 30 | infix operator <~> : DefaultPrecedence 31 | 32 | public func <~> (relay: BehaviorRelay, property: ControlProperty) -> Disposable { 33 | return relay.twoWayBind(to: property) 34 | } 35 | 36 | extension BehaviorRelay where Element == String { 37 | 38 | func twoWaybind(to textInput: TextInput) -> Disposable { 39 | let bindToUIDisposable = self.bind(to: textInput.text) 40 | 41 | let bindToRelay = textInput.text.subscribe(onNext: { [weak base = textInput.base] n in 42 | guard let base = base else { 43 | return 44 | } 45 | 46 | let nonMarkedTextValue = self.nonMarkedText(base) 47 | 48 | /** 49 | In some cases `textInput.textRangeFromPosition(start, toPosition: end)` will return nil even though the underlying 50 | value is not nil. This appears to be an Apple bug. If it's not, and we are doing something wrong, please let us know. 51 | The can be reproed easily if replace bottom code with 52 | 53 | if nonMarkedTextValue != relay.value { 54 | relay.accept(nonMarkedTextValue ?? "") 55 | } 56 | and you hit "Done" button on keyboard. 57 | */ 58 | if let nonMarkedTextValue = nonMarkedTextValue, nonMarkedTextValue != self.value { 59 | self.accept(nonMarkedTextValue) 60 | } 61 | }, onCompleted: { 62 | bindToUIDisposable.dispose() 63 | }) 64 | 65 | return Disposables.create(bindToUIDisposable, bindToRelay) 66 | } 67 | 68 | private func nonMarkedText(_ textInput: UITextInput) -> String? { 69 | let start = textInput.beginningOfDocument 70 | let end = textInput.endOfDocument 71 | 72 | guard let rangeAll = textInput.textRange(from: start, to: end), 73 | let text = textInput.text(in: rangeAll) else { 74 | return nil 75 | } 76 | 77 | guard let markedTextRange = textInput.markedTextRange else { 78 | return text 79 | } 80 | 81 | guard let startRange = textInput.textRange(from: start, to: markedTextRange.start), 82 | let endRange = textInput.textRange(from: markedTextRange.end, to: end) else { 83 | return text 84 | } 85 | 86 | return (textInput.text(in: startRange) ?? "") + (textInput.text(in: endRange) ?? "") 87 | } 88 | 89 | } 90 | 91 | extension BehaviorRelay { 92 | 93 | func twoWayBind(to property: ControlProperty) -> Disposable { 94 | if Element.self == String.self { 95 | #if DEBUG 96 | fatalError("It is ok to delete this message, but this is here to warn that you are maybe trying to bind to some `rx.text` property directly to relay.\n" + 97 | "That will usually work ok, but for some languages that use IME, that simplistic method could cause unexpected issues because it will return intermediate results while text is being inputed.\n" + 98 | "REMEDY: Just use `textField <~> relay` instead of `textField.rx.text <~> relay`.\n" 99 | ) 100 | #endif 101 | } 102 | 103 | let bindToUIDisposable = self.bind(to: property) 104 | let bindToRelay = property.subscribe(onNext: { n in 105 | self.accept(n) 106 | }, onCompleted: { 107 | bindToUIDisposable.dispose() 108 | }) 109 | return Disposables.create(bindToUIDisposable, bindToRelay) 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import RxBindingTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += RxBindingTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /Tests/RxBindingTests/RxBindingTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import RxBinding 3 | 4 | final class RxBindingTests: XCTestCase { 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | XCTAssert(true, "Pass") 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | self.measure() { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Tests/RxBindingTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(RxBindingTests.allTests), 7 | ] 8 | } 9 | #endif 10 | --------------------------------------------------------------------------------