├── .gitignore ├── ChainedAlertController.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── ChainedAlertController ├── Application │ ├── AppDelegate.swift │ ├── Resources │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ └── Info.plist │ └── ViewController.swift └── ChainedAlert │ ├── Extensions │ ├── UIAlertController+ChainedAlert.swift │ └── UIViewController+ChainedAlert.swift │ └── Models │ ├── ChainedAlert.swift │ ├── ChainedAlertAction.swift │ └── ChainedAlertTree.swift ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | .DS_Store 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /ChainedAlertController.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B23CE77320D1AEDC0007E9B6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B23CE77220D1AEDC0007E9B6 /* AppDelegate.swift */; }; 11 | B23CE77520D1AEDC0007E9B6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B23CE77420D1AEDC0007E9B6 /* ViewController.swift */; }; 12 | B23CE77820D1AEDC0007E9B6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B23CE77620D1AEDC0007E9B6 /* Main.storyboard */; }; 13 | B23CE77A20D1AEDD0007E9B6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B23CE77920D1AEDD0007E9B6 /* Assets.xcassets */; }; 14 | B23CE77D20D1AEDD0007E9B6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B23CE77B20D1AEDD0007E9B6 /* LaunchScreen.storyboard */; }; 15 | B23CE78B20D1B00B0007E9B6 /* UIAlertController+ChainedAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = B23CE78920D1B00B0007E9B6 /* UIAlertController+ChainedAlert.swift */; }; 16 | B23CE78C20D1B00B0007E9B6 /* UIViewController+ChainedAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = B23CE78A20D1B00B0007E9B6 /* UIViewController+ChainedAlert.swift */; }; 17 | B23CE79820D1B23E0007E9B6 /* ChainedAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = B23CE79720D1B23E0007E9B6 /* ChainedAlert.swift */; }; 18 | B23CE79A20D1B2460007E9B6 /* ChainedAlertAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B23CE79920D1B2460007E9B6 /* ChainedAlertAction.swift */; }; 19 | B23CE79C20D1B26D0007E9B6 /* ChainedAlertTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = B23CE79B20D1B26D0007E9B6 /* ChainedAlertTree.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | B23CE76F20D1AEDC0007E9B6 /* ChainedAlertController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChainedAlertController.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | B23CE77220D1AEDC0007E9B6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 25 | B23CE77420D1AEDC0007E9B6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 26 | B23CE77720D1AEDC0007E9B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | B23CE77920D1AEDD0007E9B6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | B23CE77C20D1AEDD0007E9B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | B23CE77E20D1AEDD0007E9B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | B23CE78920D1B00B0007E9B6 /* UIAlertController+ChainedAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertController+ChainedAlert.swift"; sourceTree = ""; }; 31 | B23CE78A20D1B00B0007E9B6 /* UIViewController+ChainedAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+ChainedAlert.swift"; sourceTree = ""; }; 32 | B23CE79720D1B23E0007E9B6 /* ChainedAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainedAlert.swift; sourceTree = ""; }; 33 | B23CE79920D1B2460007E9B6 /* ChainedAlertAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainedAlertAction.swift; sourceTree = ""; }; 34 | B23CE79B20D1B26D0007E9B6 /* ChainedAlertTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainedAlertTree.swift; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | B23CE76C20D1AEDC0007E9B6 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | B23CE76620D1AEDC0007E9B6 = { 49 | isa = PBXGroup; 50 | children = ( 51 | B23CE77120D1AEDC0007E9B6 /* ChainedAlertController */, 52 | B23CE77020D1AEDC0007E9B6 /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | B23CE77020D1AEDC0007E9B6 /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | B23CE76F20D1AEDC0007E9B6 /* ChainedAlertController.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | B23CE77120D1AEDC0007E9B6 /* ChainedAlertController */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | B23CE78420D1AEEC0007E9B6 /* Application */, 68 | B23CE78620D1AF1F0007E9B6 /* ChainedAlert */, 69 | ); 70 | path = ChainedAlertController; 71 | sourceTree = ""; 72 | }; 73 | B23CE78420D1AEEC0007E9B6 /* Application */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | B23CE77220D1AEDC0007E9B6 /* AppDelegate.swift */, 77 | B23CE77420D1AEDC0007E9B6 /* ViewController.swift */, 78 | B23CE78520D1AEF30007E9B6 /* Resources */, 79 | ); 80 | path = Application; 81 | sourceTree = ""; 82 | }; 83 | B23CE78520D1AEF30007E9B6 /* Resources */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | B23CE77620D1AEDC0007E9B6 /* Main.storyboard */, 87 | B23CE77920D1AEDD0007E9B6 /* Assets.xcassets */, 88 | B23CE77B20D1AEDD0007E9B6 /* LaunchScreen.storyboard */, 89 | B23CE77E20D1AEDD0007E9B6 /* Info.plist */, 90 | ); 91 | path = Resources; 92 | sourceTree = ""; 93 | }; 94 | B23CE78620D1AF1F0007E9B6 /* ChainedAlert */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | B23CE78720D1AF560007E9B6 /* Extensions */, 98 | B23CE78820D1AF630007E9B6 /* Models */, 99 | ); 100 | path = ChainedAlert; 101 | sourceTree = ""; 102 | }; 103 | B23CE78720D1AF560007E9B6 /* Extensions */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | B23CE78920D1B00B0007E9B6 /* UIAlertController+ChainedAlert.swift */, 107 | B23CE78A20D1B00B0007E9B6 /* UIViewController+ChainedAlert.swift */, 108 | ); 109 | path = Extensions; 110 | sourceTree = ""; 111 | }; 112 | B23CE78820D1AF630007E9B6 /* Models */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | B23CE79720D1B23E0007E9B6 /* ChainedAlert.swift */, 116 | B23CE79920D1B2460007E9B6 /* ChainedAlertAction.swift */, 117 | B23CE79B20D1B26D0007E9B6 /* ChainedAlertTree.swift */, 118 | ); 119 | path = Models; 120 | sourceTree = ""; 121 | }; 122 | /* End PBXGroup section */ 123 | 124 | /* Begin PBXNativeTarget section */ 125 | B23CE76E20D1AEDC0007E9B6 /* ChainedAlertController */ = { 126 | isa = PBXNativeTarget; 127 | buildConfigurationList = B23CE78120D1AEDD0007E9B6 /* Build configuration list for PBXNativeTarget "ChainedAlertController" */; 128 | buildPhases = ( 129 | B23CE76B20D1AEDC0007E9B6 /* Sources */, 130 | B23CE76C20D1AEDC0007E9B6 /* Frameworks */, 131 | B23CE76D20D1AEDC0007E9B6 /* Resources */, 132 | ); 133 | buildRules = ( 134 | ); 135 | dependencies = ( 136 | ); 137 | name = ChainedAlertController; 138 | productName = ChainedAlertController; 139 | productReference = B23CE76F20D1AEDC0007E9B6 /* ChainedAlertController.app */; 140 | productType = "com.apple.product-type.application"; 141 | }; 142 | /* End PBXNativeTarget section */ 143 | 144 | /* Begin PBXProject section */ 145 | B23CE76720D1AEDC0007E9B6 /* Project object */ = { 146 | isa = PBXProject; 147 | attributes = { 148 | LastSwiftUpdateCheck = 0940; 149 | LastUpgradeCheck = 0940; 150 | ORGANIZATIONNAME = "Said Ozcan"; 151 | TargetAttributes = { 152 | B23CE76E20D1AEDC0007E9B6 = { 153 | CreatedOnToolsVersion = 9.4; 154 | }; 155 | }; 156 | }; 157 | buildConfigurationList = B23CE76A20D1AEDC0007E9B6 /* Build configuration list for PBXProject "ChainedAlertController" */; 158 | compatibilityVersion = "Xcode 9.3"; 159 | developmentRegion = en; 160 | hasScannedForEncodings = 0; 161 | knownRegions = ( 162 | en, 163 | Base, 164 | ); 165 | mainGroup = B23CE76620D1AEDC0007E9B6; 166 | productRefGroup = B23CE77020D1AEDC0007E9B6 /* Products */; 167 | projectDirPath = ""; 168 | projectRoot = ""; 169 | targets = ( 170 | B23CE76E20D1AEDC0007E9B6 /* ChainedAlertController */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXResourcesBuildPhase section */ 176 | B23CE76D20D1AEDC0007E9B6 /* Resources */ = { 177 | isa = PBXResourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | B23CE77D20D1AEDD0007E9B6 /* LaunchScreen.storyboard in Resources */, 181 | B23CE77A20D1AEDD0007E9B6 /* Assets.xcassets in Resources */, 182 | B23CE77820D1AEDC0007E9B6 /* Main.storyboard in Resources */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXResourcesBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | B23CE76B20D1AEDC0007E9B6 /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | B23CE77520D1AEDC0007E9B6 /* ViewController.swift in Sources */, 194 | B23CE79A20D1B2460007E9B6 /* ChainedAlertAction.swift in Sources */, 195 | B23CE79C20D1B26D0007E9B6 /* ChainedAlertTree.swift in Sources */, 196 | B23CE78B20D1B00B0007E9B6 /* UIAlertController+ChainedAlert.swift in Sources */, 197 | B23CE78C20D1B00B0007E9B6 /* UIViewController+ChainedAlert.swift in Sources */, 198 | B23CE77320D1AEDC0007E9B6 /* AppDelegate.swift in Sources */, 199 | B23CE79820D1B23E0007E9B6 /* ChainedAlert.swift in Sources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXSourcesBuildPhase section */ 204 | 205 | /* Begin PBXVariantGroup section */ 206 | B23CE77620D1AEDC0007E9B6 /* Main.storyboard */ = { 207 | isa = PBXVariantGroup; 208 | children = ( 209 | B23CE77720D1AEDC0007E9B6 /* Base */, 210 | ); 211 | name = Main.storyboard; 212 | sourceTree = ""; 213 | }; 214 | B23CE77B20D1AEDD0007E9B6 /* LaunchScreen.storyboard */ = { 215 | isa = PBXVariantGroup; 216 | children = ( 217 | B23CE77C20D1AEDD0007E9B6 /* Base */, 218 | ); 219 | name = LaunchScreen.storyboard; 220 | sourceTree = ""; 221 | }; 222 | /* End PBXVariantGroup section */ 223 | 224 | /* Begin XCBuildConfiguration section */ 225 | B23CE77F20D1AEDD0007E9B6 /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | ALWAYS_SEARCH_USER_PATHS = NO; 229 | CLANG_ANALYZER_NONNULL = YES; 230 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 231 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 232 | CLANG_CXX_LIBRARY = "libc++"; 233 | CLANG_ENABLE_MODULES = YES; 234 | CLANG_ENABLE_OBJC_ARC = YES; 235 | CLANG_ENABLE_OBJC_WEAK = YES; 236 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 237 | CLANG_WARN_BOOL_CONVERSION = YES; 238 | CLANG_WARN_COMMA = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 242 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 243 | CLANG_WARN_EMPTY_BODY = YES; 244 | CLANG_WARN_ENUM_CONVERSION = YES; 245 | CLANG_WARN_INFINITE_RECURSION = YES; 246 | CLANG_WARN_INT_CONVERSION = YES; 247 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 248 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 249 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 250 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 251 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 252 | CLANG_WARN_STRICT_PROTOTYPES = YES; 253 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 254 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 255 | CLANG_WARN_UNREACHABLE_CODE = YES; 256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 257 | CODE_SIGN_IDENTITY = "iPhone Developer"; 258 | COPY_PHASE_STRIP = NO; 259 | DEBUG_INFORMATION_FORMAT = dwarf; 260 | ENABLE_STRICT_OBJC_MSGSEND = YES; 261 | ENABLE_TESTABILITY = YES; 262 | GCC_C_LANGUAGE_STANDARD = gnu11; 263 | GCC_DYNAMIC_NO_PIC = NO; 264 | GCC_NO_COMMON_BLOCKS = YES; 265 | GCC_OPTIMIZATION_LEVEL = 0; 266 | GCC_PREPROCESSOR_DEFINITIONS = ( 267 | "DEBUG=1", 268 | "$(inherited)", 269 | ); 270 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 271 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 272 | GCC_WARN_UNDECLARED_SELECTOR = YES; 273 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 274 | GCC_WARN_UNUSED_FUNCTION = YES; 275 | GCC_WARN_UNUSED_VARIABLE = YES; 276 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 277 | MTL_ENABLE_DEBUG_INFO = YES; 278 | ONLY_ACTIVE_ARCH = YES; 279 | SDKROOT = iphoneos; 280 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 281 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 282 | }; 283 | name = Debug; 284 | }; 285 | B23CE78020D1AEDD0007E9B6 /* Release */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ALWAYS_SEARCH_USER_PATHS = NO; 289 | CLANG_ANALYZER_NONNULL = YES; 290 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 291 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 292 | CLANG_CXX_LIBRARY = "libc++"; 293 | CLANG_ENABLE_MODULES = YES; 294 | CLANG_ENABLE_OBJC_ARC = YES; 295 | CLANG_ENABLE_OBJC_WEAK = YES; 296 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 297 | CLANG_WARN_BOOL_CONVERSION = YES; 298 | CLANG_WARN_COMMA = YES; 299 | CLANG_WARN_CONSTANT_CONVERSION = YES; 300 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 301 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 302 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN_ENUM_CONVERSION = YES; 305 | CLANG_WARN_INFINITE_RECURSION = YES; 306 | CLANG_WARN_INT_CONVERSION = YES; 307 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 308 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 309 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 315 | CLANG_WARN_UNREACHABLE_CODE = YES; 316 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 317 | CODE_SIGN_IDENTITY = "iPhone Developer"; 318 | COPY_PHASE_STRIP = NO; 319 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 320 | ENABLE_NS_ASSERTIONS = NO; 321 | ENABLE_STRICT_OBJC_MSGSEND = YES; 322 | GCC_C_LANGUAGE_STANDARD = gnu11; 323 | GCC_NO_COMMON_BLOCKS = YES; 324 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 325 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 326 | GCC_WARN_UNDECLARED_SELECTOR = YES; 327 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 328 | GCC_WARN_UNUSED_FUNCTION = YES; 329 | GCC_WARN_UNUSED_VARIABLE = YES; 330 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 331 | MTL_ENABLE_DEBUG_INFO = NO; 332 | SDKROOT = iphoneos; 333 | SWIFT_COMPILATION_MODE = wholemodule; 334 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 335 | VALIDATE_PRODUCT = YES; 336 | }; 337 | name = Release; 338 | }; 339 | B23CE78220D1AEDD0007E9B6 /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 343 | CODE_SIGN_STYLE = Automatic; 344 | INFOPLIST_FILE = "$(SRCROOT)/ChainedAlertController/Application/Resources/Info.plist"; 345 | LD_RUNPATH_SEARCH_PATHS = ( 346 | "$(inherited)", 347 | "@executable_path/Frameworks", 348 | ); 349 | PRODUCT_BUNDLE_IDENTIFIER = nl.saidozcan.ChainedAlertController; 350 | PRODUCT_NAME = "$(TARGET_NAME)"; 351 | SWIFT_VERSION = 4.0; 352 | TARGETED_DEVICE_FAMILY = "1,2"; 353 | }; 354 | name = Debug; 355 | }; 356 | B23CE78320D1AEDD0007E9B6 /* Release */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | CODE_SIGN_STYLE = Automatic; 361 | INFOPLIST_FILE = "$(SRCROOT)/ChainedAlertController/Application/Resources/Info.plist"; 362 | LD_RUNPATH_SEARCH_PATHS = ( 363 | "$(inherited)", 364 | "@executable_path/Frameworks", 365 | ); 366 | PRODUCT_BUNDLE_IDENTIFIER = nl.saidozcan.ChainedAlertController; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | SWIFT_VERSION = 4.0; 369 | TARGETED_DEVICE_FAMILY = "1,2"; 370 | }; 371 | name = Release; 372 | }; 373 | /* End XCBuildConfiguration section */ 374 | 375 | /* Begin XCConfigurationList section */ 376 | B23CE76A20D1AEDC0007E9B6 /* Build configuration list for PBXProject "ChainedAlertController" */ = { 377 | isa = XCConfigurationList; 378 | buildConfigurations = ( 379 | B23CE77F20D1AEDD0007E9B6 /* Debug */, 380 | B23CE78020D1AEDD0007E9B6 /* Release */, 381 | ); 382 | defaultConfigurationIsVisible = 0; 383 | defaultConfigurationName = Release; 384 | }; 385 | B23CE78120D1AEDD0007E9B6 /* Build configuration list for PBXNativeTarget "ChainedAlertController" */ = { 386 | isa = XCConfigurationList; 387 | buildConfigurations = ( 388 | B23CE78220D1AEDD0007E9B6 /* Debug */, 389 | B23CE78320D1AEDD0007E9B6 /* Release */, 390 | ); 391 | defaultConfigurationIsVisible = 0; 392 | defaultConfigurationName = Release; 393 | }; 394 | /* End XCConfigurationList section */ 395 | }; 396 | rootObject = B23CE76720D1AEDC0007E9B6 /* Project object */; 397 | } 398 | -------------------------------------------------------------------------------- /ChainedAlertController.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ChainedAlertController.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ChainedAlertController/Application/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ChainedAlertController 4 | // 5 | // Created by Said Ozcan on 13/06/2018. 6 | // Copyright © 2018 Said Ozcan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | return true 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /ChainedAlertController/Application/Resources/Assets.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" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /ChainedAlertController/Application/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ChainedAlertController/Application/Resources/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ChainedAlertController/Application/Resources/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /ChainedAlertController/Application/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ChainedAlertController/Application/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ChainedAlertController 4 | // 5 | // Created by Said Ozcan on 13/06/2018. 6 | // Copyright © 2018 Said Ozcan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | // MARK: Properties 14 | fileprivate lazy var firstAction: ChainedAlertTree = { 15 | let cancelAction = ChainedAlertAction(title: "Cancel", style: .cancel, shouldTriggerNextAlert: false, handler: nil) 16 | let triggerNextAction = ChainedAlertAction(title: "Trigger next", style: .default, shouldTriggerNextAlert: true, handler: nil) 17 | let alert = ChainedAlert(title: "First alert title", message: "First alert message", actions: [cancelAction, triggerNextAction]) 18 | return ChainedAlertTree.alert(alert: alert, nextAlert: secondAction) 19 | }() 20 | 21 | fileprivate lazy var secondAction: ChainedAlertTree = { 22 | let cancelAction = ChainedAlertAction(title: "Cancel", style: .cancel, shouldTriggerNextAlert: false, handler: nil) 23 | let triggerNextAction = ChainedAlertAction(title: "Trigger next", style: .default, shouldTriggerNextAlert: true, handler: nil) 24 | let alert = ChainedAlert(title: "Second action title", message: "Second action message", actions: [cancelAction, triggerNextAction]) 25 | return ChainedAlertTree.alert(alert: alert, nextAlert: thirdAction) 26 | }() 27 | 28 | fileprivate lazy var thirdAction: ChainedAlertTree = { 29 | let cancelAction = ChainedAlertAction(title: "It's enough.", style: .cancel, shouldTriggerNextAlert: false, handler: nil) 30 | let alert = ChainedAlert(title: "Third action title", message: "Third action message", actions: [cancelAction]) 31 | return ChainedAlertTree.alert(alert: alert, nextAlert: nil) 32 | }() 33 | 34 | // MARK: Lifecycle 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in 38 | guard let strongSelf = self else { return } 39 | strongSelf.showChainedAlert(alert: strongSelf.firstAction) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /ChainedAlertController/ChainedAlert/Extensions/UIAlertController+ChainedAlert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertController+ChainedAlert.swift 3 | // ChainedAlert 4 | // 5 | // Created by Said Ozcan on 13/06/2018. 6 | // Copyright © 2018 Said Ozcan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIAlertController { 12 | func add(actions: [UIAlertAction]) { 13 | for action in actions { 14 | addAction(action) 15 | } 16 | } 17 | 18 | static func alertController(from alertModel: ChainedAlert) -> UIAlertController { 19 | let alertController = UIAlertController(title: alertModel.title, message: alertModel.message, preferredStyle: .alert) 20 | 21 | let actions: [UIAlertAction] = alertModel.actions.map { actionModel -> UIAlertAction in 22 | return UIAlertAction(title: actionModel.title, style: actionModel.style) { action in 23 | if let handler = actionModel.handler { 24 | handler(actionModel) 25 | } 26 | } 27 | } 28 | 29 | alertController.add(actions: actions) 30 | 31 | return alertController 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ChainedAlertController/ChainedAlert/Extensions/UIViewController+ChainedAlert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+ChainedAlert.swift 3 | // ChainedAlert 4 | // 5 | // Created by Said Ozcan on 13/06/2018. 6 | // Copyright © 2018 Said Ozcan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIViewController { 12 | func showChainedAlert(alert: ChainedAlertTree) { 13 | // First alert 14 | switch alert { 15 | case .alert(var current, let next): 16 | 17 | guard let nextChain = next else { 18 | showChainedAlert(alert: current) 19 | return 20 | } 21 | 22 | // updating all the actions in current chain to trigger next 23 | // which says shouldTriggerNext: true 24 | let updatedActions = current.actions.map { (originalAction) -> ChainedAlertAction in 25 | 26 | guard originalAction.shouldTriggerNextAlert else { return originalAction } 27 | 28 | let newHandler: (ChainedAlertAction) -> Void = { [weak self] (newAlertAction) in 29 | 30 | // first calling the initial handler if exists 31 | if let originalHandler = originalAction.handler { 32 | originalHandler(originalAction) 33 | } 34 | 35 | // then triggering next one 36 | if originalAction.shouldTriggerNextAlert { 37 | self?.showChainedAlert(alert: nextChain) 38 | } 39 | } 40 | 41 | return ChainedAlertAction(title: originalAction.title, style: originalAction.style, shouldTriggerNextAlert: originalAction.shouldTriggerNextAlert, handler: newHandler) 42 | } 43 | 44 | current.actions = updatedActions 45 | showChainedAlert(alert: current) 46 | break 47 | } 48 | } 49 | 50 | fileprivate func showChainedAlert(alert: ChainedAlert) { 51 | let alertController = UIAlertController.alertController(from: alert) 52 | present(alertController, animated: true, completion: nil) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ChainedAlertController/ChainedAlert/Models/ChainedAlert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChainedAlert.swift 3 | // ChainedAlertController 4 | // 5 | // Created by Said Ozcan on 13/06/2018. 6 | // Copyright © 2018 Said Ozcan. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ChainedAlert { 12 | let title: String 13 | let message: String 14 | var actions: [ChainedAlertAction] 15 | } 16 | -------------------------------------------------------------------------------- /ChainedAlertController/ChainedAlert/Models/ChainedAlertAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChainedAlertAction.swift 3 | // ChainedAlertController 4 | // 5 | // Created by Said Ozcan on 13/06/2018. 6 | // Copyright © 2018 Said Ozcan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct ChainedAlertAction { 12 | let title: String 13 | let style: UIAlertActionStyle 14 | let shouldTriggerNextAlert: Bool 15 | let handler: ((ChainedAlertAction) -> Void)? 16 | } 17 | -------------------------------------------------------------------------------- /ChainedAlertController/ChainedAlert/Models/ChainedAlertTree.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChainedAlertTree.swift 3 | // ChainedAlertController 4 | // 5 | // Created by Said Ozcan on 13/06/2018. 6 | // Copyright © 2018 Said Ozcan. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | indirect enum ChainedAlertTree { 12 | case alert(alert: ChainedAlert, nextAlert: ChainedAlertTree?) 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Said Özcan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChainedAlertController 2 | A mechanism for chaining UIAlertController's to each other 3 | 4 | A small iOS code snippet to create dynamically chained UIAlerts. Example usage: 5 | 6 | ```swift 7 | fileprivate lazy var firstAction: ChainedAlertTree = { 8 | let cancelAction = ChainedAlertAction(title: "Cancel", style: .cancel, shouldTriggerNextAlert: false, handler: nil) 9 | let triggerNextAction = ChainedAlertAction(title: "Trigger next", style: .default, shouldTriggerNextAlert: true, handler: nil) 10 | let alert = ChainedAlert(title: "First alert title", message: "First alert message", actions: [cancelAction, triggerNextAction]) 11 | return ChainedAlertTree.alert(alert: alert, nextAlert: secondAction) 12 | }() 13 | 14 | fileprivate lazy var secondAction: ChainedAlertTree = { 15 | let cancelAction = ChainedAlertAction(title: "Cancel", style: .cancel, shouldTriggerNextAlert: false, handler: nil) 16 | let triggerNextAction = ChainedAlertAction(title: "Trigger next", style: .default, shouldTriggerNextAlert: true, handler: nil) 17 | let alert = ChainedAlert(title: "Second action title", message: "Second action message", actions: [cancelAction, triggerNextAction]) 18 | return ChainedAlertTree.alert(alert: alert, nextAlert: thirdAction) 19 | }() 20 | 21 | fileprivate lazy var thirdAction: ChainedAlertTree = { 22 | let cancelAction = ChainedAlertAction(title: "It's enough.", style: .cancel, shouldTriggerNextAlert: false, handler: nil) 23 | let alert = ChainedAlert(title: "Third action title", message: "Third action message", actions: [cancelAction]) 24 | return ChainedAlertTree.alert(alert: alert, nextAlert: nil) 25 | }() 26 | ``` 27 | 28 | And then: 29 | ```swift 30 | showChainedAlert(alert: firstAction) 31 | ``` 32 | --------------------------------------------------------------------------------