├── .gitignore ├── FallibleKit.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── FallibleKit.xcworkspace └── contents.xcworkspacedata ├── FallibleKit ├── ErrorSet.swift ├── Fallible.swift ├── FallibleArray.swift ├── FallibleChaining.swift ├── FallibleExtensions.swift ├── FallibleKit.h └── Info.plist ├── FallibleKitTests ├── FallibleKitTests.swift └── Info.plist ├── README.md ├── README.playground ├── Contents.swift ├── Resources │ └── default.plist ├── Sources │ └── SupportCode.swift ├── contents.xcplayground └── timeline.xctimeline └── playground2md.pl /.gitignore: -------------------------------------------------------------------------------- 1 | *xcuserdata* 2 | FallibleKit.xcworkspace/xcshareddata/FallibleKit.xccheckout 3 | -------------------------------------------------------------------------------- /FallibleKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 845A438D1A1B4E7600BDA057 /* FallibleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A438C1A1B4E7600BDA057 /* FallibleArray.swift */; }; 11 | 84E347901A10A5A400994F65 /* FallibleKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 84E3478F1A10A5A400994F65 /* FallibleKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 84E347961A10A5A500994F65 /* FallibleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84E3478A1A10A5A400994F65 /* FallibleKit.framework */; }; 13 | 84E3479D1A10A5A500994F65 /* FallibleKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E3479C1A10A5A500994F65 /* FallibleKitTests.swift */; }; 14 | 84E347B31A10A5B800994F65 /* Fallible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E347AF1A10A5B800994F65 /* Fallible.swift */; }; 15 | 84E347B41A10A5B800994F65 /* FallibleChaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E347B01A10A5B800994F65 /* FallibleChaining.swift */; }; 16 | 84E347B51A10A5B800994F65 /* ErrorSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E347B11A10A5B800994F65 /* ErrorSet.swift */; }; 17 | 84E347B61A10A5B800994F65 /* FallibleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E347B21A10A5B800994F65 /* FallibleExtensions.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 84E347971A10A5A500994F65 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 84E347811A10A5A400994F65 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 84E347891A10A5A400994F65; 26 | remoteInfo = FallibleKit; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 845A438C1A1B4E7600BDA057 /* FallibleArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FallibleArray.swift; sourceTree = ""; }; 32 | 84E3478A1A10A5A400994F65 /* FallibleKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FallibleKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 84E3478E1A10A5A400994F65 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 84E3478F1A10A5A400994F65 /* FallibleKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FallibleKit.h; sourceTree = ""; }; 35 | 84E347951A10A5A500994F65 /* FallibleKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FallibleKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 84E3479B1A10A5A500994F65 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 84E3479C1A10A5A500994F65 /* FallibleKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FallibleKitTests.swift; sourceTree = ""; }; 38 | 84E347AF1A10A5B800994F65 /* Fallible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fallible.swift; sourceTree = ""; }; 39 | 84E347B01A10A5B800994F65 /* FallibleChaining.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FallibleChaining.swift; sourceTree = ""; }; 40 | 84E347B11A10A5B800994F65 /* ErrorSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorSet.swift; sourceTree = ""; }; 41 | 84E347B21A10A5B800994F65 /* FallibleExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FallibleExtensions.swift; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 84E347861A10A5A400994F65 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | 84E347921A10A5A500994F65 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | 84E347961A10A5A500994F65 /* FallibleKit.framework in Frameworks */, 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | 84E347801A10A5A400994F65 = { 64 | isa = PBXGroup; 65 | children = ( 66 | 84E3478C1A10A5A400994F65 /* FallibleKit */, 67 | 84E347991A10A5A500994F65 /* FallibleKitTests */, 68 | 84E3478B1A10A5A400994F65 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 84E3478B1A10A5A400994F65 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 84E3478A1A10A5A400994F65 /* FallibleKit.framework */, 76 | 84E347951A10A5A500994F65 /* FallibleKitTests.xctest */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 84E3478C1A10A5A400994F65 /* FallibleKit */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 84E3478F1A10A5A400994F65 /* FallibleKit.h */, 85 | 84E347AF1A10A5B800994F65 /* Fallible.swift */, 86 | 84E347B01A10A5B800994F65 /* FallibleChaining.swift */, 87 | 84E347B11A10A5B800994F65 /* ErrorSet.swift */, 88 | 84E347B21A10A5B800994F65 /* FallibleExtensions.swift */, 89 | 845A438C1A1B4E7600BDA057 /* FallibleArray.swift */, 90 | 84E3478D1A10A5A400994F65 /* Supporting Files */, 91 | ); 92 | path = FallibleKit; 93 | sourceTree = ""; 94 | }; 95 | 84E3478D1A10A5A400994F65 /* Supporting Files */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 84E3478E1A10A5A400994F65 /* Info.plist */, 99 | ); 100 | name = "Supporting Files"; 101 | sourceTree = ""; 102 | }; 103 | 84E347991A10A5A500994F65 /* FallibleKitTests */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 84E3479C1A10A5A500994F65 /* FallibleKitTests.swift */, 107 | 84E3479A1A10A5A500994F65 /* Supporting Files */, 108 | ); 109 | path = FallibleKitTests; 110 | sourceTree = ""; 111 | }; 112 | 84E3479A1A10A5A500994F65 /* Supporting Files */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 84E3479B1A10A5A500994F65 /* Info.plist */, 116 | ); 117 | name = "Supporting Files"; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXHeadersBuildPhase section */ 123 | 84E347871A10A5A400994F65 /* Headers */ = { 124 | isa = PBXHeadersBuildPhase; 125 | buildActionMask = 2147483647; 126 | files = ( 127 | 84E347901A10A5A400994F65 /* FallibleKit.h in Headers */, 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | /* End PBXHeadersBuildPhase section */ 132 | 133 | /* Begin PBXNativeTarget section */ 134 | 84E347891A10A5A400994F65 /* FallibleKit */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = 84E347A01A10A5A500994F65 /* Build configuration list for PBXNativeTarget "FallibleKit" */; 137 | buildPhases = ( 138 | 84E347851A10A5A400994F65 /* Sources */, 139 | 84E347861A10A5A400994F65 /* Frameworks */, 140 | 84E347871A10A5A400994F65 /* Headers */, 141 | 84E347881A10A5A400994F65 /* Resources */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = FallibleKit; 148 | productName = FallibleKit; 149 | productReference = 84E3478A1A10A5A400994F65 /* FallibleKit.framework */; 150 | productType = "com.apple.product-type.framework"; 151 | }; 152 | 84E347941A10A5A500994F65 /* FallibleKitTests */ = { 153 | isa = PBXNativeTarget; 154 | buildConfigurationList = 84E347A31A10A5A500994F65 /* Build configuration list for PBXNativeTarget "FallibleKitTests" */; 155 | buildPhases = ( 156 | 84E347911A10A5A500994F65 /* Sources */, 157 | 84E347921A10A5A500994F65 /* Frameworks */, 158 | 84E347931A10A5A500994F65 /* Resources */, 159 | ); 160 | buildRules = ( 161 | ); 162 | dependencies = ( 163 | 84E347981A10A5A500994F65 /* PBXTargetDependency */, 164 | ); 165 | name = FallibleKitTests; 166 | productName = FallibleKitTests; 167 | productReference = 84E347951A10A5A500994F65 /* FallibleKitTests.xctest */; 168 | productType = "com.apple.product-type.bundle.unit-test"; 169 | }; 170 | /* End PBXNativeTarget section */ 171 | 172 | /* Begin PBXProject section */ 173 | 84E347811A10A5A400994F65 /* Project object */ = { 174 | isa = PBXProject; 175 | attributes = { 176 | LastSwiftUpdateCheck = 0700; 177 | LastUpgradeCheck = 0700; 178 | ORGANIZATIONNAME = Architechies; 179 | TargetAttributes = { 180 | 84E347891A10A5A400994F65 = { 181 | CreatedOnToolsVersion = 6.1; 182 | }; 183 | 84E347941A10A5A500994F65 = { 184 | CreatedOnToolsVersion = 6.1; 185 | }; 186 | }; 187 | }; 188 | buildConfigurationList = 84E347841A10A5A400994F65 /* Build configuration list for PBXProject "FallibleKit" */; 189 | compatibilityVersion = "Xcode 3.2"; 190 | developmentRegion = English; 191 | hasScannedForEncodings = 0; 192 | knownRegions = ( 193 | en, 194 | ); 195 | mainGroup = 84E347801A10A5A400994F65; 196 | productRefGroup = 84E3478B1A10A5A400994F65 /* Products */; 197 | projectDirPath = ""; 198 | projectRoot = ""; 199 | targets = ( 200 | 84E347891A10A5A400994F65 /* FallibleKit */, 201 | 84E347941A10A5A500994F65 /* FallibleKitTests */, 202 | ); 203 | }; 204 | /* End PBXProject section */ 205 | 206 | /* Begin PBXResourcesBuildPhase section */ 207 | 84E347881A10A5A400994F65 /* Resources */ = { 208 | isa = PBXResourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | 84E347931A10A5A500994F65 /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXResourcesBuildPhase section */ 222 | 223 | /* Begin PBXSourcesBuildPhase section */ 224 | 84E347851A10A5A400994F65 /* Sources */ = { 225 | isa = PBXSourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | 84E347B31A10A5B800994F65 /* Fallible.swift in Sources */, 229 | 845A438D1A1B4E7600BDA057 /* FallibleArray.swift in Sources */, 230 | 84E347B41A10A5B800994F65 /* FallibleChaining.swift in Sources */, 231 | 84E347B61A10A5B800994F65 /* FallibleExtensions.swift in Sources */, 232 | 84E347B51A10A5B800994F65 /* ErrorSet.swift in Sources */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | 84E347911A10A5A500994F65 /* Sources */ = { 237 | isa = PBXSourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | 84E3479D1A10A5A500994F65 /* FallibleKitTests.swift in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXSourcesBuildPhase section */ 245 | 246 | /* Begin PBXTargetDependency section */ 247 | 84E347981A10A5A500994F65 /* PBXTargetDependency */ = { 248 | isa = PBXTargetDependency; 249 | target = 84E347891A10A5A400994F65 /* FallibleKit */; 250 | targetProxy = 84E347971A10A5A500994F65 /* PBXContainerItemProxy */; 251 | }; 252 | /* End PBXTargetDependency section */ 253 | 254 | /* Begin XCBuildConfiguration section */ 255 | 84E3479E1A10A5A500994F65 /* Debug */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BOOL_CONVERSION = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | CURRENT_PROJECT_VERSION = 1; 275 | ENABLE_STRICT_OBJC_MSGSEND = YES; 276 | ENABLE_TESTABILITY = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu99; 278 | GCC_DYNAMIC_NO_PIC = NO; 279 | GCC_OPTIMIZATION_LEVEL = 0; 280 | GCC_PREPROCESSOR_DEFINITIONS = ( 281 | "DEBUG=1", 282 | "$(inherited)", 283 | ); 284 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 285 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 286 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 287 | GCC_WARN_UNDECLARED_SELECTOR = YES; 288 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 289 | GCC_WARN_UNUSED_FUNCTION = YES; 290 | GCC_WARN_UNUSED_VARIABLE = YES; 291 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 292 | MTL_ENABLE_DEBUG_INFO = YES; 293 | ONLY_ACTIVE_ARCH = YES; 294 | SDKROOT = iphoneos; 295 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 296 | TARGETED_DEVICE_FAMILY = "1,2"; 297 | VERSIONING_SYSTEM = "apple-generic"; 298 | VERSION_INFO_PREFIX = ""; 299 | }; 300 | name = Debug; 301 | }; 302 | 84E3479F1A10A5A500994F65 /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 307 | CLANG_CXX_LIBRARY = "libc++"; 308 | CLANG_ENABLE_MODULES = YES; 309 | CLANG_ENABLE_OBJC_ARC = YES; 310 | CLANG_WARN_BOOL_CONVERSION = YES; 311 | CLANG_WARN_CONSTANT_CONVERSION = YES; 312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 313 | CLANG_WARN_EMPTY_BODY = YES; 314 | CLANG_WARN_ENUM_CONVERSION = YES; 315 | CLANG_WARN_INT_CONVERSION = YES; 316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 317 | CLANG_WARN_UNREACHABLE_CODE = YES; 318 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 319 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 320 | COPY_PHASE_STRIP = YES; 321 | CURRENT_PROJECT_VERSION = 1; 322 | ENABLE_NS_ASSERTIONS = NO; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | GCC_C_LANGUAGE_STANDARD = gnu99; 325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 327 | GCC_WARN_UNDECLARED_SELECTOR = YES; 328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 329 | GCC_WARN_UNUSED_FUNCTION = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 332 | MTL_ENABLE_DEBUG_INFO = NO; 333 | SDKROOT = iphoneos; 334 | TARGETED_DEVICE_FAMILY = "1,2"; 335 | VALIDATE_PRODUCT = YES; 336 | VERSIONING_SYSTEM = "apple-generic"; 337 | VERSION_INFO_PREFIX = ""; 338 | }; 339 | name = Release; 340 | }; 341 | 84E347A11A10A5A500994F65 /* Debug */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | APPLICATION_EXTENSION_API_ONLY = YES; 345 | DEFINES_MODULE = YES; 346 | DYLIB_COMPATIBILITY_VERSION = 1; 347 | DYLIB_CURRENT_VERSION = 1; 348 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 349 | INFOPLIST_FILE = FallibleKit/Info.plist; 350 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 351 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 352 | PRODUCT_BUNDLE_IDENTIFIER = "com.architechies.cocoa.$(PRODUCT_NAME:rfc1034identifier)"; 353 | PRODUCT_NAME = "$(TARGET_NAME)"; 354 | SKIP_INSTALL = YES; 355 | }; 356 | name = Debug; 357 | }; 358 | 84E347A21A10A5A500994F65 /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | APPLICATION_EXTENSION_API_ONLY = YES; 362 | DEFINES_MODULE = YES; 363 | DYLIB_COMPATIBILITY_VERSION = 1; 364 | DYLIB_CURRENT_VERSION = 1; 365 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 366 | INFOPLIST_FILE = FallibleKit/Info.plist; 367 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 368 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 369 | PRODUCT_BUNDLE_IDENTIFIER = "com.architechies.cocoa.$(PRODUCT_NAME:rfc1034identifier)"; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | SKIP_INSTALL = YES; 372 | }; 373 | name = Release; 374 | }; 375 | 84E347A41A10A5A500994F65 /* Debug */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | FRAMEWORK_SEARCH_PATHS = ( 379 | "$(SDKROOT)/Developer/Library/Frameworks", 380 | "$(inherited)", 381 | ); 382 | GCC_PREPROCESSOR_DEFINITIONS = ( 383 | "DEBUG=1", 384 | "$(inherited)", 385 | ); 386 | INFOPLIST_FILE = FallibleKitTests/Info.plist; 387 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 388 | PRODUCT_BUNDLE_IDENTIFIER = "com.architechies.cocoa.$(PRODUCT_NAME:rfc1034identifier)"; 389 | PRODUCT_NAME = "$(TARGET_NAME)"; 390 | }; 391 | name = Debug; 392 | }; 393 | 84E347A51A10A5A500994F65 /* Release */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | FRAMEWORK_SEARCH_PATHS = ( 397 | "$(SDKROOT)/Developer/Library/Frameworks", 398 | "$(inherited)", 399 | ); 400 | INFOPLIST_FILE = FallibleKitTests/Info.plist; 401 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 402 | PRODUCT_BUNDLE_IDENTIFIER = "com.architechies.cocoa.$(PRODUCT_NAME:rfc1034identifier)"; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | }; 405 | name = Release; 406 | }; 407 | /* End XCBuildConfiguration section */ 408 | 409 | /* Begin XCConfigurationList section */ 410 | 84E347841A10A5A400994F65 /* Build configuration list for PBXProject "FallibleKit" */ = { 411 | isa = XCConfigurationList; 412 | buildConfigurations = ( 413 | 84E3479E1A10A5A500994F65 /* Debug */, 414 | 84E3479F1A10A5A500994F65 /* Release */, 415 | ); 416 | defaultConfigurationIsVisible = 0; 417 | defaultConfigurationName = Release; 418 | }; 419 | 84E347A01A10A5A500994F65 /* Build configuration list for PBXNativeTarget "FallibleKit" */ = { 420 | isa = XCConfigurationList; 421 | buildConfigurations = ( 422 | 84E347A11A10A5A500994F65 /* Debug */, 423 | 84E347A21A10A5A500994F65 /* Release */, 424 | ); 425 | defaultConfigurationIsVisible = 0; 426 | defaultConfigurationName = Release; 427 | }; 428 | 84E347A31A10A5A500994F65 /* Build configuration list for PBXNativeTarget "FallibleKitTests" */ = { 429 | isa = XCConfigurationList; 430 | buildConfigurations = ( 431 | 84E347A41A10A5A500994F65 /* Debug */, 432 | 84E347A51A10A5A500994F65 /* Release */, 433 | ); 434 | defaultConfigurationIsVisible = 0; 435 | defaultConfigurationName = Release; 436 | }; 437 | /* End XCConfigurationList section */ 438 | }; 439 | rootObject = 84E347811A10A5A400994F65 /* Project object */; 440 | } 441 | -------------------------------------------------------------------------------- /FallibleKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FallibleKit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /FallibleKit/ErrorSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorSet.swift 3 | // WishList 4 | // 5 | // Created by Brent Royal-Gordon on 9/15/14. 6 | // Copyright (c) 2014 Architechies. All rights reserved. 7 | // 8 | 9 | private extension ErrorType { 10 | private var domainAndCode: (String, Int) { 11 | let nsError = self as NSError 12 | return (nsError.domain, nsError.code) 13 | } 14 | } 15 | 16 | /// An ErrorSet represents a particular set of NSError domains and codes within 17 | /// those domains. ErrorSet itself has no public API, but you can use the 18 | /// `error(domain:code:)` and `errors(domain:codes:)` functions to construct 19 | /// ErrorSets, the `|` and `|=` operators to combine error sets, and the `~=` 20 | /// operator (and thus `switch` statements) to test errors against error sets. 21 | public struct ErrorSet: SetAlgebraType { 22 | private var domainsAndCodes: [String: Set] = [:] 23 | 24 | private subscript(domain: String, code: Int) -> Bool { 25 | get { return domainsAndCodes[domain]?.contains(code) ?? false } 26 | set { 27 | if newValue { 28 | if domainsAndCodes[domain] == nil { 29 | domainsAndCodes[domain] = [code] 30 | } 31 | else { 32 | domainsAndCodes[domain]!.insert(code) 33 | } 34 | } 35 | else { 36 | domainsAndCodes[domain]?.remove(code) 37 | } 38 | } 39 | } 40 | 41 | /// Creates an empty set. 42 | /// 43 | /// - Equivalent to `[] as Self` 44 | public init() {} 45 | 46 | /// Returns `true` if `self` contains `member`. 47 | /// 48 | /// - Equivalent to `self.intersect([member]) == [member]` 49 | public func contains(member: ErrorType) -> Bool { 50 | return self[member.domainAndCode] 51 | } 52 | 53 | /// Returns the set of elements contained in `self`, in `other`, or in 54 | /// both `self` and `other`. 55 | public func union(var other: ErrorSet) -> ErrorSet { 56 | other.unionInPlace(self) 57 | return other 58 | } 59 | 60 | /// Returns the set of elements contained in both `self` and `other`. 61 | public func intersect(var other: ErrorSet) -> ErrorSet { 62 | other.intersectInPlace(self) 63 | return other 64 | } 65 | 66 | /// Returns the set of elements contained in `self` or in `other`, 67 | /// but not in both `self` and `other`. 68 | public func exclusiveOr(other: ErrorSet) -> ErrorSet { 69 | var both = union(other) 70 | for (domain, codes) in both.domainsAndCodes { 71 | for code in codes { 72 | if self[domain, code] && other[domain, code] { 73 | both[domain, code] = false 74 | } 75 | } 76 | } 77 | return other 78 | } 79 | 80 | /// If `member` is not already contained in `self`, inserts it. 81 | /// 82 | /// - Equivalent to `self.unionInPlace([member])` 83 | /// - Postcondition: `self.contains(member)` 84 | public mutating func insert(member: ErrorType) { 85 | self[member.domainAndCode] = true 86 | } 87 | 88 | /// If `member` is contained in `self`, removes and returns it. 89 | /// Otherwise, removes all elements subsumed by `member` and returns 90 | /// `nil`. 91 | /// 92 | /// - Postcondition: `self.intersect([member]).isEmpty` 93 | public mutating func remove(member: ErrorType) -> ErrorType? { 94 | guard self[member.domainAndCode] else { 95 | return nil 96 | } 97 | 98 | self[member.domainAndCode] = false 99 | return member 100 | } 101 | 102 | /// Insert all elements of `other` into `self`. 103 | /// 104 | /// - Equivalent to replacing `self` with `self.union(other)`. 105 | /// - Postcondition: `self.isSupersetOf(other)` 106 | public mutating func unionInPlace(other: ErrorSet) { 107 | for (domain, otherCodes) in other.domainsAndCodes { 108 | var selfCodes = domainsAndCodes[domain] ?? [] 109 | selfCodes.unionInPlace(otherCodes) 110 | domainsAndCodes[domain] = selfCodes 111 | } 112 | } 113 | 114 | /// Removes all elements of `self` that are not also present in 115 | /// `other`. 116 | /// 117 | /// - Equivalent to replacing `self` with `self.intersect(other)` 118 | /// - Postcondition: `self.isSubsetOf(other)` 119 | public mutating func intersectInPlace(other: ErrorSet) { 120 | for domain in domainsAndCodes.keys { 121 | if let otherCodes = other.domainsAndCodes[domain] { 122 | domainsAndCodes[domain]!.intersectInPlace(otherCodes) 123 | } 124 | else { 125 | domainsAndCodes[domain] = nil 126 | } 127 | } 128 | } 129 | 130 | /// Replaces `self` with a set containing all elements contained in 131 | /// either `self` or `other`, but not both. 132 | /// 133 | /// - Equivalent to replacing `self` with `self.exclusiveOr(other)` 134 | public mutating func exclusiveOrInPlace(other: ErrorSet) { 135 | // No clue how to do this in-place 136 | self = exclusiveOr(other) 137 | } 138 | 139 | /// Returns true iff `self.contains(e)` is `false` for all `e`. 140 | public var isEmpty: Bool { 141 | return domainsAndCodes.indexOf { (_, codes) in !codes.isEmpty } == nil 142 | } 143 | 144 | /// Returns `true` iff `a` subsumes `b`. 145 | /// 146 | /// - Equivalent to `([a] as Self).isSupersetOf([b])` 147 | public static func element(a: ErrorType, subsumes b: ErrorType) -> Bool { 148 | if case let ((d1, c1), (d2, c2)) = (a.domainAndCode, b.domainAndCode) where d1 == d2 && c1 == c2 { 149 | return true 150 | } 151 | else { 152 | return false 153 | } 154 | } 155 | } 156 | 157 | public func == (lhs: ErrorSet, rhs: ErrorSet) -> Bool { 158 | return lhs.exclusiveOr(rhs).isEmpty 159 | } 160 | 161 | /// The `|=` operator can be used to merge the errors matched by the ErrorSet on 162 | /// the right side into the ErrorSet on the left side. 163 | public func |= (inout lhs: ErrorSet, rhs: ErrorSet) { 164 | } 165 | 166 | /// The `|` operator can be used to combine the errors matched by two ErrorSets 167 | /// into a third ErrorSet. 168 | public func | (var lhs: ErrorSet, rhs: ErrorSet) -> ErrorSet { 169 | lhs |= rhs 170 | return lhs 171 | } 172 | 173 | /// The `~=` operator matches an NSError against an ErrorSet. If the NSError's 174 | /// domain and code are present in the ErrorSet, it returns true; otherwise it 175 | /// returns false. 176 | public func ~= (lhs: ErrorSet, rhs: ErrorType) -> Bool { 177 | return lhs.contains(rhs) 178 | } 179 | 180 | /// Constructor for an ErrorSet matching a specific Swift ErrorType. 181 | public func error(error: ErrorType) -> ErrorSet { 182 | var all = ErrorSet() 183 | all.insert(error) 184 | return all 185 | } 186 | 187 | /// Constructor for an ErrorSet matching one of many Swift ErrorTypes. This variant 188 | /// takes an array, and can thus match a set of types determined at runtime. 189 | public func errors(errors: [ErrorType]) -> ErrorSet { 190 | var all: ErrorSet = [] 191 | errors.map(all.insert) 192 | return all 193 | } 194 | 195 | // Constructor for an ErrorSet matching one of many Swift ErrorTypes. This variant 196 | // is variadic, and thus has a cleaner syntax. 197 | public func errors(errors e: ErrorType...) -> ErrorSet { 198 | return errors(e) 199 | } 200 | 201 | /// Constructor for an ErrorSet matching specific error domains and codes. 202 | public func errors(domain domain: String, codes: Set) -> ErrorSet { 203 | var all = ErrorSet() 204 | all.domainsAndCodes = [domain: codes] 205 | return all 206 | } 207 | -------------------------------------------------------------------------------- /FallibleKit/Fallible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Fallible.swift 3 | // WishList 4 | // 5 | // Created by Brent Royal-Gordon on 9/3/14. 6 | // Copyright (c) 2014 Architechies. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Shorthand for a successful Fallible, which is awkward to construct 12 | /// normally. 13 | public let Succeeded = Fallible(succeeded: ()) 14 | 15 | /// Represents the result of an operation which may succeed or fail. Upon success, 16 | /// a Fallible contains a `value` of type T; upon failure, it contains an `error` 17 | /// of type NSError. 18 | /// 19 | /// Note that, for various technical reasons, you generally shouldn't construct a 20 | /// Fallible using `Fallible.Success` and `Fallible.Failure`; instead use the 21 | /// `init(succeeded:)` and `init(failed:)` initializers. 22 | public enum Fallible: CustomStringConvertible { 23 | case Success (ResultType) 24 | case Failure (ErrorType) 25 | 26 | /// Constructs a successful Fallible result, hiding the ugliness of the internal 27 | /// `Reference` (which is needed to avoid a feature that Swift 1.1 doesn't 28 | /// implement). 29 | public init(succeeded value: ResultType) { 30 | self = .Success(value) 31 | } 32 | 33 | /// Constructs a failed Fallible result. This is a good place to drop a 34 | /// breakpoint if you're trying to find the source of a failure. 35 | public init(failed error: ErrorType) { 36 | self = .Failure(error) 37 | } 38 | 39 | /// Constructs a Fallible result from a Swift throwing operation. 40 | public init(@autoclosure catches operation: Void throws -> ResultType) { 41 | do { 42 | self.init(succeeded: try operation()) 43 | } 44 | catch { 45 | self.init(failed: error) 46 | } 47 | } 48 | 49 | /// Returns true if the operation succeeded. 50 | public var succeeded: Bool { 51 | switch self { 52 | case .Success: 53 | return true 54 | case .Failure: 55 | return false 56 | } 57 | } 58 | 59 | /// Returns true if the operation failed. 60 | public var failed: Bool { 61 | return !succeeded 62 | } 63 | 64 | /// Returns the value if the Fallible operation succeeded, or nil if it failed. 65 | public var value: ResultType? { 66 | switch self { 67 | case .Success(let value): 68 | return value 69 | case .Failure: 70 | return nil 71 | } 72 | } 73 | 74 | /// Returns the error if the Fallible operation failed, or nil if it succeeded. 75 | public var error: ErrorType? { 76 | switch self { 77 | case .Success: 78 | return nil 79 | case .Failure(let error): 80 | return error 81 | } 82 | } 83 | 84 | public var description: String { 85 | switch self { 86 | case .Success(let value): 87 | return "Success(\(value))" 88 | case .Failure(let error): 89 | return "Failure(\(error))" 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /FallibleKit/FallibleArray.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FallibleArray.swift 3 | // FallibleKit 4 | // 5 | // Created by Brent Royal-Gordon on 11/18/14. 6 | // Copyright (c) 2014 Architechies. All rights reserved. 7 | // 8 | 9 | /// Takes an array of Fallible objects and returns an array of all successful 10 | /// values, ignoring failures. 11 | public func filterFailures(array: [Fallible]) -> [T] { 12 | return (anySucceeded(array) => recover { _ in Fallible(succeeded: []) }).value! 13 | } 14 | 15 | /// Takes an array of Fallible objects and divides it into two separate arrays: 16 | /// one containing the values of successful operations, the other containing the 17 | /// errors of failed operations. 18 | public func classifyFallibles(array: [Fallible]) -> (successes: [T], failures: [ErrorType]) { 19 | return array.reduce((successes: [] as [T], failures: [] as [ErrorType])) { (tuple, elem) in 20 | if let value = elem.value { 21 | return (successes: tuple.successes + [value], failures: tuple.failures) 22 | } 23 | else { 24 | return (successes: tuple.successes, failures: tuple.failures + [elem.error!]) 25 | } 26 | } 27 | } 28 | 29 | /// Takes an array of Fallible objects and returns a successful 30 | /// Fallible<[T]> if all of the operations succeeded, or failure otherwise. If more 31 | /// than one operation failed, the error domain will be FallibleError.Domain, the 32 | /// code will be FallibleError.MultipleErrors.rawValue, and the list of errors 33 | /// may be found in FallibleError.DetailedErrorsKey. 34 | public func allSucceeded(array: [Fallible]) -> Fallible<[T]> { 35 | let (successes, failures) = classifyFallibles(array) 36 | if let error = FallibleError.errorFromErrors(failures) { 37 | return Fallible(failed: error) 38 | } 39 | return Fallible(succeeded: successes) 40 | } 41 | 42 | /// Takes an array of Fallible objects and returns a successful 43 | /// Fallible<[T]> containing the values of all successful operations, or failure 44 | /// if none of them succeeded. An empty array counts as a success. If more 45 | /// than one operation failed, the error domain will be FallibleError.Domain, the 46 | /// code will be FallibleError.MultipleErrors.rawValue, and the list of errors 47 | /// may be found in FallibleError.DetailedErrorsKey. 48 | public func anySucceeded(array: [Fallible]) -> Fallible<[T]> { 49 | let (successes, failures) = classifyFallibles(array) 50 | if successes.isEmpty { 51 | if let error = FallibleError.errorFromErrors(failures) { 52 | return Fallible(failed: error) 53 | } 54 | } 55 | 56 | return Fallible(succeeded: successes) 57 | } 58 | 59 | /// Enum for error codes generated by Fallible itself, not any specific Fallible 60 | /// version of an API. 61 | // 62 | // N.B. This is not yet an ErrorType because ErrorType doesn't support userInfo yet. 63 | public enum FallibleError: Int { 64 | /// Error domain for Fallible errors. 65 | public static var Domain = "FallibleKit" 66 | /// Key for the errors contained in a .MultipleErrors error. Note that this should 67 | /// actually be interchangeable with NSDetailedErrorsKey. 68 | public static var DetailedErrorsKey = "NSDetailedErrors" 69 | 70 | /// Error code indicating that several different errors occurred. Check 71 | /// DetailedErrorsKey for specifics. 72 | case MultipleErrors = 1560 73 | 74 | /// Returns a FallibleError instance if the error object represents a 75 | /// FallibleError, or nil otherwise. 76 | public init?(error: NSError) { 77 | if error.domain != FallibleError.Domain { 78 | return nil 79 | } 80 | self.init(rawValue: error.code) 81 | } 82 | 83 | var localizedDescription: String { 84 | switch self { 85 | case .MultipleErrors: 86 | return NSLocalizedString("Several operations failed.", comment: "") 87 | } 88 | } 89 | 90 | static func errorsFromError(error: ErrorType?) -> [NSError] { 91 | if let error = error as? NSError { 92 | if let code = FallibleError(error: error) { 93 | switch code { 94 | case .MultipleErrors: 95 | return error.userInfo[DetailedErrorsKey] as! [NSError] 96 | } 97 | } 98 | return [error as NSError] 99 | } 100 | else { 101 | return [] 102 | } 103 | } 104 | 105 | static func errorFromErrors(errors: [ErrorType]) -> NSError? { 106 | let flatErrors: [NSError] = lazy(errors).map { errorsFromError($0) }.reduce([], combine: +) 107 | 108 | if flatErrors.count > 1 { 109 | let code = FallibleError.MultipleErrors 110 | let userInfo: [NSObject: AnyObject] = [ 111 | DetailedErrorsKey: flatErrors, 112 | NSLocalizedDescriptionKey: code.localizedDescription 113 | ] 114 | return NSError(domain: Domain, code: code.rawValue, userInfo: userInfo as [NSObject : AnyObject]) 115 | } 116 | else { 117 | return flatErrors.first.map { $0 as NSError } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /FallibleKit/FallibleChaining.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FallibleChaining.swift 3 | // WishList 4 | // 5 | // Created by Brent Royal-Gordon on 9/14/14. 6 | // Copyright (c) 2014 Architechies. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | infix operator => { associativity left precedence 100 } 12 | 13 | /// The function chaining operator is used to link several operations together into a 14 | /// pipeline. You begin the pipeline with a value, then use the `=>` operator 15 | /// one or more times to pass it through a series of transformations. Each 16 | /// transformation is a function, and each function in the chain must accept a 17 | /// single parameter of the type returned by the previous function. The end of the 18 | /// chain returns the value after all the transformations are applied. 19 | /// 20 | /// Although this operator can be used with any value and any function, it is 21 | /// specifically intended to be used with Fallible values. See `then`, 22 | /// `mapSuccess`, `recover`, and `mapFailure` for examples of operations that can 23 | /// be used with function chaining and Fallible values. 24 | public func => (input: T, transform: T -> U) -> U { 25 | return transform(input) 26 | } 27 | 28 | /// The function chaining operator is used to link several operations together into a 29 | /// pipeline. You begin the pipeline with a value, then use the `=>` operator 30 | /// one or more times to pass it through a series of transformations. 31 | /// 32 | /// This variant is used to build a function chain when you don't yet have the 33 | /// value you're going to transform. One reason you might do this is to build up a 34 | /// completion function for an asynchronous operation. 35 | /// 36 | /// Although this operator can be used with any value and any function, it is 37 | /// specifically intended to be used with Fallible values. See `then`, 38 | /// `mapSuccess`, `recover`, and `mapFailure` for examples of operations that can 39 | /// be used with function chaining and Fallible values. 40 | public func => (first: T -> U, second: U -> V)(input: T) -> V { 41 | return input => first => second 42 | } 43 | 44 | /// When used with the function chaining operator (`=>`), chains two Fallible 45 | /// operations together, ensuring that the second runs only if the first succeeded. 46 | /// It can be thought of as similar to `Optional.flatMap`. 47 | /// 48 | /// If the previous Fallible operation succeeded, `then` extracts the value and 49 | /// passes it to the `operation` function. It then returns the result of 50 | /// `operation`, which should itself be Fallible, though perhaps with a 51 | /// different ResultType. 52 | /// 53 | /// If the current Fallible operation failed, `then` does not run `operation`, 54 | /// but instead propagates the current operation's error into a new Fallible 55 | /// result. 56 | public func then(operation: T -> Fallible)(input: Fallible) -> Fallible { 57 | switch input { 58 | case .Success(let value): 59 | return operation(value) 60 | case .Failure(let error): 61 | return Fallible.Failure (error) 62 | } 63 | } 64 | 65 | /// When used with the function chaining operator (`=>`), transforms the value of a 66 | /// successful Fallible operation, possibly changing its type in the process. Use 67 | /// `mapSuccess` instead of `then` when the operation you want to perform is 68 | /// not itself Fallible. 69 | public func mapSuccess(transform: T -> U) -> Fallible -> Fallible { 70 | return then { Fallible.Success(transform($0)) } 71 | } 72 | 73 | /// When used with the function chaining operator (`=>`), uses the value of a 74 | /// successful Fallible operation without changing the operation in any way. 75 | public func useSuccess(usage usage: T -> Void) -> Fallible -> Fallible { 76 | return mapSuccess { value in 77 | usage(value) 78 | return value 79 | } 80 | } 81 | 82 | /// When used with the function chaining operator (`=>`), chains two Fallible 83 | /// operations together, ensuring that the second runs only if the first failed. This 84 | /// allows you to potentially correct a failure and return to the main successful 85 | /// flow of control through your code. 86 | /// 87 | /// If the current Fallible operation failed, `recover` extracts the error and 88 | /// passes it to the `recovery` function. It then returns the result of 89 | /// `recovery`, which should itself be Fallible. (If the result is not Fallible, you 90 | /// want `correct` instead.) 91 | /// 92 | /// If the current Fallible operation succeeded, `recover` does not run 93 | /// `recovery`, instead returning the current result unchanged. 94 | /// 95 | /// To recover selectively from only certain errors, see `recover(from:recovery:) 96 | public func recover(recovery: ErrorType -> Fallible)(input: Fallible) -> Fallible { 97 | switch input { 98 | case .Success: 99 | return input 100 | case .Failure(let error): 101 | return recovery(error) 102 | } 103 | } 104 | 105 | /// When used with the function chaining operator (`=>`), chains two Fallible 106 | /// operations together, ensuring that the second runs only if the first failed. This 107 | /// allows you to potentially correct a failure and return to the main successful 108 | /// flow of control through your code. 109 | /// 110 | /// If the current Fallible operation failed, `recover` extracts the error and 111 | /// passes it to the `recovery` function. It then returns the result of 112 | /// `recovery`, which should itself be Fallible. (If the result is not Fallible, you 113 | /// want `correct` instead.) 114 | /// 115 | /// If the current Fallible operation succeeded, `recover` does not run 116 | /// `recovery`, instead returning the current result unchanged. 117 | /// 118 | /// The `recover(from:recovery:)` variant only runs the `recovery` function if the 119 | /// error matches the particular error set specified in the parameters. All other 120 | /// failures pass through `recover(from:recovery:)` unchanged. To try to recover 121 | /// from all failures, see `recover(_:)`. To construct error sets, see 122 | /// `error(domain:code:)`, `errors(domain:codes:)`, and the error set combining 123 | /// operator, `|`. 124 | public func recover(from errorSet: ErrorSet, recovery: ErrorType -> Fallible) -> Fallible -> Fallible { 125 | return recover { error in 126 | if errorSet ~= error { 127 | return recovery(error) 128 | } 129 | else { 130 | return Fallible.Failure(error) 131 | } 132 | } 133 | } 134 | 135 | /// When used with the function chaining operator (`=>`), transforms the error of a 136 | /// failed Fallible operation. This is often used to wrap an NSError in a more 137 | /// general, domain-specific NSError, or to add additional domain-specific 138 | /// information to the NSError. Use `mapFailure` instead of `recover` when the 139 | /// operation does not try to fix the error, but merely substitutes a different error 140 | /// for the original. 141 | /// 142 | /// To convert only certain errors, see `mapFailure(from:transform:)`. 143 | public func mapFailure(transform: ErrorType -> ErrorType) -> Fallible -> Fallible { 144 | return recover { error in Fallible.Failure (transform(error)) } 145 | } 146 | 147 | /// When used with the Fallible chaining operator (`=>`), transforms the error of a 148 | /// failed Fallible operation. This is often used to wrap an NSError in a more 149 | /// general, domain-specific NSError, or to add additional domain-specific 150 | /// information to the NSError. Use `mapFailure` instead of `recover` when the 151 | /// operation does not try to fix the error, but merely substitutes a different error 152 | /// for the original. 153 | /// 154 | /// The `mapFailure(from:transform:)` variant only runs the `transform` 155 | /// function if the error matches the particular error set specified in the 156 | /// parameters. All other failures pass through `mapFailure(from:transform:)` 157 | /// unchanged. To transform all failures, see `mapFailure(transform:)`. To 158 | /// construct error sets, see `error(domain:code:)`, `errors(domain:codes:)`, and 159 | /// the error set combining operator, `|`. 160 | public func mapFailure(from errorSet: ErrorSet, transform: ErrorType -> ErrorType) -> Fallible -> Fallible { 161 | return recover(from: errorSet) { error in Fallible.Failure (transform(error)) } 162 | } 163 | 164 | /// When used with the Fallible chaining operator (`=>`), unconditionally replaces 165 | /// the result of a failed Fallible operation with a successful result. The correction 166 | /// closure cannot fail, and thus the closure passed to this function does not 167 | /// return a fallible type. If your attempted correction can itself fail, you want 168 | /// `recover` instead. 169 | /// 170 | /// To correct only certain errors, see `correct(from:correction:)`. 171 | public func correct(correction: ErrorType -> T) -> Fallible -> Fallible { 172 | return recover { error in Fallible(succeeded: correction(error)) } 173 | } 174 | 175 | /// When used with the Fallible chaining operator (`=>`), unconditionally replaces 176 | /// the result of a failed Fallible operation with a successful result. The correction 177 | /// closure cannot fail, and thus the closure passed to this function does not 178 | /// return a fallible type. If your attempted correction can itself fail, you want 179 | /// `recover` instead. 180 | /// 181 | /// `correct(from:correction:)` might be used to replace missing data with a new, 182 | /// empty data structure, while allowing other errors to be handled normally. 183 | /// 184 | /// The `correct(from:correction:)` variant only runs the `correction` 185 | /// function if the error matches the particular error set specified in the 186 | /// parameters. All other failures pass through `correct(from:correction:)` 187 | /// unchanged. To transform all failures, see `correct(correction:)`. To 188 | /// construct error sets, see `error(domain:code:)`, `errors(domain:codes:)`, and 189 | /// the error set combining operator, `|`. 190 | public func correct(from errorSet: ErrorSet, correction: ErrorType -> T) -> Fallible -> Fallible { 191 | return recover(from: errorSet) { error in Fallible(succeeded: correction(error)) } 192 | } 193 | 194 | /// When used with the Fallible chaining operator (`=>`), removes a level of 195 | /// nesting from a Fallible operation, transforming a `Fallible>` into 196 | /// a `Fallible`. Thus, if either `param` or `param.value!` is a `Fallible.Failure`, 197 | /// the flattened `Fallible` will be a `Failure`; otherwise it will be a `Success`. 198 | public func flatten(value: Fallible>) -> Fallible { 199 | return value => then { value in value } 200 | } 201 | 202 | /// When used with the Fallible chaining operator (`=>`), uses the error from a 203 | /// failed Fallible operation without changing it in any way. 204 | /// 205 | /// To use only certain errors, see `useFailure(from:usage:)`. 206 | public func useFailure(usage: ErrorType -> Void) -> Fallible -> Fallible { 207 | return mapFailure { error in 208 | usage(error) 209 | return error 210 | } 211 | } 212 | 213 | /// When used with the Fallible chaining operator (`=>`), uses the error from a 214 | /// failed Fallible operation without changing it in any way. 215 | /// 216 | /// The `useFailure(from:usage:)` variant only runs the `usage` 217 | /// function if the error matches the particular error set specified in the 218 | /// parameters. All other failures pass through `useFailure(usage:)` 219 | /// unprocessed. To use all failures, see `useFailure(usage:)`. To 220 | /// construct error sets, see `error(domain:code:)`, `errors(domain:codes:)`, and 221 | /// the error set combining operator, `|`. 222 | public func useFailure(from errorSet: ErrorSet, usage: ErrorType -> Void) -> Fallible -> Fallible { 223 | return mapFailure(from: errorSet) { error in 224 | usage(error) 225 | return error 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /FallibleKit/FallibleExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FallibleExtensions.swift 3 | // WishList 4 | // 5 | // Created by Brent Royal-Gordon on 9/3/14. 6 | // Copyright (c) 2014 Architechies. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | public extension NSURL { 13 | func readData() -> Fallible { 14 | return Fallible(catches: try NSData(contentsOfURL: self, options: [])) 15 | } 16 | 17 | func writeData(data: NSData, options: NSDataWritingOptions) -> Fallible { 18 | return Fallible(catches: try data.writeToURL(self, options: options)) 19 | } 20 | 21 | func writeData(data: NSData) -> Fallible { 22 | return writeData(data, options: .DataWritingAtomic) 23 | } 24 | } 25 | 26 | public extension NSFileCoordinator { 27 | func coordinateReadingItemAtURL(url: NSURL, options: NSFileCoordinatorReadingOptions, byAccessor reader: (NSURL!) -> T) -> Fallible { 28 | var error: NSError? 29 | var value: T? 30 | 31 | self.coordinateReadingItemAtURL(url, options: options, error: &error) { (url) -> Void in 32 | value = reader(url) 33 | } 34 | return value.map { Fallible(succeeded: $0) } ?? Fallible(failed: error!) 35 | } 36 | 37 | func coordinateWritingItemAtURL(url: NSURL, options: NSFileCoordinatorWritingOptions, byAccessor writer: (NSURL!) -> T) -> Fallible { 38 | var error: NSError? 39 | var value: T? 40 | 41 | self.coordinateWritingItemAtURL(url, options: options, error: &error) { (url) -> Void in 42 | value = writer(url) 43 | } 44 | return value.map { Fallible(succeeded: $0) } ?? Fallible(failed: error!) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /FallibleKit/FallibleKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // FallibleKit.h 3 | // FallibleKit 4 | // 5 | // Created by Brent Royal-Gordon on 11/9/14. 6 | // Copyright (c) 2014 Architechies. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for FallibleKit. 12 | FOUNDATION_EXPORT double FallibleKitVersionNumber; 13 | 14 | //! Project version string for FallibleKit. 15 | FOUNDATION_EXPORT const unsigned char FallibleKitVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /FallibleKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /FallibleKitTests/FallibleKitTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FallibleKitTests.swift 3 | // FallibleKitTests 4 | // 5 | // Created by Brent Royal-Gordon on 11/9/14. 6 | // Copyright (c) 2014 Architechies. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class FallibleKitTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /FallibleKitTests/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FallibleKit 2 | ======= 3 | import FallibleKit 4 | FallibleKit is an implementation of functional-style error handling for Swift. Functional-style error handling allows you to easily handle errors by chaining operations together; FallibleKit makes this readable and understandable. 5 | 6 | This version of FallibleKit is written in Swift 2.0. [A Swift 1.2 branch is also available.](https://github.com/brentdax/FallibleKit/tree/v1.x) It only includes iOS targets, although FallibleKit should be able to run on OS X. 7 | 8 | Fallible 9 | ------ 10 | 11 | A `Fallible` value represents a value of type `T` that was created in a way that might fail. For instance, an `NSData` read from a file that might not exist would be a `Fallible`. If the operation succeeded, the Fallible instance will contain a piece of data; if it failed, the Fallible instance will contain an NSError describing the failure. 12 | 13 | A Fallible instance may have succeeded or it may have failed: 14 | 15 | let okay = Fallible(succeeded: "Hello world!") 16 | let oops = Fallible(failed: NSCocoaError.FileReadNoSuchFileError) 17 | 18 | If an operation does not return any useful value, but merely wants to report whether it succeeded or failed (and the error if it failed), it should have a return type of `Fallible`. The syntax for constructing a succeeded `Fallible` is a little awkward, so FallibleKit includes a `Succeeded` constant you can return instead. 19 | 20 | A successful Fallible instance will have its value in the `value` property; a failed one will have `nil` there. Similarly, a failed Fallible instance will have its error in its `error` property; a successful Fallible instance will have `nil` there. You can also check the `succeeded` and `failed` properties for a simple boolean test. 21 | 22 | okay.succeeded // true 23 | okay.value // "Hello world!" 24 | okay.failed // false 25 | okay.error // nil 26 | 27 | oops.succeeded // false 28 | oops.value // nil 29 | oops.failed // true 30 | oops.error // Foundation.NSCocoaError 31 | 32 | To use Fallible, simply wrap the type you would return when successful in `Fallible<>`, then use the `succeeded:` and `failed:` constructors as needed. 33 | 34 | extension UserData { 35 | static func fromPropertyList(plist: AnyObject) -> Fallible { 36 | if let plist = plist as? NSDictionary, 37 | let notes = plist["notes"] as? [String] { 38 | // Great, the file had everything we needed 39 | return Fallible(succeeded: UserData(notes: notes)) 40 | } 41 | else { 42 | // Oops, something was missing 43 | return Fallible(failed: NSCocoaError.FileReadCorruptFileError) 44 | } 45 | } 46 | } 47 | 48 | Fallibles and Framework APIs 49 | --------------------- 50 | 51 | When you're writing your own APIs, you should have them return (or, for asynchronous calls, pass) `Fallible` types directly, but you have no such luxury with standard Foundation, Cocoa Touch, or Cocoa APIs. If they use Swift's standard `throws` feature, the `Fallible(catches:)` initializer can help you convert their results to Fallible instances. 52 | 53 | public func readPropertyListWithToFallible(URL: NSURL) -> Fallible { 54 | let dataResult = Fallible(catches: try NSData(contentsOfURL: URL, options: [])) 55 | 56 | if let data = dataResult.value { 57 | return Fallible(catches: try NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)) 58 | } 59 | else { 60 | // Have to convert this to a Fallible 61 | return Fallible(failed: dataResult.error!) 62 | } 63 | } 64 | 65 | FallibleKit also includes extensions which create Fallible versions of a few APIs which are, for some reason, awkward to use with FallibleKit. Additions to these extensions are always welcome. 66 | 67 | public func readPropertyList(URL: NSURL) -> Fallible { 68 | let dataResult = URL.readData() // Here 69 | 70 | if let data = dataResult.value { 71 | return Fallible(catches: try NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)) 72 | } 73 | else { 74 | return Fallible(failed: dataResult.error!) 75 | } 76 | } 77 | 78 | Fallible Chaining 79 | ------------ 80 | 81 | It's possible to work with Fallible instances purely by using `if` and `if let` statements with the four properties described above, but Fallible gets really powerful if you use its chaining operations. 82 | 83 | Fallible chaining treats your code as a pipeline. Each step in the chain runs in a certain condition and transforms the result of the previous step in a certain way. For instance, you might read in a piece of raw data, correct a specific error by substituting default data, transform the data into a model object, set a property on your root view controller to your new model object, and display any unhandled errors that came up during this whole process: 84 | 85 | readPropertyList(userDataURL) => 86 | // This step is run only if we've encounted the specified error. 87 | // It runs another operation that might fail. 88 | recover(from: error(NSCocoaError.FileReadNoSuchFileError)) { error in 89 | readPropertyList(emptyDataURL) 90 | } => 91 | // This step is run only if we've successfully read a plist. 92 | // It takes the plist and performs another operation that might fail. 93 | then { plist in 94 | UserData.fromPropertyList(plist) 95 | } => 96 | // This step is run only if the previous step ran successfully. 97 | // It uses the UserData instance without replacing it. 98 | useSuccess { userData in 99 | rootViewController.userData = userData 100 | } => 101 | // This step is run only if the previous steps produced an error. 102 | // It uses the error without replacing it. 103 | useFailure { error in 104 | rootViewController.presentError(error) 105 | } 106 | 107 | Operations supported by Fallible include: 108 | 109 | * `then`: If previous steps succeeded, perform an additional step that might fail. 110 | * `mapSuccess`: If previous steps succeeded, perform additional processing that cannot fail. 111 | * `useSuccess`: If previous steps succeeded, use their value without replacing it. 112 | * `recover`: If previous steps failed, perform an additional step that might fail, but also might succeed. 113 | * `mapFailure`: If previous steps failed, replace the error with a different error. 114 | * `correct` If previous steps failed, perform a corrective action that cannot fail. 115 | * `useFailure`: If previous steps failed, use their error without replacing it. 116 | 117 | The error-related operations all support an optional `from:` parameter which specifies particular errors you want to handle. 118 | 119 | Copyright 120 | ------- 121 | 122 | Copyright © 2014-15 Architechies. 123 | 124 | This library is licensed under the MIT License: 125 | 126 | > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 127 | > 128 | > The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 129 | > 130 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 131 | 132 | Author 133 | ---- 134 | 135 | Brent Royal-Gordon, , @brentdax on GitHub and Twitter. 136 | 137 | -------------------------------------------------------------------------------- /README.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | FallibleKit 3 | ======= 4 | */ 5 | import FallibleKit 6 | /*: 7 | FallibleKit is an implementation of functional-style error handling for Swift. Functional-style error handling allows you to easily handle errors by chaining operations together; FallibleKit makes this readable and understandable. 8 | 9 | This version of FallibleKit is written in Swift 2.0. [A Swift 1.2 branch is also available.](https://github.com/brentdax/FallibleKit/tree/v1.x) It only includes iOS targets, although FallibleKit should be able to run on OS X. 10 | 11 | Fallible 12 | ------ 13 | 14 | A `Fallible` value represents a value of type `T` that was created in a way that might fail. For instance, an `NSData` read from a file that might not exist would be a `Fallible`. If the operation succeeded, the Fallible instance will contain a piece of data; if it failed, the Fallible instance will contain an NSError describing the failure. 15 | 16 | A Fallible instance may have succeeded or it may have failed: 17 | */ 18 | 19 | let okay = Fallible(succeeded: "Hello world!") 20 | let oops = Fallible(failed: NSCocoaError.FileReadNoSuchFileError) 21 | 22 | /*: 23 | If an operation does not return any useful value, but merely wants to report whether it succeeded or failed (and the error if it failed), it should have a return type of `Fallible`. The syntax for constructing a succeeded `Fallible` is a little awkward, so FallibleKit includes a `Succeeded` constant you can return instead. 24 | 25 | A successful Fallible instance will have its value in the `value` property; a failed one will have `nil` there. Similarly, a failed Fallible instance will have its error in its `error` property; a successful Fallible instance will have `nil` there. You can also check the `succeeded` and `failed` properties for a simple boolean test. 26 | */ 27 | 28 | okay.succeeded // true 29 | okay.value // "Hello world!" 30 | okay.failed // false 31 | okay.error // nil 32 | 33 | oops.succeeded // false 34 | oops.value // nil 35 | oops.failed // true 36 | oops.error // Foundation.NSCocoaError 37 | 38 | /*: 39 | To use Fallible, simply wrap the type you would return when successful in `Fallible<>`, then use the `succeeded:` and `failed:` constructors as needed. 40 | */ 41 | 42 | extension UserData { 43 | static func fromPropertyList(plist: AnyObject) -> Fallible { 44 | if let plist = plist as? NSDictionary, 45 | let notes = plist["notes"] as? [String] { 46 | // Great, the file had everything we needed 47 | return Fallible(succeeded: UserData(notes: notes)) 48 | } 49 | else { 50 | // Oops, something was missing 51 | return Fallible(failed: NSCocoaError.FileReadCorruptFileError) 52 | } 53 | } 54 | } 55 | 56 | /*: 57 | Fallibles and Framework APIs 58 | --------------------- 59 | 60 | When you're writing your own APIs, you should have them return (or, for asynchronous calls, pass) `Fallible` types directly, but you have no such luxury with standard Foundation, Cocoa Touch, or Cocoa APIs. If they use Swift's standard `throws` feature, the `Fallible(catches:)` initializer can help you convert their results to Fallible instances. 61 | */ 62 | 63 | public func readPropertyListWithToFallible(URL: NSURL) -> Fallible { 64 | let dataResult = Fallible(catches: try NSData(contentsOfURL: URL, options: [])) 65 | 66 | if let data = dataResult.value { 67 | return Fallible(catches: try NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)) 68 | } 69 | else { 70 | // Have to convert this to a Fallible 71 | return Fallible(failed: dataResult.error!) 72 | } 73 | } 74 | 75 | /*: 76 | FallibleKit also includes extensions which create Fallible versions of a few APIs which are, for some reason, awkward to use with FallibleKit. Additions to these extensions are always welcome. 77 | */ 78 | 79 | public func readPropertyList(URL: NSURL) -> Fallible { 80 | let dataResult = URL.readData() // Here 81 | 82 | if let data = dataResult.value { 83 | return Fallible(catches: try NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)) 84 | } 85 | else { 86 | return Fallible(failed: dataResult.error!) 87 | } 88 | } 89 | 90 | /*: 91 | Fallible Chaining 92 | ------------ 93 | 94 | It's possible to work with Fallible instances purely by using `if` and `if let` statements with the four properties described above, but Fallible gets really powerful if you use its chaining operations. 95 | 96 | Fallible chaining treats your code as a pipeline. Each step in the chain runs in a certain condition and transforms the result of the previous step in a certain way. For instance, you might read in a piece of raw data, correct a specific error by substituting default data, transform the data into a model object, set a property on your root view controller to your new model object, and display any unhandled errors that came up during this whole process: 97 | */ 98 | 99 | readPropertyList(userDataURL) => 100 | // This step is run only if we've encounted the specified error. 101 | // It runs another operation that might fail. 102 | recover(from: error(NSCocoaError.FileReadNoSuchFileError)) { error in 103 | readPropertyList(emptyDataURL) 104 | } => 105 | // This step is run only if we've successfully read a plist. 106 | // It takes the plist and performs another operation that might fail. 107 | then { plist in 108 | UserData.fromPropertyList(plist) 109 | } => 110 | // This step is run only if the previous step ran successfully. 111 | // It uses the UserData instance without replacing it. 112 | useSuccess { userData in 113 | rootViewController.userData = userData 114 | } => 115 | // This step is run only if the previous steps produced an error. 116 | // It uses the error without replacing it. 117 | useFailure { error in 118 | rootViewController.presentError(error) 119 | } 120 | 121 | /*: 122 | Operations supported by Fallible include: 123 | 124 | * `then`: If previous steps succeeded, perform an additional step that might fail. 125 | * `mapSuccess`: If previous steps succeeded, perform additional processing that cannot fail. 126 | * `useSuccess`: If previous steps succeeded, use their value without replacing it. 127 | * `recover`: If previous steps failed, perform an additional step that might fail, but also might succeed. 128 | * `mapFailure`: If previous steps failed, replace the error with a different error. 129 | * `correct` If previous steps failed, perform a corrective action that cannot fail. 130 | * `useFailure`: If previous steps failed, use their error without replacing it. 131 | 132 | The error-related operations all support an optional `from:` parameter which specifies particular errors you want to handle. 133 | 134 | Copyright 135 | ------- 136 | 137 | Copyright © 2014-15 Architechies. 138 | 139 | This library is licensed under the MIT License: 140 | 141 | > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 142 | > 143 | > The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 144 | > 145 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 146 | 147 | Author 148 | ---- 149 | 150 | Brent Royal-Gordon, , @brentdax on GitHub and Twitter. 151 | 152 | */ 153 | -------------------------------------------------------------------------------- /README.playground/Resources/default.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | notes 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.playground/Sources/SupportCode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This file (and all other Swift source files in the Sources directory of this playground) will be precompiled into a framework which is automatically made available to README.playground. 3 | // 4 | 5 | import Foundation 6 | import XCPlayground 7 | 8 | public let userDataURL = NSURL(fileURLWithPath: XCPSharedDataDirectoryPath).URLByAppendingPathComponent("userData").URLByAppendingPathExtension("plist") 9 | public let emptyDataURL = NSBundle.mainBundle().URLForResource("default", withExtension: "plist")! 10 | 11 | public struct UserData { 12 | public var notes: [String] 13 | 14 | public init(notes: [String]) { 15 | self.notes = notes 16 | } 17 | } 18 | 19 | public class MockViewController { 20 | public var userData: UserData? = nil 21 | 22 | public func presentError(error: ErrorType) { 23 | print(error) 24 | } 25 | } 26 | public var rootViewController = MockViewController() 27 | 28 | -------------------------------------------------------------------------------- /README.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /README.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /playground2md.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # playground2md.pl 4 | # FallibleKit 5 | # 6 | # Created by Brent Royal-Gordon on 6/11/15. 7 | # Copyright (c) 2015 Architechies. All rights reserved. 8 | 9 | use strict; 10 | use warnings; 11 | 12 | for my $playground_name(@ARGV) { 13 | my $in_markdown = 0; 14 | 15 | open(my $swift_file, "<", "$playground_name/Contents.swift") 16 | or die "Can't open $playground_name/Contents.swift: $!"; 17 | 18 | (my $md_name = $playground_name) =~ s/\.playground$/.md/; 19 | open(my $md_file, ">", $md_name) 20 | or die "Can't open $md_name for writing: $!"; 21 | 22 | while(<$swift_file>) { 23 | if($in_markdown) { 24 | # We're in a Markdown section 25 | if(s(^\*/$)()) { 26 | # Hit the end 27 | $in_markdown = 0; 28 | next; 29 | } 30 | # Print verbatim 31 | print $md_file $_; 32 | } 33 | else { 34 | # We're in a code section 35 | if(s(^/\*:$)()) { 36 | # Hit Markdown 37 | $in_markdown = 1; 38 | next; 39 | } 40 | # Print indented 41 | 42 | if(m(\S)) { 43 | print $md_file " ", $_; 44 | } 45 | else { 46 | print $md_file $_; 47 | } 48 | } 49 | } 50 | } 51 | --------------------------------------------------------------------------------