├── .github └── workflows │ └── build_check.yml ├── .gitignore ├── Example ├── .gitignore ├── Podfile ├── Podfile.lock ├── RxAlertViewable.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── RxAlertViewable-Example.xcscheme ├── RxAlertViewable │ ├── AppDelegate.swift │ ├── CustomAlertController.swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── LaunchScreen.storyboard │ ├── ViewController.swift │ └── ViewModel.swift └── Tests │ ├── Info.plist │ └── Tests.swift ├── LICENSE ├── Package.swift ├── README.md ├── RxAlertViewable.podspec ├── RxAlertViewable ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── ObserverType+RxAlert.swift │ ├── RxAction.swift │ ├── RxActionSheet.swift │ ├── RxAlert.swift │ ├── RxAlertConfig.swift │ ├── RxAlertController.swift │ ├── RxAlertViewable+Rx.swift │ └── RxAlertViewable.swift ├── _Pods.xcodeproj └── screenshots └── demo.jpg /.github/workflows/build_check.yml: -------------------------------------------------------------------------------- 1 | name: build_check 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macOS-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Pod install 17 | run: pod install --project-directory=Example --repo-update 18 | - name: Run test 19 | run: set -o pipefail && xcodebuild -workspace Example/RxAlertViewable.xcworkspace -scheme RxAlertViewable -sdk iphonesimulator build CODE_SIGNING_REQUIRED=NO | xcpretty -c 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | .idea/ 4 | 5 | # Xcode 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata/ 16 | *.xccheckout 17 | profile 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | 23 | # Bundler 24 | .bundle 25 | 26 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 27 | # Carthage/Checkouts 28 | 29 | Carthage/Build 30 | 31 | # We recommend against adding the Pods directory to your .gitignore. However 32 | # you should judge for yourself, the pros and cons are mentioned at: 33 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 34 | # 35 | # Note: if you ignore the Pods directory, make sure to uncomment 36 | # `pod install` in .travis.yml 37 | # 38 | -------------------------------------------------------------------------------- /Example/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | xcuserdata/ 3 | *.moved-aside 4 | *.xcuserstate 5 | 6 | # Pod 7 | *.xcworkspace/ 8 | Pods/ 9 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | platform :ios, '10.0' 3 | 4 | target 'RxAlertViewable_Example' do 5 | pod 'RxAlertViewable', :path => '../' 6 | pod 'RxCocoa' 7 | pod 'RxSwift' 8 | pod 'SnapKit' 9 | pod 'Kingfisher' 10 | 11 | target 'RxAlertViewable_Tests' do 12 | inherit! :search_paths 13 | pod 'RxAlertViewable', :path => '../' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (6.3.1) 3 | - RxAlertViewable (1.2): 4 | - RxCocoa (~> 6) 5 | - RxSwift (~> 6) 6 | - RxCocoa (6.2.0): 7 | - RxRelay (= 6.2.0) 8 | - RxSwift (= 6.2.0) 9 | - RxRelay (6.2.0): 10 | - RxSwift (= 6.2.0) 11 | - RxSwift (6.2.0) 12 | - SnapKit (5.0.1) 13 | 14 | DEPENDENCIES: 15 | - Kingfisher 16 | - RxAlertViewable (from `../`) 17 | - RxCocoa 18 | - RxSwift 19 | - SnapKit 20 | 21 | SPEC REPOS: 22 | trunk: 23 | - Kingfisher 24 | - RxCocoa 25 | - RxRelay 26 | - RxSwift 27 | - SnapKit 28 | 29 | EXTERNAL SOURCES: 30 | RxAlertViewable: 31 | :path: "../" 32 | 33 | SPEC CHECKSUMS: 34 | Kingfisher: 016c8b653a35add51dd34a3aba36b580041acc74 35 | RxAlertViewable: 8b8b82e6c22b974813e67a79b0c3d500bb08b676 36 | RxCocoa: 4baf94bb35f2c0ab31bc0cb9f1900155f646ba42 37 | RxRelay: e72dbfd157807478401ef1982e1c61c945c94b2f 38 | RxSwift: d356ab7bee873611322f134c5f9ef379fa183d8f 39 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb 40 | 41 | PODFILE CHECKSUM: b4605b4abcc2e96e810853dd0c68eb98e91ba151 42 | 43 | COCOAPODS: 1.11.2 44 | -------------------------------------------------------------------------------- /Example/RxAlertViewable.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 288734B8D8F6F6E14F46A11A /* Pods_RxAlertViewable_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7F8693FEB83F3E3254F979A /* Pods_RxAlertViewable_Example.framework */; }; 11 | 49DD9DCE22D591E10028B5A6 /* CustomAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49DD9DCD22D591E10028B5A6 /* CustomAlertController.swift */; }; 12 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 13 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 14 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 15 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 16 | 7753CA732294EBFD00EB110E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7753CA722294EBFD00EB110E /* LaunchScreen.storyboard */; }; 17 | 7753CA752294EEC200EB110E /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7753CA742294EEC200EB110E /* ViewModel.swift */; }; 18 | 8F79715B705D8BFEBFDA94E3 /* Pods_RxAlertViewable_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C6D34ECC2A4AC89986AF0D4 /* Pods_RxAlertViewable_Tests.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 27 | remoteInfo = RxAlertViewable; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 0907F501190023657AFCFB9D /* Pods-RxAlertViewable_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxAlertViewable_Tests.debug.xcconfig"; path = "Target Support Files/Pods-RxAlertViewable_Tests/Pods-RxAlertViewable_Tests.debug.xcconfig"; sourceTree = ""; }; 33 | 1C6D34ECC2A4AC89986AF0D4 /* Pods_RxAlertViewable_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxAlertViewable_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 4009373706FD359F3DBAE4CD /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 35 | 40B1B30229812090F226D1A5 /* Pods-RxAlertViewable_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxAlertViewable_Example.release.xcconfig"; path = "Target Support Files/Pods-RxAlertViewable_Example/Pods-RxAlertViewable_Example.release.xcconfig"; sourceTree = ""; }; 36 | 49DD9DCD22D591E10028B5A6 /* CustomAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAlertController.swift; sourceTree = ""; }; 37 | 53148EEB551BF869796EF32A /* Pods-RxAlertViewable_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxAlertViewable_Tests.release.xcconfig"; path = "Target Support Files/Pods-RxAlertViewable_Tests/Pods-RxAlertViewable_Tests.release.xcconfig"; sourceTree = ""; }; 38 | 607FACD01AFB9204008FA782 /* RxAlertViewable_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxAlertViewable_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 41 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 42 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 43 | 607FACE51AFB9204008FA782 /* RxAlertViewable_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxAlertViewable_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 46 | 7753CA722294EBFD00EB110E /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 47 | 7753CA742294EEC200EB110E /* ViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 48 | 862064704A9C35D6208E2B84 /* RxAlertViewable.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = RxAlertViewable.podspec; path = ../RxAlertViewable.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 49 | A221529308C0BD16BE043806 /* Pods-RxAlertViewable_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxAlertViewable_Example.debug.xcconfig"; path = "Target Support Files/Pods-RxAlertViewable_Example/Pods-RxAlertViewable_Example.debug.xcconfig"; sourceTree = ""; }; 50 | D7F8693FEB83F3E3254F979A /* Pods_RxAlertViewable_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxAlertViewable_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E6D9A987A7E434D1F2083FA6 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | 288734B8D8F6F6E14F46A11A /* Pods_RxAlertViewable_Example.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 8F79715B705D8BFEBFDA94E3 /* Pods_RxAlertViewable_Tests.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 0ACE582E310CE065E2C0F074 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | D7F8693FEB83F3E3254F979A /* Pods_RxAlertViewable_Example.framework */, 78 | 1C6D34ECC2A4AC89986AF0D4 /* Pods_RxAlertViewable_Tests.framework */, 79 | ); 80 | name = Frameworks; 81 | sourceTree = ""; 82 | }; 83 | 139778870040A324A7660D82 /* Pods */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | A221529308C0BD16BE043806 /* Pods-RxAlertViewable_Example.debug.xcconfig */, 87 | 40B1B30229812090F226D1A5 /* Pods-RxAlertViewable_Example.release.xcconfig */, 88 | 0907F501190023657AFCFB9D /* Pods-RxAlertViewable_Tests.debug.xcconfig */, 89 | 53148EEB551BF869796EF32A /* Pods-RxAlertViewable_Tests.release.xcconfig */, 90 | ); 91 | path = Pods; 92 | sourceTree = ""; 93 | }; 94 | 607FACC71AFB9204008FA782 = { 95 | isa = PBXGroup; 96 | children = ( 97 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 98 | 607FACD21AFB9204008FA782 /* Example for RxAlertViewable */, 99 | 607FACE81AFB9204008FA782 /* Tests */, 100 | 607FACD11AFB9204008FA782 /* Products */, 101 | 139778870040A324A7660D82 /* Pods */, 102 | 0ACE582E310CE065E2C0F074 /* Frameworks */, 103 | ); 104 | sourceTree = ""; 105 | }; 106 | 607FACD11AFB9204008FA782 /* Products */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 607FACD01AFB9204008FA782 /* RxAlertViewable_Example.app */, 110 | 607FACE51AFB9204008FA782 /* RxAlertViewable_Tests.xctest */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | 607FACD21AFB9204008FA782 /* Example for RxAlertViewable */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 119 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 120 | 7753CA742294EEC200EB110E /* ViewModel.swift */, 121 | 49DD9DCD22D591E10028B5A6 /* CustomAlertController.swift */, 122 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 123 | 7753CA722294EBFD00EB110E /* LaunchScreen.storyboard */, 124 | 607FACD31AFB9204008FA782 /* Supporting Files */, 125 | ); 126 | name = "Example for RxAlertViewable"; 127 | path = RxAlertViewable; 128 | sourceTree = ""; 129 | }; 130 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 607FACD41AFB9204008FA782 /* Info.plist */, 134 | ); 135 | name = "Supporting Files"; 136 | sourceTree = ""; 137 | }; 138 | 607FACE81AFB9204008FA782 /* Tests */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 142 | 607FACE91AFB9204008FA782 /* Supporting Files */, 143 | ); 144 | path = Tests; 145 | sourceTree = ""; 146 | }; 147 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 607FACEA1AFB9204008FA782 /* Info.plist */, 151 | ); 152 | name = "Supporting Files"; 153 | sourceTree = ""; 154 | }; 155 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 862064704A9C35D6208E2B84 /* RxAlertViewable.podspec */, 159 | E6D9A987A7E434D1F2083FA6 /* README.md */, 160 | 4009373706FD359F3DBAE4CD /* LICENSE */, 161 | ); 162 | name = "Podspec Metadata"; 163 | sourceTree = ""; 164 | }; 165 | /* End PBXGroup section */ 166 | 167 | /* Begin PBXNativeTarget section */ 168 | 607FACCF1AFB9204008FA782 /* RxAlertViewable_Example */ = { 169 | isa = PBXNativeTarget; 170 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "RxAlertViewable_Example" */; 171 | buildPhases = ( 172 | F774761A10ADE559EBB72800 /* [CP] Check Pods Manifest.lock */, 173 | 607FACCC1AFB9204008FA782 /* Sources */, 174 | 607FACCD1AFB9204008FA782 /* Frameworks */, 175 | 607FACCE1AFB9204008FA782 /* Resources */, 176 | F64CA1E2C83D6783277AA0CF /* [CP] Embed Pods Frameworks */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | ); 182 | name = RxAlertViewable_Example; 183 | productName = RxAlertViewable; 184 | productReference = 607FACD01AFB9204008FA782 /* RxAlertViewable_Example.app */; 185 | productType = "com.apple.product-type.application"; 186 | }; 187 | 607FACE41AFB9204008FA782 /* RxAlertViewable_Tests */ = { 188 | isa = PBXNativeTarget; 189 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "RxAlertViewable_Tests" */; 190 | buildPhases = ( 191 | 4C5EF6C6D6A6571A222A281E /* [CP] Check Pods Manifest.lock */, 192 | 607FACE11AFB9204008FA782 /* Sources */, 193 | 607FACE21AFB9204008FA782 /* Frameworks */, 194 | 607FACE31AFB9204008FA782 /* Resources */, 195 | 7753CAAB22968B1400EB110E /* Rebuild Pods */, 196 | ED742D7C8C88902A1367AEB1 /* [CP] Embed Pods Frameworks */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 202 | ); 203 | name = RxAlertViewable_Tests; 204 | productName = Tests; 205 | productReference = 607FACE51AFB9204008FA782 /* RxAlertViewable_Tests.xctest */; 206 | productType = "com.apple.product-type.bundle.unit-test"; 207 | }; 208 | /* End PBXNativeTarget section */ 209 | 210 | /* Begin PBXProject section */ 211 | 607FACC81AFB9204008FA782 /* Project object */ = { 212 | isa = PBXProject; 213 | attributes = { 214 | LastSwiftUpdateCheck = 0830; 215 | LastUpgradeCheck = 1230; 216 | ORGANIZATIONNAME = CocoaPods; 217 | TargetAttributes = { 218 | 607FACCF1AFB9204008FA782 = { 219 | CreatedOnToolsVersion = 6.3.1; 220 | LastSwiftMigration = 0900; 221 | }; 222 | 607FACE41AFB9204008FA782 = { 223 | CreatedOnToolsVersion = 6.3.1; 224 | LastSwiftMigration = 0900; 225 | TestTargetID = 607FACCF1AFB9204008FA782; 226 | }; 227 | }; 228 | }; 229 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "RxAlertViewable" */; 230 | compatibilityVersion = "Xcode 3.2"; 231 | developmentRegion = en; 232 | hasScannedForEncodings = 0; 233 | knownRegions = ( 234 | en, 235 | Base, 236 | ); 237 | mainGroup = 607FACC71AFB9204008FA782; 238 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 239 | projectDirPath = ""; 240 | projectRoot = ""; 241 | targets = ( 242 | 607FACCF1AFB9204008FA782 /* RxAlertViewable_Example */, 243 | 607FACE41AFB9204008FA782 /* RxAlertViewable_Tests */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXResourcesBuildPhase section */ 249 | 607FACCE1AFB9204008FA782 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 254 | 7753CA732294EBFD00EB110E /* LaunchScreen.storyboard in Resources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | 607FACE31AFB9204008FA782 /* Resources */ = { 259 | isa = PBXResourcesBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXResourcesBuildPhase section */ 266 | 267 | /* Begin PBXShellScriptBuildPhase section */ 268 | 4C5EF6C6D6A6571A222A281E /* [CP] Check Pods Manifest.lock */ = { 269 | isa = PBXShellScriptBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | ); 273 | inputFileListPaths = ( 274 | ); 275 | inputPaths = ( 276 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 277 | "${PODS_ROOT}/Manifest.lock", 278 | ); 279 | name = "[CP] Check Pods Manifest.lock"; 280 | outputFileListPaths = ( 281 | ); 282 | outputPaths = ( 283 | "$(DERIVED_FILE_DIR)/Pods-RxAlertViewable_Tests-checkManifestLockResult.txt", 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | shellPath = /bin/sh; 287 | 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"; 288 | showEnvVarsInLog = 0; 289 | }; 290 | 7753CAAB22968B1400EB110E /* Rebuild Pods */ = { 291 | isa = PBXShellScriptBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | ); 295 | inputFileListPaths = ( 296 | ); 297 | inputPaths = ( 298 | ); 299 | name = "Rebuild Pods"; 300 | outputFileListPaths = ( 301 | ); 302 | outputPaths = ( 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | shellPath = /bin/sh; 306 | shellScript = "find \"${SRCROOT}/Pods\" -type f -name *frameworks.sh -exec bash -c \"touch \\\"{}\\\"\" \\;\n"; 307 | }; 308 | ED742D7C8C88902A1367AEB1 /* [CP] Embed Pods Frameworks */ = { 309 | isa = PBXShellScriptBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | ); 313 | inputPaths = ( 314 | "${PODS_ROOT}/Target Support Files/Pods-RxAlertViewable_Tests/Pods-RxAlertViewable_Tests-frameworks.sh", 315 | "${BUILT_PRODUCTS_DIR}/RxAlertViewable/RxAlertViewable.framework", 316 | "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework", 317 | "${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework", 318 | "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", 319 | ); 320 | name = "[CP] Embed Pods Frameworks"; 321 | outputPaths = ( 322 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxAlertViewable.framework", 323 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework", 324 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework", 325 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | shellPath = /bin/sh; 329 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RxAlertViewable_Tests/Pods-RxAlertViewable_Tests-frameworks.sh\"\n"; 330 | showEnvVarsInLog = 0; 331 | }; 332 | F64CA1E2C83D6783277AA0CF /* [CP] Embed Pods Frameworks */ = { 333 | isa = PBXShellScriptBuildPhase; 334 | buildActionMask = 2147483647; 335 | files = ( 336 | ); 337 | inputPaths = ( 338 | "${PODS_ROOT}/Target Support Files/Pods-RxAlertViewable_Example/Pods-RxAlertViewable_Example-frameworks.sh", 339 | "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework", 340 | "${BUILT_PRODUCTS_DIR}/RxAlertViewable/RxAlertViewable.framework", 341 | "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework", 342 | "${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework", 343 | "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", 344 | "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework", 345 | ); 346 | name = "[CP] Embed Pods Frameworks"; 347 | outputPaths = ( 348 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework", 349 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxAlertViewable.framework", 350 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework", 351 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework", 352 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", 353 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework", 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | shellPath = /bin/sh; 357 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RxAlertViewable_Example/Pods-RxAlertViewable_Example-frameworks.sh\"\n"; 358 | showEnvVarsInLog = 0; 359 | }; 360 | F774761A10ADE559EBB72800 /* [CP] Check Pods Manifest.lock */ = { 361 | isa = PBXShellScriptBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | ); 365 | inputFileListPaths = ( 366 | ); 367 | inputPaths = ( 368 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 369 | "${PODS_ROOT}/Manifest.lock", 370 | ); 371 | name = "[CP] Check Pods Manifest.lock"; 372 | outputFileListPaths = ( 373 | ); 374 | outputPaths = ( 375 | "$(DERIVED_FILE_DIR)/Pods-RxAlertViewable_Example-checkManifestLockResult.txt", 376 | ); 377 | runOnlyForDeploymentPostprocessing = 0; 378 | shellPath = /bin/sh; 379 | 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"; 380 | showEnvVarsInLog = 0; 381 | }; 382 | /* End PBXShellScriptBuildPhase section */ 383 | 384 | /* Begin PBXSourcesBuildPhase section */ 385 | 607FACCC1AFB9204008FA782 /* Sources */ = { 386 | isa = PBXSourcesBuildPhase; 387 | buildActionMask = 2147483647; 388 | files = ( 389 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 390 | 7753CA752294EEC200EB110E /* ViewModel.swift in Sources */, 391 | 49DD9DCE22D591E10028B5A6 /* CustomAlertController.swift in Sources */, 392 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 393 | ); 394 | runOnlyForDeploymentPostprocessing = 0; 395 | }; 396 | 607FACE11AFB9204008FA782 /* Sources */ = { 397 | isa = PBXSourcesBuildPhase; 398 | buildActionMask = 2147483647; 399 | files = ( 400 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 401 | ); 402 | runOnlyForDeploymentPostprocessing = 0; 403 | }; 404 | /* End PBXSourcesBuildPhase section */ 405 | 406 | /* Begin PBXTargetDependency section */ 407 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 408 | isa = PBXTargetDependency; 409 | target = 607FACCF1AFB9204008FA782 /* RxAlertViewable_Example */; 410 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 411 | }; 412 | /* End PBXTargetDependency section */ 413 | 414 | /* Begin XCBuildConfiguration section */ 415 | 607FACED1AFB9204008FA782 /* Debug */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ALWAYS_SEARCH_USER_PATHS = NO; 419 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 420 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 421 | CLANG_CXX_LIBRARY = "libc++"; 422 | CLANG_ENABLE_MODULES = YES; 423 | CLANG_ENABLE_OBJC_ARC = YES; 424 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 425 | CLANG_WARN_BOOL_CONVERSION = YES; 426 | CLANG_WARN_COMMA = YES; 427 | CLANG_WARN_CONSTANT_CONVERSION = YES; 428 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 429 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 430 | CLANG_WARN_EMPTY_BODY = YES; 431 | CLANG_WARN_ENUM_CONVERSION = YES; 432 | CLANG_WARN_INFINITE_RECURSION = YES; 433 | CLANG_WARN_INT_CONVERSION = YES; 434 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 435 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 436 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 437 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 438 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 439 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 440 | CLANG_WARN_STRICT_PROTOTYPES = YES; 441 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 442 | CLANG_WARN_UNREACHABLE_CODE = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 445 | COPY_PHASE_STRIP = NO; 446 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 447 | ENABLE_STRICT_OBJC_MSGSEND = YES; 448 | ENABLE_TESTABILITY = YES; 449 | GCC_C_LANGUAGE_STANDARD = gnu99; 450 | GCC_DYNAMIC_NO_PIC = NO; 451 | GCC_NO_COMMON_BLOCKS = YES; 452 | GCC_OPTIMIZATION_LEVEL = 0; 453 | GCC_PREPROCESSOR_DEFINITIONS = ( 454 | "DEBUG=1", 455 | "$(inherited)", 456 | ); 457 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 460 | GCC_WARN_UNDECLARED_SELECTOR = YES; 461 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 462 | GCC_WARN_UNUSED_FUNCTION = YES; 463 | GCC_WARN_UNUSED_VARIABLE = YES; 464 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 465 | MTL_ENABLE_DEBUG_INFO = YES; 466 | ONLY_ACTIVE_ARCH = YES; 467 | SDKROOT = iphoneos; 468 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 469 | SWIFT_VERSION = 5.0; 470 | }; 471 | name = Debug; 472 | }; 473 | 607FACEE1AFB9204008FA782 /* Release */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | ALWAYS_SEARCH_USER_PATHS = NO; 477 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 479 | CLANG_CXX_LIBRARY = "libc++"; 480 | CLANG_ENABLE_MODULES = YES; 481 | CLANG_ENABLE_OBJC_ARC = YES; 482 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 483 | CLANG_WARN_BOOL_CONVERSION = YES; 484 | CLANG_WARN_COMMA = YES; 485 | CLANG_WARN_CONSTANT_CONVERSION = YES; 486 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 487 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 488 | CLANG_WARN_EMPTY_BODY = YES; 489 | CLANG_WARN_ENUM_CONVERSION = YES; 490 | CLANG_WARN_INFINITE_RECURSION = YES; 491 | CLANG_WARN_INT_CONVERSION = YES; 492 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 493 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 494 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 495 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 496 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 497 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 498 | CLANG_WARN_STRICT_PROTOTYPES = YES; 499 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 500 | CLANG_WARN_UNREACHABLE_CODE = YES; 501 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 502 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 503 | COPY_PHASE_STRIP = NO; 504 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 505 | ENABLE_NS_ASSERTIONS = NO; 506 | ENABLE_STRICT_OBJC_MSGSEND = YES; 507 | GCC_C_LANGUAGE_STANDARD = gnu99; 508 | GCC_NO_COMMON_BLOCKS = YES; 509 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 510 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 511 | GCC_WARN_UNDECLARED_SELECTOR = YES; 512 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 513 | GCC_WARN_UNUSED_FUNCTION = YES; 514 | GCC_WARN_UNUSED_VARIABLE = YES; 515 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 516 | MTL_ENABLE_DEBUG_INFO = NO; 517 | SDKROOT = iphoneos; 518 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 519 | SWIFT_VERSION = 5.0; 520 | VALIDATE_PRODUCT = YES; 521 | }; 522 | name = Release; 523 | }; 524 | 607FACF01AFB9204008FA782 /* Debug */ = { 525 | isa = XCBuildConfiguration; 526 | baseConfigurationReference = A221529308C0BD16BE043806 /* Pods-RxAlertViewable_Example.debug.xcconfig */; 527 | buildSettings = { 528 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 529 | INFOPLIST_FILE = RxAlertViewable/Info.plist; 530 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 531 | MODULE_NAME = ExampleApp; 532 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 535 | SWIFT_VERSION = 5.0; 536 | TARGETED_DEVICE_FAMILY = "1,2"; 537 | }; 538 | name = Debug; 539 | }; 540 | 607FACF11AFB9204008FA782 /* Release */ = { 541 | isa = XCBuildConfiguration; 542 | baseConfigurationReference = 40B1B30229812090F226D1A5 /* Pods-RxAlertViewable_Example.release.xcconfig */; 543 | buildSettings = { 544 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 545 | INFOPLIST_FILE = RxAlertViewable/Info.plist; 546 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 547 | MODULE_NAME = ExampleApp; 548 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 549 | PRODUCT_NAME = "$(TARGET_NAME)"; 550 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 551 | SWIFT_VERSION = 5.0; 552 | TARGETED_DEVICE_FAMILY = "1,2"; 553 | }; 554 | name = Release; 555 | }; 556 | 607FACF31AFB9204008FA782 /* Debug */ = { 557 | isa = XCBuildConfiguration; 558 | baseConfigurationReference = 0907F501190023657AFCFB9D /* Pods-RxAlertViewable_Tests.debug.xcconfig */; 559 | buildSettings = { 560 | FRAMEWORK_SEARCH_PATHS = ( 561 | "$(SDKROOT)/Developer/Library/Frameworks", 562 | "$(inherited)", 563 | ); 564 | GCC_PREPROCESSOR_DEFINITIONS = ( 565 | "DEBUG=1", 566 | "$(inherited)", 567 | ); 568 | INFOPLIST_FILE = Tests/Info.plist; 569 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 570 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 571 | PRODUCT_NAME = "$(TARGET_NAME)"; 572 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 573 | SWIFT_VERSION = 5.0; 574 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RxAlertViewable_Example.app/RxAlertViewable_Example"; 575 | }; 576 | name = Debug; 577 | }; 578 | 607FACF41AFB9204008FA782 /* Release */ = { 579 | isa = XCBuildConfiguration; 580 | baseConfigurationReference = 53148EEB551BF869796EF32A /* Pods-RxAlertViewable_Tests.release.xcconfig */; 581 | buildSettings = { 582 | FRAMEWORK_SEARCH_PATHS = ( 583 | "$(SDKROOT)/Developer/Library/Frameworks", 584 | "$(inherited)", 585 | ); 586 | INFOPLIST_FILE = Tests/Info.plist; 587 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 588 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 589 | PRODUCT_NAME = "$(TARGET_NAME)"; 590 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 591 | SWIFT_VERSION = 5.0; 592 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RxAlertViewable_Example.app/RxAlertViewable_Example"; 593 | }; 594 | name = Release; 595 | }; 596 | /* End XCBuildConfiguration section */ 597 | 598 | /* Begin XCConfigurationList section */ 599 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "RxAlertViewable" */ = { 600 | isa = XCConfigurationList; 601 | buildConfigurations = ( 602 | 607FACED1AFB9204008FA782 /* Debug */, 603 | 607FACEE1AFB9204008FA782 /* Release */, 604 | ); 605 | defaultConfigurationIsVisible = 0; 606 | defaultConfigurationName = Release; 607 | }; 608 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "RxAlertViewable_Example" */ = { 609 | isa = XCConfigurationList; 610 | buildConfigurations = ( 611 | 607FACF01AFB9204008FA782 /* Debug */, 612 | 607FACF11AFB9204008FA782 /* Release */, 613 | ); 614 | defaultConfigurationIsVisible = 0; 615 | defaultConfigurationName = Release; 616 | }; 617 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "RxAlertViewable_Tests" */ = { 618 | isa = XCConfigurationList; 619 | buildConfigurations = ( 620 | 607FACF31AFB9204008FA782 /* Debug */, 621 | 607FACF41AFB9204008FA782 /* Release */, 622 | ); 623 | defaultConfigurationIsVisible = 0; 624 | defaultConfigurationName = Release; 625 | }; 626 | /* End XCConfigurationList section */ 627 | }; 628 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 629 | } 630 | -------------------------------------------------------------------------------- /Example/RxAlertViewable.xcodeproj/xcshareddata/xcschemes/RxAlertViewable-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/RxAlertViewable/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2018/12/01. 6 | // Copyright © 2018 MuShare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxAlertViewable 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | RxAlertConfig.current = RxAlertConfig( 19 | tip: "My Tip", 20 | confirm: "My Confirm", 21 | warning: "My Warning", 22 | error: "My Error", 23 | yes: "My Yes", 24 | no: "My No", 25 | ok: "My OK", 26 | cancel: "My Cancel", 27 | tintColor: .blue 28 | ) 29 | 30 | let viewModel = ViewModel() 31 | let viewController = ViewController(viewModel: viewModel) 32 | window = UIWindow(frame: UIScreen.main.bounds) 33 | window?.rootViewController = viewController 34 | window?.makeKeyAndVisible() 35 | 36 | return true 37 | } 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Example/RxAlertViewable/CustomAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomAlertController.swift 3 | // RxAlertViewable_Example 4 | // 5 | // Created by takuji.terada on 2019/07/10. 6 | // Copyright © 2019 XFLAG. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxAlertViewable 11 | import SnapKit 12 | import Kingfisher 13 | 14 | struct CustomAlertItem: RxAlertItem { 15 | static var controllerType: RxAlertController.Type = CustomAlertController.self 16 | 17 | var name: String 18 | var avatar: URL? 19 | } 20 | 21 | class CustomAlertController: UIViewController { 22 | 23 | private lazy var titleLabel: UILabel = { 24 | let label = UILabel() 25 | label.font = .systemFont(ofSize: 18, weight: .bold) 26 | label.textColor = .white 27 | label.textAlignment = .center 28 | return label 29 | }() 30 | 31 | private lazy var messageLabel: UILabel = { 32 | let label = UILabel() 33 | label.font = .systemFont(ofSize: 12, weight: .regular) 34 | label.textColor = .white 35 | label.textAlignment = .center 36 | return label 37 | }() 38 | 39 | private lazy var avatarImageView: UIImageView = { 40 | let imageView = UIImageView() 41 | imageView.contentMode = .scaleAspectFill 42 | imageView.layer.cornerRadius = 30 43 | imageView.layer.masksToBounds = true 44 | return imageView 45 | }() 46 | 47 | private lazy var nameLabel: UILabel = { 48 | let label = UILabel() 49 | label.font = .systemFont(ofSize: 16, weight: .medium) 50 | label.textColor = .white 51 | return label 52 | }() 53 | 54 | private lazy var confirmButton: UIButton = { 55 | let button = UIButton() 56 | button.backgroundColor = .lightGray 57 | button.addTarget(self, action: #selector(onConfirmButton), for: .touchUpInside) 58 | return button 59 | }() 60 | 61 | private lazy var denyButton: UIButton = { 62 | let button = UIButton() 63 | button.backgroundColor = .gray 64 | button.addTarget(self, action: #selector(onConfirmButton), for: .touchUpInside) 65 | return button 66 | }() 67 | 68 | private lazy var buttonsView: UIStackView = { 69 | let stackView = UIStackView(arrangedSubviews: [denyButton, confirmButton]) 70 | stackView.axis = .horizontal 71 | stackView.alignment = .center 72 | stackView.distribution = .fillEqually 73 | stackView.spacing = 8 74 | return stackView 75 | }() 76 | 77 | private lazy var frameView: UIView = { 78 | let view = UIView() 79 | view.backgroundColor = .black 80 | view.clipsToBounds = true 81 | view.layer.cornerRadius = 8 82 | view.layer.borderWidth = 4 83 | view.layer.borderColor = UIColor.white.cgColor 84 | view.addSubview(titleLabel) 85 | view.addSubview(messageLabel) 86 | view.addSubview(avatarImageView) 87 | view.addSubview(nameLabel) 88 | view.addSubview(buttonsView) 89 | return view 90 | }() 91 | 92 | private let alertTitle: String? 93 | private let message: String? 94 | 95 | private var onConfirm: RxAlertCompletion = nil 96 | private var onDeny: RxAlertCompletion = nil 97 | 98 | required init(title: String?, message: String?) { 99 | alertTitle = title 100 | self.message = message 101 | 102 | super.init(nibName: nil, bundle: nil) 103 | 104 | modalPresentationStyle = .overCurrentContext 105 | modalTransitionStyle = .crossDissolve 106 | } 107 | 108 | required init?(coder aDecoder: NSCoder) { 109 | fatalError("init(coder:) has not been implemented") 110 | } 111 | 112 | override func viewDidLoad() { 113 | super.viewDidLoad() 114 | 115 | view.backgroundColor = UIColor.black.withAlphaComponent(0.5) 116 | view.addSubview(frameView) 117 | 118 | titleLabel.text = alertTitle 119 | messageLabel.text = message 120 | 121 | createConstraints() 122 | } 123 | 124 | private func createConstraints() { 125 | 126 | frameView.snp.makeConstraints { 127 | $0.width.equalTo(300) 128 | $0.center.equalToSuperview() 129 | } 130 | 131 | titleLabel.snp.makeConstraints { 132 | $0.centerX.equalToSuperview() 133 | $0.top.equalToSuperview().offset(20) 134 | } 135 | 136 | messageLabel.snp.makeConstraints { 137 | $0.left.equalToSuperview().offset(24) 138 | $0.top.equalTo(titleLabel.snp.bottom).offset(18) 139 | $0.right.equalToSuperview().offset(-24) 140 | } 141 | 142 | avatarImageView.snp.makeConstraints { 143 | $0.size.equalTo(60) 144 | $0.centerX.equalToSuperview() 145 | $0.top.equalTo(messageLabel.snp.bottom).offset(20) 146 | } 147 | 148 | nameLabel.snp.makeConstraints { 149 | $0.centerX.equalToSuperview() 150 | $0.top.equalTo(avatarImageView.snp.bottom).offset(20) 151 | } 152 | 153 | buttonsView.snp.makeConstraints { 154 | $0.left.equalToSuperview().offset(24) 155 | $0.top.equalTo(nameLabel.snp.bottom).offset(20) 156 | $0.right.equalToSuperview().offset(-24) 157 | $0.bottom.equalToSuperview().offset(-30) 158 | } 159 | 160 | buttonsView.arrangedSubviews.forEach { 161 | $0.snp.makeConstraints { 162 | $0.height.equalTo(36) 163 | } 164 | } 165 | } 166 | 167 | @objc private func onConfirmButton() { 168 | dismiss(animated: true) { 169 | self.onConfirm?() 170 | } 171 | } 172 | 173 | @objc private func onDenyButton() { 174 | dismiss(animated: true) { 175 | self.onDeny?() 176 | } 177 | } 178 | } 179 | 180 | extension CustomAlertController: RxAlertController { 181 | 182 | static func create(title: String?, message: String?) -> Self { 183 | return self.init(title: title, message: message) 184 | } 185 | 186 | func setAction(for category: RxAlertCategory, item: RxAlertItem?) { 187 | switch category { 188 | case .single(let confirm): 189 | confirmButton.setTitle("OK", for: .normal) 190 | onConfirm = confirm 191 | denyButton.isHidden = true 192 | case .double(let confirm, let deny): 193 | confirmButton.setTitle("Yes", for: .normal) 194 | denyButton.setTitle("Cancel", for: .normal) 195 | denyButton.isHidden = false 196 | onConfirm = confirm 197 | onDeny = deny 198 | case .multiple(_): 199 | break 200 | } 201 | 202 | guard let customAlertItem = item as? CustomAlertItem else { 203 | return 204 | } 205 | avatarImageView.kf.setImage(with: customAlertItem.avatar) 206 | nameLabel.text = customAlertItem.name 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /Example/RxAlertViewable/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/RxAlertViewable/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/RxAlertViewable/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/RxAlertViewable/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2018/12/01. 6 | // Copyright © 2018 MuShare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxAlertViewable 12 | import SnapKit 13 | 14 | class ViewController: UIViewController, RxAlertViewable { 15 | 16 | private lazy var alertButton: UIButton = { 17 | let button = UIButton() 18 | button.setTitle("Open Alert View", for: .normal) 19 | button.setTitleColor(.blue, for: .normal) 20 | button.rx.tap.bind { [unowned self] in 21 | self.viewModel.showAlert() 22 | }.disposed(by: disposeBag) 23 | return button 24 | }() 25 | 26 | private lazy var multipleAlertButton: UIButton = { 27 | let button = UIButton() 28 | button.setTitle("Open Multiple Alert View", for: .normal) 29 | button.setTitleColor(.orange, for: .normal) 30 | button.rx.tap.bind { [unowned self] in 31 | self.viewModel.showMultipleAlert() 32 | }.disposed(by: disposeBag) 33 | return button 34 | }() 35 | 36 | private lazy var customizedAlertButton: UIButton = { 37 | let button = UIButton() 38 | button.setTitle("Open Customized Alert View", for: .normal) 39 | button.setTitleColor(.brown, for: .normal) 40 | button.rx.tap.bind { [unowned self] in 41 | self.viewModel.showCustomizedAlert() 42 | }.disposed(by: disposeBag) 43 | return button 44 | }() 45 | 46 | private lazy var globalAlertButton: UIButton = { 47 | let button = UIButton() 48 | button.setTitle("Open Global Alert View", for: .normal) 49 | button.setTitleColor(.green, for: .normal) 50 | button.rx.tap.bind { [unowned self] in 51 | self.viewModel.showGlobalAlert() 52 | }.disposed(by: disposeBag) 53 | return button 54 | }() 55 | 56 | private lazy var actionSheetButton: UIButton = { 57 | let button = UIButton() 58 | button.setTitle("Open Action Sheet", for: .normal) 59 | button.setTitleColor(.red, for: .normal) 60 | button.rx.tap.bind { [unowned self] in 61 | self.viewModel.showActionSheet(for: button) 62 | }.disposed(by: disposeBag) 63 | return button 64 | }() 65 | 66 | private lazy var inputButton: UIButton = { 67 | let button = UIButton() 68 | button.setTitle("Open Input AlertView", for: .normal) 69 | button.setTitleColor(.red, for: .normal) 70 | button.rx.tap.bind { [unowned self] in 71 | self.viewModel.showInputAlertView() 72 | }.disposed(by: disposeBag) 73 | return button 74 | }() 75 | 76 | private let viewModel: ViewModel 77 | private let disposeBag = DisposeBag() 78 | 79 | init(viewModel: ViewModel) { 80 | self.viewModel = viewModel 81 | super.init(nibName: nil, bundle: nil) 82 | } 83 | 84 | required init?(coder aDecoder: NSCoder) { 85 | fatalError("init(coder:) has not been implemented") 86 | } 87 | 88 | override func viewDidLoad() { 89 | super.viewDidLoad() 90 | 91 | view.backgroundColor = .white 92 | view.addSubview(alertButton) 93 | view.addSubview(multipleAlertButton) 94 | view.addSubview(customizedAlertButton) 95 | view.addSubview(globalAlertButton) 96 | view.addSubview(actionSheetButton) 97 | view.addSubview(inputButton) 98 | createConstraints() 99 | 100 | viewModel.alert.bind(to: rx.alert).disposed(by: disposeBag) 101 | viewModel.globalTip.bind(to: rx.globalAlert).disposed(by: disposeBag) 102 | viewModel.actionSheet.bind(to: rx.actionSheet).disposed(by: disposeBag) 103 | } 104 | 105 | private func createConstraints() { 106 | 107 | alertButton.snp.makeConstraints { 108 | $0.centerX.equalToSuperview() 109 | $0.bottom.equalTo(multipleAlertButton.snp.top).offset(-20) 110 | } 111 | 112 | multipleAlertButton.snp.makeConstraints { 113 | $0.centerX.equalToSuperview() 114 | $0.bottom.equalTo(customizedAlertButton.snp.top).offset(-20) 115 | } 116 | 117 | customizedAlertButton.snp.makeConstraints { 118 | $0.center.equalToSuperview() 119 | } 120 | 121 | globalAlertButton.snp.makeConstraints { 122 | $0.centerX.equalToSuperview() 123 | $0.top.equalTo(customizedAlertButton.snp.bottom).offset(20) 124 | } 125 | 126 | actionSheetButton.snp.makeConstraints { 127 | $0.centerX.equalToSuperview() 128 | $0.top.equalTo(globalAlertButton.snp.bottom).offset(20) 129 | } 130 | 131 | inputButton.snp.makeConstraints { 132 | $0.centerX.equalToSuperview() 133 | $0.top.equalTo(actionSheetButton.snp.bottom).offset(20) 134 | } 135 | } 136 | 137 | } 138 | 139 | -------------------------------------------------------------------------------- /Example/RxAlertViewable/ViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModel.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2018/12/11. 6 | // Copyright © 2018 MuShare. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import RxCocoa 11 | import RxAlertViewable 12 | 13 | class ViewModel { 14 | 15 | let alert = PublishSubject() 16 | let globalTip = PublishSubject() 17 | let actionSheet = PublishSubject() 18 | let disposeBag = DisposeBag() 19 | 20 | private var clickTimes = 0 21 | 22 | func showAlert() { 23 | clickTimes += 1 24 | let message = "Clicked \(clickTimes) time\(clickTimes > 1 ? "s" : "")." 25 | switch clickTimes % 5 { 26 | case 1: 27 | alert.onNextTip(message) 28 | case 2: 29 | alert.onNextCustomTip(title: "Custom Tip", message: message) 30 | case 3: 31 | alert.onNextWarning(message) 32 | case 4: 33 | alert.onNextError(message) 34 | case 0: 35 | alert.onNextConfirm(message, onConfirm: { 36 | self.showAlert() 37 | }) 38 | default: 39 | alert.onNextTip("???") 40 | } 41 | } 42 | 43 | func showMultipleAlert() { 44 | alert.onNextMultiple( 45 | title: "Multiple", 46 | message: "Multiple actions alert", 47 | .destructive(title: "destructive") { 48 | self.alert.onNextTip("destructive") 49 | }, 50 | .default(title: "default") { 51 | self.alert.onNextTip("default") 52 | }, 53 | .customCancel(title: "customCancel") 54 | ) 55 | } 56 | 57 | func showCustomizedAlert() { 58 | alert.onNextCustomConfirm( 59 | title: "Custom Controller", 60 | message: "Custom alert", 61 | item: CustomAlertItem(name: "Meng Li", avatar: URL(string: "https://avatars0.githubusercontent.com/u/9463655")), 62 | onConfirm: nil, 63 | onDeny: nil 64 | ) 65 | } 66 | 67 | func showGlobalAlert() { 68 | globalTip.onNextConfirm("Confirm message.", onConfirm: { 69 | print("comfirm") 70 | }, onDeny: { 71 | print("deny") 72 | }) 73 | } 74 | 75 | func showActionSheet(for view: UIView) { 76 | actionSheet.onNextActions( 77 | sourceView: view, 78 | title: "Test title", 79 | message: "Test message", 80 | .default(title: "Default") { 81 | print("Default") 82 | }, 83 | .destructive(title: "Destructive") { 84 | print("Destructive") 85 | }, 86 | .cancel 87 | ) 88 | } 89 | 90 | private let textRealy = BehaviorRelay(value: nil) 91 | 92 | func showInputAlertView() { 93 | 94 | alert.onNextTip( 95 | "My Input AlertView", 96 | inputs: [ 97 | RxAlertInput( 98 | placeholder: "My placeholder", 99 | text: "My text", 100 | textAlignment: .center, 101 | onTextChanged: RxAlertInput.OnTextChanged( 102 | text: textRealy, 103 | disposeBag: disposeBag 104 | ) 105 | ) 106 | ], 107 | onConfirm: { [weak self] in 108 | self?.alert.onNextTip(self?.textRealy.value ?? "") 109 | } 110 | ) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Example/Tests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import RxAlertViewable 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /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.1 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: "RxAlertViewable", 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: "RxAlertViewable", 15 | targets: ["RxAlertViewable"] 16 | ), 17 | ], 18 | dependencies: [ 19 | // Dependencies declare other packages that this package depends on. 20 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.0.0")), 21 | ], 22 | targets: [ 23 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 24 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 25 | .target( 26 | name: "RxAlertViewable", 27 | dependencies: [ 28 | "RxSwift", 29 | .product(name: "RxCocoa", package: "RxSwift") 30 | ]), 31 | .testTarget( 32 | name: "RxAlertViewableTests", 33 | dependencies: ["RxAlertViewable"] 34 | ), 35 | ] 36 | ) 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxAlertViewable ![build_check](https://github.com/RxSwiftCommunity/RxAlertViewable/workflows/build_check/badge.svg) [![Version](https://img.shields.io/cocoapods/v/RxAlertViewable.svg?style=flat)](https://cocoapods.org/pods/RxAlertViewable) [![License](https://img.shields.io/cocoapods/l/RxAlertViewable.svg?style=flat)](https://cocoapods.org/pods/RxAlertViewable) [![Platform](https://img.shields.io/cocoapods/p/RxAlertViewable.svg?style=flat)](https://cocoapods.org/pods/RxAlertViewable) 2 | 3 | RxAlertViewable is created for developing the MVVM app with RxSwift. 4 | It supports to show a simple alert from the view model class using the signal `Observable`. 5 | 6 | ![Demo](https://raw.githubusercontent.com/lm2343635/RxAlertViewable/master/screenshots/demo.jpg) 7 | 8 | ## Installation 9 | 10 | RxAlertViewable is available through [CocoaPods](https://cocoapods.org). 11 | To install it, simply add the following line to your Podfile: 12 | 13 | ```ruby 14 | pod 'RxAlertViewable' 15 | ``` 16 | 17 | ## Run Demo 18 | 19 | To run the demo application, install the dependencies with CocoaPods and open the project by `.xcworkspace`. 20 | 21 | ```Shell 22 | pod install 23 | open RxAlertViewable.xcworkspace 24 | ``` 25 | 26 | ## Documentaion 27 | 28 | To use RxAlertViewable, confirm the `RxAlertViewable` protocol in your view controller class at first. 29 | 30 | ```Swift 31 | class ViewController: UIViewController, RxAlertViewable {} 32 | ``` 33 | 34 | ### Alert 35 | 36 | Prepare a PublishSubject `alert` in your view model class. 37 | 38 | ```swift 39 | let alert = PublishSubject() 40 | ``` 41 | 42 | Then, bind it in the view controller class which implemented the protocol `RxAlertViewable`. 43 | 44 | ```Swift 45 | viewModel.alert.bind(to: rx.alert).disposed(by: disposeBag) 46 | ``` 47 | 48 | RxAlertViewable supports the following basic alert types. 49 | 50 | - ```tip(_ message:, onConfirm:, controllerType:)``` 51 | - ```warning(_ message:, onConfirm:, controllerType:)``` 52 | - ```error(_ message:, onConfirm:, controllerType:)``` 53 | - ```confirm(_ message:, onConfirm:, onDeny:, controllerType:)``` 54 | 55 | To show an alert, just send a singal to `alert`. 56 | 57 | ```swift 58 | alert.onNext(.tip("Hello")) 59 | ``` 60 | 61 | or just using the wrapper method like: 62 | 63 | ```swift 64 | alert.onNextTip("Hello") 65 | ``` 66 | From the version `0.8.4`, using the wrapper methods for RxAlert and RxActionSheet is recomended. 67 | 68 | From the version `1.1`, alert with multiple actions was supported. 69 | 70 | ```swift 71 | alert.onNextMultiple( 72 | title: "Multiple", 73 | message: "Multiple actions alert", 74 | .destructive(title: "destructive") { 75 | self.alert.onNextTip("destructive") 76 | }, 77 | .default(title: "default") { 78 | self.alert.onNextTip("default") 79 | }, 80 | .customCancel(title: "customCancel") 81 | ) 82 | ``` 83 | 84 | ### Customized default title, button name and tint color. 85 | 86 | Customize your own strings and tint color using the following code. 87 | 88 | ```swift 89 | RxAlertConfig.current = RxAlertConfig( 90 | tip: "My Tip", 91 | confirm: "My Confirm", 92 | warning: "My Warning", 93 | error: "My Error", 94 | yes: "My Yes", 95 | no: "My No", 96 | ok: "My OK", 97 | cancel: "My Cancel", 98 | tintColor: .blue 99 | ) 100 | ``` 101 | 102 | ### Customized alert controller 103 | 104 | RxAlertViewable supports to customize button names and style of the tip and confirm alert. 105 | 106 | - ```customTip(title:, message:, item:, onConfirm:)``` 107 | - ```customConfirm(title:, message:, item:, onConfirm:, onDeny:)``` 108 | - ```customConfirm(title:, message:, confirmTitle:, denyTitle:, onConfirm:, onDeny:)``` 109 | 110 | To use a customized alert, a data type which implements the `RxAlertItem` protocol, 111 | and a view controller which implements the `RxAlertController` protocol show be prepared. 112 | 113 | A demo custom alert controller is here https://github.com/lm2343635/RxAlertViewable/blob/master/Example/RxAlertViewable/CustomAlertController.swift 114 | 115 | ```swift 116 | struct CustomAlertItem: RxAlertItem { 117 | static var controllerType: RxAlertController.Type = CustomAlertController.self 118 | 119 | var name: String 120 | var avatar: URL? 121 | } 122 | 123 | extension CustomAlertController: RxAlertController { 124 | 125 | static func create(title: String?, message: String?) -> Self { 126 | return self.init(title: title, message: message) 127 | } 128 | 129 | func setAction(for category: RxAlertCategory, item: RxAlertItem?) { 130 | switch category { 131 | case .single(let confirm): 132 | confirmButton.setTitle("OK", for: .normal) 133 | onConfirm = confirm 134 | denyButton.isHidden = true 135 | case .double(let confirm, let deny): 136 | confirmButton.setTitle("Yes", for: .normal) 137 | denyButton.setTitle("Cancel", for: .normal) 138 | denyButton.isHidden = false 139 | onConfirm = confirm 140 | onDeny = deny 141 | } 142 | 143 | guard let customAlertItem = item as? CustomAlertItem else { 144 | return 145 | } 146 | avatarImageView.kf.setImage(with: customAlertItem.avatar) 147 | nameLabel.text = customAlertItem.name 148 | } 149 | 150 | } 151 | ``` 152 | 153 | To show an alert with customzied alert controller, the `item` should be indicated. 154 | 155 | ```swift 156 | alert.onNextCustomConfirm( 157 | title: "Custom Controller", 158 | message: "Custom alert", 159 | item: CustomAlertItem(name: "Meng Li", avatar: URL(string: "https://avatars0.githubusercontent.com/u/9463655")), 160 | onConfirm: nil, 161 | onDeny: nil 162 | ) 163 | ``` 164 | 165 | ### Global Alert 166 | 167 | RxAlertViewable supports to show a global alert view in a new UIWindow instance above the current window, by binding to the `rx.globalAlert` singal from any class. 168 | 169 | ```swift 170 | viewModel.globalTip.bind(to: rx.globalAlert).disposed(by: disposeBag) 171 | ``` 172 | 173 | ### Action sheet. 174 | 175 | Using action sheet is nearly same as using alert. 176 | Prepare a PublishSubject `alert` in your view model class. 177 | 178 | ```swift 179 | let actionSheet = PublishSubject() 180 | ``` 181 | 182 | Then, bind it in the view controller class which implemented the protocol `RxAlertViewable`. 183 | 184 | ```Swift 185 | viewModel.actionSheet.bind(to: rx.actionSheet).disposed(by: disposeBag) 186 | ``` 187 | 188 | To show an action sheet, just send a singal to `actionSheet`. 189 | **The parameter `sourceView` must be indicated if the action sheet will be shown on iPad devices.** 190 | Otherwise, the app will be crashed before showing the action sheet. 191 | 192 | ```swift 193 | actionSheet.onNextActions( 194 | sourceView: view, 195 | title: "Test title", 196 | message: "Test message", 197 | .default(title: "Default") { 198 | print("Default") 199 | }, 200 | .destructive(title: "Destructive") { 201 | print("Destructive") 202 | }, 203 | .cancel 204 | ) 205 | ``` 206 | 207 | ## Author 208 | 209 | lm2343635, lm2343635@126.com 210 | 211 | ## License 212 | 213 | RxAlertViewable is available under the MIT license. See the LICENSE file for more info. 214 | -------------------------------------------------------------------------------- /RxAlertViewable.podspec: -------------------------------------------------------------------------------- 1 | # Be sure to run `pod lib lint RxOrientation.podspec' to ensure this is a 2 | # valid spec before submitting. 3 | # 4 | # Any lines starting with a # are optional, but their use is encouraged 5 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 6 | # 7 | 8 | Pod::Spec.new do |s| 9 | s.name = 'RxAlertViewable' 10 | s.version = '1.2' 11 | s.summary = 'A simple alert library with RxSwift supported.' 12 | 13 | s.description = <<-DESC 14 | RxAlertViewable is created for developing the MVVM app with RxSwift. It supports to show a simple alert from the view model class using the signal Observable. 15 | DESC 16 | 17 | s.homepage = 'https://github.com/lm2343635/RxAlertViewable' 18 | s.license = { :type => 'MIT', :file => 'LICENSE' } 19 | s.author = { 'lm2343635' => 'lm2343635@126.com' } 20 | s.source = { :git => 'https://github.com/lm2343635/RxAlertViewable.git', :tag => s.version.to_s } 21 | 22 | s.ios.deployment_target = '10.0' 23 | s.swift_version = '5.1' 24 | s.source_files = 'RxAlertViewable/Classes/**/*' 25 | s.dependency 'RxSwift', '~> 6' 26 | s.dependency 'RxCocoa', '~> 6' 27 | 28 | end 29 | -------------------------------------------------------------------------------- /RxAlertViewable/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/ObserverType+RxAlert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxAlert.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2019/08/15. 6 | // Copyright © 2018 MuShare. All rights reserved. 7 | // 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import RxSwift 28 | 29 | extension ObserverType where Element == RxAlert { 30 | 31 | public func onNextTip( 32 | _ message: String, 33 | inputs: [RxAlertInput] = [], 34 | onConfirm: RxAlertCompletion = nil 35 | ) { 36 | onNext(.tip(message, inputs: inputs, onConfirm: onConfirm)) 37 | } 38 | 39 | public func onNextWarning(_ message: String, onConfirm: RxAlertCompletion = nil) { 40 | onNext(.warning(message, onConfirm: onConfirm)) 41 | } 42 | 43 | public func onNextError(_ message: String, onConfirm: RxAlertCompletion = nil) { 44 | onNext(.error(message, onConfirm: onConfirm)) 45 | } 46 | 47 | public func onNextConfirm( 48 | _ message: String, 49 | onConfirm: RxAlertCompletion = nil, 50 | onDeny: RxAlertCompletion = nil 51 | ) { 52 | onNext(.confirm(message, onConfirm: onConfirm, onDeny: onDeny)) 53 | } 54 | 55 | public func onNextCustomTip( 56 | title: String, 57 | message: String, 58 | item: RxAlertItem? = nil, 59 | onConfirm: RxAlertCompletion = nil 60 | ) { 61 | onNext(.customTip(title: title, message: message, item: item, onConfirm: onConfirm)) 62 | } 63 | 64 | public func onNextCustomConfirm( 65 | title: String, 66 | message: String, 67 | item: RxAlertItem? = nil, 68 | onConfirm: RxAlertCompletion = nil, 69 | onDeny: RxAlertCompletion = nil 70 | ) { 71 | onNext(.customConfirm(title: title, message: message, item: item, onConfirm: onConfirm, onDeny: onDeny)) 72 | } 73 | 74 | public func onNextCustomConfirm( 75 | title: String, 76 | message: String, 77 | confirmTitle: String, 78 | denyTitle: String? = nil, 79 | onConfirm: RxAlertCompletion = nil, 80 | onDeny: RxAlertCompletion = nil 81 | ) { 82 | let item = UIAlertItem(confirmTitle: confirmTitle, denyTitle: denyTitle) 83 | onNext(.customConfirm(title: title, message: message, item: item, onConfirm: onConfirm, onDeny: onDeny)) 84 | } 85 | 86 | public func onNextMultiple(title: String, message: String, actions: [RxAction]) { 87 | onNext(.multiple(title: title, message: message, actions: actions)) 88 | } 89 | 90 | public func onNextMultiple(title: String, message: String, _ actions: RxAction...) { 91 | onNext(.multiple(title: title, message: message, actions: actions)) 92 | } 93 | 94 | } 95 | 96 | extension ObserverType where Element == RxActionSheet { 97 | 98 | public func onNextActions( 99 | sourceView: UIView? = nil, 100 | title: String? = nil, 101 | message: String? = nil, 102 | _ actions: RxAction... 103 | ) { 104 | onNext(.actions(sourceView: sourceView, title: title, message: message, actions: actions)) 105 | } 106 | 107 | public func onNextActions( 108 | sourceView: UIView? = nil, 109 | title: String? = nil, 110 | message: String? = nil, 111 | actions: [RxAction] 112 | ) { 113 | onNext(.actions(sourceView: sourceView, title: title, message: message, actions: actions)) 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/RxAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxActionSheet.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2021/01/17. 6 | // Copyright © 2021 MuShare. All rights reserved. 7 | // 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | public enum RxAction { 28 | case `default`(title: String, action: RxAlertCompletion) 29 | case destructive(title: String, action: RxAlertCompletion) 30 | case customCancel(title: String) 31 | case cancel 32 | 33 | var alertAction: UIAlertAction { 34 | switch self { 35 | case .default(let title, let action): 36 | return UIAlertAction(title: title, style: .default) { _ in 37 | action?() 38 | } 39 | case .destructive(let title, let action): 40 | return UIAlertAction(title: title, style: .destructive) { _ in 41 | action?() 42 | } 43 | case .customCancel(let title): 44 | return UIAlertAction(title: title, style: .cancel) 45 | case .cancel: 46 | return UIAlertAction(title: RxActionSheet.config.cancel, style: .cancel) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/RxActionSheet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxActionSheet.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2019/05/21. 6 | // Copyright © 2019 MuShare. All rights reserved. 7 | // 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | public struct RxActionSheetConfig { 28 | var cancel: String 29 | var tintColor: UIColor? 30 | 31 | public init(cancel: String? = nil, tintColor: UIColor? = nil) { 32 | self.cancel = cancel ?? RxAlertConfig.current.cancel 33 | self.tintColor = tintColor 34 | } 35 | } 36 | 37 | public struct RxActionSheet { 38 | 39 | public static var config = RxActionSheetConfig() 40 | 41 | private var title: String? 42 | private var message: String? 43 | private var sourceView: UIView? 44 | private var actions: [RxAction] 45 | 46 | public var alertController: UIAlertController { 47 | let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet) 48 | actions.forEach { 49 | alertController.addAction($0.alertAction) 50 | } 51 | if let tintColor = RxAlertConfig.current.tintColor { 52 | alertController.view.tintColor = tintColor 53 | } 54 | if let view = sourceView { 55 | alertController.popoverPresentationController?.sourceView = view 56 | alertController.popoverPresentationController?.sourceRect = view.bounds 57 | } 58 | // The constraints layout warning is a bug caused by iOS from 12.2. 59 | // We can just remove it, until Apple has fixed the bug. 60 | alertController.view.subviews.forEach { subview in 61 | subview.constraints.filter { 62 | $0.debugDescription.contains("width == - 16") 63 | }.forEach { 64 | subview.removeConstraint($0) 65 | } 66 | } 67 | return alertController 68 | } 69 | 70 | } 71 | 72 | extension RxActionSheet { 73 | 74 | public static func actions( 75 | sourceView: UIView? = nil, 76 | title: String? = nil, 77 | message: String? = nil, 78 | actions: [RxAction] 79 | ) -> RxActionSheet { 80 | self.init(title: title, message: message, sourceView: sourceView, actions: actions) 81 | } 82 | 83 | public static func actions( 84 | sourceView: UIView? = nil, 85 | title: String? = nil, 86 | message: String? = nil, 87 | _ actions: RxAction... 88 | ) -> RxActionSheet { 89 | self.init(title: title, message: message, sourceView: sourceView, actions: actions) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/RxAlert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxAlert.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2018/12/09. 6 | // Copyright © 2018 MuShare. All rights reserved. 7 | // 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | public typealias RxAlertCompletion = (() -> ())? 28 | 29 | public enum RxAlertCategory { 30 | case single(onConfirm: RxAlertCompletion) 31 | case double(onConfirm: RxAlertCompletion, onDeny: RxAlertCompletion) 32 | case multiple(actions: [RxAction]) 33 | } 34 | 35 | public struct RxAlert { 36 | 37 | private var title: String 38 | private var message: String 39 | 40 | private var item: RxAlertItem? 41 | private var category: RxAlertCategory 42 | 43 | init( 44 | title: String, 45 | message: String, 46 | item: RxAlertItem? = nil, 47 | category: RxAlertCategory 48 | ) { 49 | self.title = title 50 | self.message = message 51 | self.item = item 52 | self.category = category 53 | } 54 | 55 | var alertController: RxAlertController { 56 | let controllerType: RxAlertController.Type 57 | if let item = item { 58 | controllerType = type(of: item).controllerType 59 | } else { 60 | controllerType = UIAlertController.self 61 | } 62 | 63 | let alertController = controllerType.create(title: title, message: message) 64 | alertController.setAction(for: category, item: item) 65 | if let tintColor = RxAlertConfig.current.tintColor { 66 | alertController.view.tintColor = tintColor 67 | } 68 | return alertController 69 | } 70 | 71 | } 72 | 73 | extension RxAlert { 74 | 75 | public static func tip( 76 | _ message: String, 77 | inputs: [RxAlertInput] = [], 78 | onConfirm: RxAlertCompletion = nil 79 | ) -> RxAlert { 80 | return self.init( 81 | title: RxAlertConfig.current.tip, 82 | message: message, 83 | item: UIAlertItem( 84 | inputs: inputs, 85 | confirmTitle: RxAlertConfig.current.ok 86 | ), 87 | category: .single(onConfirm: onConfirm) 88 | ) 89 | } 90 | 91 | public static func warning( 92 | _ message: String, 93 | inputs: [RxAlertInput] = [], 94 | onConfirm: RxAlertCompletion = nil 95 | ) -> RxAlert { 96 | return self.init( 97 | title: RxAlertConfig.current.warning, 98 | message: message, 99 | item: UIAlertItem( 100 | inputs: inputs, 101 | confirmTitle: RxAlertConfig.current.ok 102 | ), 103 | category: .single(onConfirm: onConfirm) 104 | ) 105 | } 106 | 107 | public static func error( 108 | _ message: String, 109 | inputs: [RxAlertInput] = [], 110 | onConfirm: RxAlertCompletion = nil 111 | ) -> RxAlert { 112 | return self.init( 113 | title: RxAlertConfig.current.error, 114 | message: message, 115 | item: UIAlertItem( 116 | inputs: inputs, 117 | confirmTitle: RxAlertConfig.current.ok 118 | ), 119 | category: .single(onConfirm: onConfirm) 120 | ) 121 | } 122 | 123 | public static func confirm( 124 | _ message: String, 125 | onConfirm: RxAlertCompletion = nil, 126 | onDeny: RxAlertCompletion = nil 127 | ) -> RxAlert { 128 | return self.init( 129 | title: RxAlertConfig.current.confirm, 130 | message: message, 131 | category: .double(onConfirm: onConfirm, onDeny: onDeny) 132 | ) 133 | } 134 | 135 | public static func customTip( 136 | title: String, 137 | message: String, 138 | item: RxAlertItem? = nil, 139 | onConfirm: RxAlertCompletion = nil 140 | ) -> RxAlert { 141 | return self.init( 142 | title: title, 143 | message: message, 144 | item: item, 145 | category: .single(onConfirm: onConfirm) 146 | ) 147 | } 148 | 149 | public static func customConfirm( 150 | title: String, 151 | message: String, 152 | item: RxAlertItem? = nil, 153 | onConfirm: RxAlertCompletion = nil, 154 | onDeny: RxAlertCompletion = nil 155 | ) -> RxAlert { 156 | return self.init( 157 | title: title, 158 | message: message, 159 | item: item, 160 | category: .double(onConfirm: onConfirm, onDeny: onDeny) 161 | ) 162 | } 163 | 164 | public static func multiple( 165 | title: String, 166 | message: String, 167 | actions: [RxAction] 168 | ) -> RxAlert { 169 | return self.init(title: title, message: message, category: .multiple(actions: actions)) 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/RxAlertConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxAlertConfig.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2020/10/29. 6 | // Copyright © 2018 MuShare. All rights reserved. 7 | // 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | public struct RxAlertConfig { 28 | var tip: String 29 | var confirm: String 30 | var warning: String 31 | var error: String 32 | var yes: String 33 | var no: String 34 | var ok: String 35 | var cancel: String 36 | var tintColor: UIColor? 37 | 38 | public init( 39 | tip: String? = nil, 40 | confirm: String? = nil, 41 | warning: String? = nil, 42 | error: String? = nil, 43 | yes: String? = nil, 44 | no: String? = nil, 45 | ok: String? = nil, 46 | cancel: String? = nil, 47 | tintColor: UIColor? = nil 48 | ) { 49 | self.tip = tip ?? "Tip" 50 | self.confirm = confirm ?? "Confirm" 51 | self.warning = warning ?? "Warning" 52 | self.error = error ?? "Error" 53 | self.yes = yes ?? "Yes" 54 | self.no = no ?? "No" 55 | self.ok = ok ?? "OK" 56 | self.cancel = cancel ?? "Cancel" 57 | self.tintColor = tintColor 58 | } 59 | 60 | public static var current = RxAlertConfig() 61 | } 62 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/RxAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxAlert.swift 3 | // RxAlertViewable 4 | // 5 | // Created by takuji.terada on 2019/07/10. 6 | // Copyright © 2019 XFLAG. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import RxCocoa 30 | import RxSwift 31 | 32 | public protocol RxAlertItem { 33 | static var controllerType: RxAlertController.Type { get } 34 | } 35 | 36 | public protocol RxAlertController: UIViewController { 37 | 38 | static func create(title: String?, message: String?) -> Self 39 | 40 | func setAction(for category: RxAlertCategory, item: RxAlertItem?) 41 | } 42 | 43 | public struct RxAlertInput { 44 | let placeholder: String? 45 | let text: String? 46 | let textAlignment: NSTextAlignment 47 | let onTextChanged: OnTextChanged? 48 | 49 | public init( 50 | placeholder: String? = nil, 51 | text: String? = nil, 52 | textAlignment: NSTextAlignment = .left, 53 | onTextChanged: OnTextChanged? = nil 54 | ) { 55 | self.placeholder = placeholder 56 | self.text = text 57 | self.textAlignment = textAlignment 58 | self.onTextChanged = onTextChanged 59 | } 60 | 61 | public struct OnTextChanged { 62 | let text: BehaviorRelay 63 | let disposeBag: DisposeBag 64 | 65 | public init(text: BehaviorRelay, disposeBag: DisposeBag) { 66 | self.text = text 67 | self.disposeBag = disposeBag 68 | } 69 | } 70 | } 71 | 72 | public struct UIAlertItem: RxAlertItem { 73 | 74 | public static let controllerType: RxAlertController.Type = UIAlertController.self 75 | 76 | var inputs: [RxAlertInput] 77 | var confirmTitle: String 78 | var denyTitle: String? 79 | 80 | public init( 81 | inputs: [RxAlertInput] = [], 82 | confirmTitle: String, 83 | denyTitle: String? = nil 84 | ) { 85 | self.inputs = inputs 86 | self.confirmTitle = confirmTitle 87 | self.denyTitle = denyTitle 88 | } 89 | 90 | } 91 | 92 | extension UIAlertController: RxAlertController { 93 | 94 | public static func create(title: String?, message: String?) -> Self { 95 | self.init(title: title, message: message, preferredStyle: .alert) 96 | } 97 | 98 | public func setAction(for category: RxAlertCategory, item: RxAlertItem?) { 99 | var confirmTitle = RxAlertConfig.current.yes 100 | var denyTitle = RxAlertConfig.current.no 101 | if let alertItem = item as? UIAlertItem { 102 | confirmTitle = alertItem.confirmTitle 103 | if let deny = alertItem.denyTitle { 104 | denyTitle = deny 105 | } 106 | alertItem.inputs.forEach { input in 107 | addTextField { 108 | $0.placeholder = input.placeholder 109 | $0.text = input.text 110 | $0.textAlignment = input.textAlignment 111 | if let onChanged = input.onTextChanged { 112 | $0.rx.text.bind(to: onChanged.text).disposed(by: onChanged.disposeBag) 113 | } 114 | } 115 | } 116 | } 117 | 118 | switch category { 119 | case .single(let onConfirm): 120 | addAction(UIAlertAction(title: confirmTitle, style: .cancel) { _ in 121 | onConfirm?() 122 | }) 123 | case .double(let onConfirm, let onDeny): 124 | addAction(UIAlertAction(title: confirmTitle, style: .destructive) { _ in 125 | onConfirm?() 126 | }) 127 | addAction(UIAlertAction(title: denyTitle, style: .cancel) { _ in 128 | onDeny?() 129 | }) 130 | case .multiple(let actions): 131 | actions.forEach { 132 | addAction($0.alertAction) 133 | } 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/RxAlertViewable+Rx.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxAlertViewable+Rx.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2018/12/09. 6 | // Copyright © 2018 MuShare. All rights reserved. 7 | // 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(iOS) 28 | 29 | import RxSwift 30 | import RxCocoa 31 | 32 | extension Reactive where Base: UIViewController, Base: RxAlertViewable { 33 | 34 | public var alert: Binder { 35 | Binder(base) { viewController, alert in 36 | viewController.showAlert(alert) 37 | } 38 | } 39 | 40 | public var actionSheet: Binder { 41 | Binder(base) { viewControlelr, actionSheet in 42 | viewControlelr.showActionSheet(actionSheet) 43 | } 44 | } 45 | 46 | } 47 | 48 | extension Reactive where Base: AnyObject, Base: RxAlertViewable { 49 | 50 | public var globalAlert: Binder { 51 | Binder(base) { object, alert in 52 | object.showGlobalAlert(alert) 53 | } 54 | } 55 | 56 | } 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /RxAlertViewable/Classes/RxAlertViewable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxAlertViewable.swift 3 | // RxAlertViewable 4 | // 5 | // Created by Meng Li on 2018/12/09. 6 | // Copyright © 2018 MuShare. All rights reserved. 7 | // 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | public protocol RxAlertViewable {} 28 | 29 | extension RxAlertViewable where Self: UIViewController { 30 | 31 | public func showAlert(_ alert: RxAlert) { 32 | present(alert.alertController, animated: true) 33 | } 34 | 35 | public func showActionSheet(_ actionSheet: RxActionSheet) { 36 | present(actionSheet.alertController, animated: true) 37 | } 38 | 39 | } 40 | 41 | extension RxAlertViewable where Self: AnyObject { 42 | 43 | public func showGlobalAlert(_ alert: RxAlert) { 44 | let viewController = UIViewController() 45 | let alertWindow = UIWindow(frame: UIScreen.main.bounds) 46 | alertWindow.rootViewController = viewController 47 | alertWindow.windowLevel = UIWindow.Level.alert + 1 48 | alertWindow.makeKeyAndVisible() 49 | GlobalWindowHolder.shared.alertWindow = alertWindow 50 | viewController.present(alert.alertController, animated: true) 51 | } 52 | 53 | } 54 | 55 | class GlobalWindowHolder { 56 | static let shared = GlobalWindowHolder() 57 | 58 | private var timer: Timer? 59 | 60 | var alertWindow: UIWindow? { 61 | didSet { 62 | timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in 63 | if self.alertWindow?.rootViewController?.presentedViewController == nil { 64 | self.timer?.invalidate() 65 | self.alertWindow = nil 66 | } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /screenshots/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxAlertViewable/HEAD/screenshots/demo.jpg --------------------------------------------------------------------------------