├── .gitignore ├── .travis.yml ├── Cartfile ├── Cartfile.resolved ├── ErrorHandler.png ├── ErrorHandler.podspec ├── ErrorHandler ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── Alamofire │ ├── AFErrorStatusCodeMatcher.swift │ └── ErrorHandler+AFExtensions.swift │ └── Core │ ├── ErrorHandler.swift │ ├── Matchers.swift │ └── NSErrorMatcher.swift ├── Example ├── ErrorHandler.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── ErrorHandler-Example.xcscheme ├── ErrorHandler.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── ErrorHandler │ ├── AFError+Extensions.swift │ ├── AlertManager.swift │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── DefaultErrorHandler.swift │ ├── ErrorHandlingTableViewController.swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift ├── Podfile ├── Podfile.lock ├── Pods │ ├── Alamofire │ │ ├── LICENSE │ │ ├── README.md │ │ └── Source │ │ │ ├── AFError.swift │ │ │ ├── Alamofire.swift │ │ │ ├── AlamofireExtended.swift │ │ │ ├── CachedResponseHandler.swift │ │ │ ├── DispatchQueue+Alamofire.swift │ │ │ ├── EventMonitor.swift │ │ │ ├── HTTPHeaders.swift │ │ │ ├── HTTPMethod.swift │ │ │ ├── MultipartFormData.swift │ │ │ ├── MultipartUpload.swift │ │ │ ├── NetworkReachabilityManager.swift │ │ │ ├── Notifications.swift │ │ │ ├── OperationQueue+Alamofire.swift │ │ │ ├── ParameterEncoder.swift │ │ │ ├── ParameterEncoding.swift │ │ │ ├── Protector.swift │ │ │ ├── RedirectHandler.swift │ │ │ ├── Request.swift │ │ │ ├── RequestInterceptor.swift │ │ │ ├── RequestTaskMap.swift │ │ │ ├── Response.swift │ │ │ ├── ResponseSerialization.swift │ │ │ ├── Result+Alamofire.swift │ │ │ ├── RetryPolicy.swift │ │ │ ├── ServerTrustEvaluation.swift │ │ │ ├── Session.swift │ │ │ ├── SessionDelegate.swift │ │ │ ├── URLConvertible+URLRequestConvertible.swift │ │ │ ├── URLEncodedFormEncoder.swift │ │ │ ├── URLRequest+Alamofire.swift │ │ │ ├── URLSessionConfiguration+Alamofire.swift │ │ │ └── Validation.swift │ ├── Local Podspecs │ │ └── ErrorHandler.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── ErrorHandler.xcscheme │ └── Target Support Files │ │ ├── Alamofire │ │ ├── Alamofire-Info.plist │ │ ├── Alamofire-dummy.m │ │ ├── Alamofire-prefix.pch │ │ ├── Alamofire-umbrella.h │ │ ├── Alamofire.modulemap │ │ ├── Alamofire.xcconfig │ │ └── Info.plist │ │ ├── ErrorHandler │ │ ├── ErrorHandler-Info.plist │ │ ├── ErrorHandler-dummy.m │ │ ├── ErrorHandler-prefix.pch │ │ ├── ErrorHandler-umbrella.h │ │ ├── ErrorHandler.modulemap │ │ ├── ErrorHandler.xcconfig │ │ └── Info.plist │ │ ├── Pods-ErrorHandler_Example │ │ ├── Info.plist │ │ ├── Pods-ErrorHandler_Example-Info.plist │ │ ├── Pods-ErrorHandler_Example-acknowledgements.markdown │ │ ├── Pods-ErrorHandler_Example-acknowledgements.plist │ │ ├── Pods-ErrorHandler_Example-dummy.m │ │ ├── Pods-ErrorHandler_Example-frameworks.sh │ │ ├── Pods-ErrorHandler_Example-resources.sh │ │ ├── Pods-ErrorHandler_Example-umbrella.h │ │ ├── Pods-ErrorHandler_Example.debug.xcconfig │ │ ├── Pods-ErrorHandler_Example.modulemap │ │ └── Pods-ErrorHandler_Example.release.xcconfig │ │ └── Pods-ErrorHandler_Tests │ │ ├── Info.plist │ │ ├── Pods-ErrorHandler_Tests-Info.plist │ │ ├── Pods-ErrorHandler_Tests-acknowledgements.markdown │ │ ├── Pods-ErrorHandler_Tests-acknowledgements.plist │ │ ├── Pods-ErrorHandler_Tests-dummy.m │ │ ├── Pods-ErrorHandler_Tests-frameworks.sh │ │ ├── Pods-ErrorHandler_Tests-resources.sh │ │ ├── Pods-ErrorHandler_Tests-umbrella.h │ │ ├── Pods-ErrorHandler_Tests.debug.xcconfig │ │ ├── Pods-ErrorHandler_Tests.modulemap │ │ └── Pods-ErrorHandler_Tests.release.xcconfig └── Tests │ ├── AFErrorStatusCodeMatcherTests.swift │ ├── ClosureErrorMatcherTests.swift │ ├── ErrorHandler-AFExtensionsTests.swift │ ├── ErrorHandlerTests.swift │ ├── ErrorTypeMatcherTests.swift │ ├── Info.plist │ └── NSErrorMatcherTests.swift ├── LICENSE ├── Package.swift ├── README.md └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.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 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | 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 68 | fastlane/test_output 69 | 70 | .build 71 | DerivedData 72 | /.previous-build 73 | xcuserdata 74 | .DS_Store 75 | *~ 76 | \#* 77 | .\#* 78 | .*.sw[nop] 79 | *.xcscmblueprint 80 | /default.profraw 81 | *.xcodeproj 82 | Utilities/Docker/*.tar.gz 83 | .swiftpm 84 | Package.resolved 85 | /build 86 | *.pyc 87 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode8.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/ErrorHandler.xcworkspace -scheme ErrorHandler-Example -destination "OS=10.3.1,name=iPhone 7 Plus" ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" ~> 4.1 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "4.5.0" 2 | -------------------------------------------------------------------------------- /ErrorHandler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Workable/swift-error-handler/783c888b50067ef543e5d0358f7dc42897072823/ErrorHandler.png -------------------------------------------------------------------------------- /ErrorHandler.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "ErrorHandler" 3 | s.version = "0.8.4" 4 | s.swift_versions = ['4.2', '5.0'] 5 | s.summary = "Elegant and flexible error handling for Swift" 6 | s.description = <<-DESC 7 | > Elegant and flexible error handling for Swift 8 | 9 | ErrorHandler enables expressing complex error handling logic with a few lines of code using a memorable fluent API. 10 | DESC 11 | s.homepage = "https://github.com/Workable/swift-error-handler" 12 | s.license = { :type => "MIT", :file => "LICENSE" } 13 | s.authors = { "Kostas Kremizas" => "kremizask@gmail.com", 14 | "Eleni Papanikolopoulou" => "eleni.papanikolopoulou@gmail.com" } 15 | s.ios.deployment_target = '10.0' 16 | s.osx.deployment_target = '10.12' 17 | s.tvos.deployment_target = '10.0' 18 | s.watchos.deployment_target = '3.0' 19 | s.source = { :git => "https://github.com/Workable/swift-error-handler.git", :tag => s.version.to_s } 20 | 21 | s.default_subspec = "Core" 22 | 23 | s.subspec "Core" do |ss| 24 | ss.source_files = "ErrorHandler/Classes/Core/**/*" 25 | ss.framework = "Foundation" 26 | end 27 | 28 | s.subspec "Alamofire" do |ss| 29 | ss.source_files = "ErrorHandler/Classes/Alamofire/**/*" 30 | ss.dependency "Alamofire", "~> 5" 31 | ss.dependency "ErrorHandler/Core" 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /ErrorHandler/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Workable/swift-error-handler/783c888b50067ef543e5d0358f7dc42897072823/ErrorHandler/Assets/.gitkeep -------------------------------------------------------------------------------- /ErrorHandler/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Workable/swift-error-handler/783c888b50067ef543e5d0358f7dc42897072823/ErrorHandler/Classes/.gitkeep -------------------------------------------------------------------------------- /ErrorHandler/Classes/Alamofire/AFErrorStatusCodeMatcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorHandler-AFExtensions.swift 3 | // Workable SA 4 | // 5 | // Created by Kostas Kremizas on {TODAY}. 6 | // Copyright © 2017 Workable SA. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | #if !COCOAPODS 12 | import ErrorHandler 13 | #endif 14 | 15 | public class AFErrorStatusCodeMatcher: ErrorMatcher { 16 | 17 | private let validRange: Range 18 | 19 | public init(_ range: Range) { 20 | self.validRange = range 21 | } 22 | 23 | public init(statusCode: Int) { 24 | self.validRange = statusCode.. Bool { 28 | guard let error = error as? AFError else { return false } 29 | guard case .responseValidationFailed(reason: let validationFailureReason) = error else { return false } 30 | guard case .unacceptableStatusCode(code: let statusCode) = validationFailureReason else { return false } 31 | return validRange ~= statusCode 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ErrorHandler/Classes/Alamofire/ErrorHandler+AFExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorHandler+AFExtensions.swift 3 | // ErrorHandler+AFExtensions 4 | // 5 | // Created by Kostas Kremizas on 30/08/2017. 6 | // Copyright © 2017 Workable SA. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if !COCOAPODS 11 | import ErrorHandler 12 | #endif 13 | 14 | public extension ErrorHandler { 15 | 16 | func onAFError(withStatus statusCode: Int, do action: @escaping ErrorAction) -> ErrorHandler { 17 | let matcher = AFErrorStatusCodeMatcher(statusCode: statusCode) 18 | return self.on(matcher, do: action) 19 | } 20 | 21 | func onAFError(withStatus range: Range, do action: @escaping ErrorAction) -> ErrorHandler { 22 | let matcher = AFErrorStatusCodeMatcher(range) 23 | return self.on(matcher, do: action) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ErrorHandler/Classes/Core/Matchers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorMatcher.swift 3 | // workable 4 | // 5 | // Created by Kostas Kremizas on 09/05/16. 6 | // Copyright © 2016 Workable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | An `ErrorMatcher` is an alternative to using `matches` closures when adding `ErrorActions` to an `ErrorHandler`. An `ErrorMatcher` is considered to match an error when it's `matches` function returns `true` for this error. `ErrorMatcher`s are used to match errors with the `ErrorHandler`'s `on(_ matcher: ErrorMatcher, do action: @escaping ErrorAction)` method. 13 | 14 | `ErrorMatcher`s can be combined using || and && operators. 15 | For example: 16 | 17 | ``` 18 | let notConnectedMatcher = NSErrorMatcher(domain: NSURLErrorDomain, code: NSURLErrorNotConnectedToInternet) 19 | 20 | let connectionLostMatcher = NSErrorMatcher(domain: NSURLErrorDomain, code: NSURLErrorNetworkConnectionLost) 21 | 22 | let offlineMatcher = notConnectedMatcher || connectionLostMatcher 23 | ``` 24 | Error matchers have the additional benefit compared to `matches` closures that they describe matching logic in a way that can be more naturally reused. i.e. it is more natural in `Swift` to reuse Types than free functions and closures as the unit of composition in Swift is the type. 25 | */ 26 | public protocol ErrorMatcher { 27 | 28 | /** 29 | The `ErrorMatcher` is considered to match the error if this function returns true. 30 | - Returns: `true` if the matcher matches the `error` otherwise `false` 31 | */ 32 | func matches(_ error: Error) -> Bool 33 | } 34 | 35 | /** 36 | A generic `ErrorMatcher` over type `E` that `matches` an error if the error `is` `T` 37 | */ 38 | public class ErrorTypeMatcher: ErrorMatcher { 39 | public init() {} 40 | public func matches(_ error: Error) -> Bool { 41 | return error is E 42 | } 43 | } 44 | 45 | /** 46 | An `ErrorMatcher` that wraps a `matches` closure 47 | */ 48 | public class ClosureErrorMatcher: ErrorMatcher { 49 | private let matches: (Error) -> Bool 50 | 51 | public init(matches: @escaping (Error) -> Bool) { 52 | self.matches = matches 53 | } 54 | 55 | public func matches(_ error: Error) -> Bool { 56 | return self.matches(error) 57 | } 58 | } 59 | 60 | public func && (lhs: ErrorMatcher, rhs: ErrorMatcher) -> ErrorMatcher { 61 | return ClosureErrorMatcher(matches: { (error) -> Bool in 62 | return lhs.matches(error) && rhs.matches(error) 63 | }) 64 | } 65 | 66 | public func || (lhs: ErrorMatcher, rhs: ErrorMatcher) -> ErrorMatcher { 67 | return ClosureErrorMatcher(matches: { (error) -> Bool in 68 | return lhs.matches(error) || rhs.matches(error) 69 | }) 70 | } 71 | 72 | -------------------------------------------------------------------------------- /ErrorHandler/Classes/Core/NSErrorMatcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLErrorMatcher.swift 3 | // ErrorHandler-iOS 4 | // 5 | // Created by Eleni Papanikolopoulou on 02/08/2017. 6 | // Copyright © 2017 Workable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct NSErrorMatcher: ErrorMatcher { 12 | 13 | let domain: String 14 | var code: Int? 15 | 16 | public func matches(_ error: Error) -> Bool { 17 | let nsError = error as NSError 18 | 19 | guard nsError.domain == domain else { return false } 20 | 21 | if let code = code { 22 | return code == nsError.code 23 | } 24 | 25 | return true 26 | } 27 | 28 | public init(domain: String, code: Int?){ 29 | self.domain = domain 30 | self.code = code 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Example/ErrorHandler.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ErrorHandler.xcodeproj/xcshareddata/xcschemes/ErrorHandler-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 81 | 87 | 88 | 89 | 90 | 91 | 92 | 98 | 100 | 106 | 107 | 108 | 109 | 111 | 112 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /Example/ErrorHandler.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/ErrorHandler.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/ErrorHandler/AFError+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AFError+Extensions.swift 3 | // ErrorHandler_Example 4 | // 5 | // Created by Kostas Kremizas on 01/09/2017. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension AFError { 13 | init(statusCode: Int) { 14 | self = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: statusCode)) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/ErrorHandler/AlertManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertManager.swift 3 | // Example-iOS 4 | // 5 | // Created by Eleni Papanikolopoulou on 05/08/2017. 6 | // Copyright © 2017 Workable. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIApplication { 12 | class func topVC() -> UIViewController { 13 | // cheating, I know 14 | // Normally if you want the handler to have state you should subclass it. 15 | return UIApplication.shared.keyWindow!.rootViewController! 16 | } 17 | } 18 | 19 | extension UIAlertController { 20 | func show() { 21 | DispatchQueue.main.async { 22 | UIApplication.topVC().present(self, animated: true, completion: nil) 23 | } 24 | } 25 | } 26 | 27 | class AlertManager { 28 | class func showAlert(title: String = "", message: String = "") { 29 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 30 | let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil) 31 | alert.addAction(defaultAction) 32 | alert.show() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/ErrorHandler/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example-iOS 4 | // 5 | // Created by Kostas Kremizas on 24/07/2017. 6 | // Copyright © 2017 Workable. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/ErrorHandler/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/ErrorHandler/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 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Example/ErrorHandler/DefaultErrorHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultErrorHandler.swift 3 | // Example-iOS 4 | // 5 | // Created by Eleni Papanikolopoulou on 05/08/2017. 6 | // Copyright © 2017 Workable. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ErrorHandler 11 | 12 | protocol LoggableError: Error { 13 | var loggableDescripiton: String { get } 14 | } 15 | 16 | enum CustomError: Error, LoggableError { 17 | case unknown 18 | case parsingError 19 | 20 | var loggableDescripiton: String { 21 | return String(describing: self) 22 | } 23 | } 24 | 25 | extension ErrorHandler { 26 | static var `default` : ErrorHandler { 27 | 28 | return ErrorHandler() 29 | .onAFError(withStatus: 400..<451, do: { (error) -> MatchingPolicy in 30 | AlertManager.showAlert(title: "Client error") 31 | return .continueMatching 32 | }) 33 | .onAFError(withStatus: 500..<512, do: { (_) -> MatchingPolicy in 34 | AlertManager.showAlert(title: "Server Error") 35 | return .continueMatching 36 | }) 37 | .onAFError(withStatus: 401, do: { (error) -> MatchingPolicy in 38 | AlertManager.showAlert(title: "Unauthorized errror", message: "You are not authorized for this action.") 39 | return .stopMatching 40 | }) 41 | .onNSError(domain: NSURLErrorDomain, code: NSURLErrorNotConnectedToInternet, do: { (error) -> MatchingPolicy in 42 | AlertManager.showAlert(title: "You seem to be offline", message: "Check your connection and try again.") 43 | return .continueMatching 44 | }) 45 | .on(CustomError.parsingError, do: { (error) -> MatchingPolicy in 46 | AlertManager.showAlert(title: "Parsing Error") 47 | return .continueMatching 48 | }) 49 | .tag(AFErrorStatusCodeMatcher(400..<512), with: "http") 50 | .onNoMatch(do: { (error) -> MatchingPolicy in 51 | AlertManager.showAlert(title: "Unknown error", message: "Oops.. Something went wrong.") 52 | return .continueMatching 53 | }) 54 | .always(do: { (error) -> MatchingPolicy in 55 | print((error as? LoggableError)?.loggableDescripiton ?? error.localizedDescription) 56 | return .continueMatching 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Example/ErrorHandler/ErrorHandlingTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorHandlingTableViewController.swift 3 | // Example-iOS 4 | // 5 | // Created by Eleni Papanikolopoulou on 05/08/2017. 6 | // Copyright © 2017 Workable. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ErrorHandler 11 | import Alamofire 12 | 13 | class ErrorHandlingTableViewController: UITableViewController { 14 | 15 | private let errorsAndTitles: [(Error, String)] = [ 16 | (AFError(statusCode: 401), "401 Unauthorized error"), 17 | (NSError(domain: NSURLErrorDomain, code: NSURLErrorNotConnectedToInternet, userInfo: [NSLocalizedDescriptionKey: "Not connected to the internet"]) , "Offline Error"), 18 | (AFError(statusCode: 400), "400 (4xx client errors)"), 19 | (AFError(statusCode: 402), "401 (4xx client errors)"), 20 | (AFError(statusCode: 500), "500"), 21 | (CustomError.parsingError, "Parsing error") 22 | ] 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | } 27 | 28 | // MARK: - Table view data source 29 | 30 | override func numberOfSections(in tableView: UITableView) -> Int { 31 | return 1 32 | } 33 | 34 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 35 | return errorsAndTitles.count 36 | } 37 | 38 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 39 | let cell = tableView.dequeueReusableCell(withIdentifier: "errorCell", for: indexPath) 40 | cell.textLabel?.text = errorsAndTitles[indexPath.row].1 41 | cell.selectionStyle = .none 42 | return cell 43 | } 44 | 45 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 46 | 47 | let error: Error = errorsAndTitles[indexPath.row].0 48 | 49 | switch indexPath.row { 50 | case 3: 51 | // Lets say thatin this case as a single exception in our app we want to provide for 401 errors 52 | // the more generic error message about 4xx errors 53 | ErrorHandler.default 54 | .onAFError(withStatus: 401, do: { (error) -> MatchingPolicy in 55 | return .continueMatching 56 | }) 57 | .handle(error) 58 | case 4: 59 | // Let's say that in this case we also want to do some additional action if it is any invalid http status error 60 | ErrorHandler.default 61 | .on(tag: "http", do: { (_) -> MatchingPolicy in 62 | print("Oh dear! They tapped on the 5th row and also got an invalid http status!") 63 | return .continueMatching 64 | }) 65 | .handle(error) 66 | default: 67 | ErrorHandler.default 68 | .handle(error) 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Example/ErrorHandler/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/ErrorHandler/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/ErrorHandler/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ErrorHandler 4 | // 5 | // Created by kremizask on 09/01/2017. 6 | // Copyright (c) 2017 kremizask. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '10.0' 2 | source 'https://github.com/Workable/PodSpecs.git' 3 | source 'https://github.com/CocoaPods/Specs.git' 4 | use_frameworks! 5 | 6 | target 'ErrorHandler_Example' do 7 | 8 | pod 'ErrorHandler', :path => "../" 9 | pod 'ErrorHandler/Alamofire', :path => "../" 10 | 11 | target 'ErrorHandler_Tests' do 12 | inherit! :search_paths 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.0.2) 3 | - ErrorHandler (0.8.4): 4 | - ErrorHandler/Core (= 0.8.4) 5 | - ErrorHandler/Alamofire (0.8.4): 6 | - Alamofire (~> 5) 7 | - ErrorHandler/Core 8 | - ErrorHandler/Core (0.8.4) 9 | 10 | DEPENDENCIES: 11 | - ErrorHandler (from `../`) 12 | - ErrorHandler/Alamofire (from `../`) 13 | 14 | SPEC REPOS: 15 | https://github.com/CocoaPods/Specs.git: 16 | - Alamofire 17 | 18 | EXTERNAL SOURCES: 19 | ErrorHandler: 20 | :path: "../" 21 | 22 | SPEC CHECKSUMS: 23 | Alamofire: 3ba7a4db18b4f62c4a1c0e1cb39d7f3d52e10ada 24 | ErrorHandler: f7e8d8236e305978f856eb52bc2f19435072906b 25 | 26 | PODFILE CHECKSUM: 1ecd38147b811789385bbac850686caa2f83077b 27 | 28 | COCOAPODS: 1.8.4 29 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Reference to `Session.default` for quick bootstrapping and examples. 26 | public let AF = Session.default 27 | 28 | /// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate. 29 | let version = "5.0.2" 30 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/AlamofireExtended.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlamofireExtended.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Type that acts as a generic extension point for all `AlamofireExtended` types. 26 | public struct AlamofireExtension { 27 | /// Stores the type or meta-type of any extended type. 28 | public private(set) var type: ExtendedType 29 | 30 | /// Create an instance from the provided value. 31 | /// 32 | /// - Parameter type: Instance being extended. 33 | public init(_ type: ExtendedType) { 34 | self.type = type 35 | } 36 | } 37 | 38 | /// Protocol describing the `af` extension points for Alamofire extended types. 39 | public protocol AlamofireExtended { 40 | /// Type being extended. 41 | associatedtype ExtendedType 42 | 43 | /// Static Alamofire extension point. 44 | static var af: AlamofireExtension.Type { get set } 45 | /// Instance Alamofire extension point. 46 | var af: AlamofireExtension { get set } 47 | } 48 | 49 | public extension AlamofireExtended { 50 | /// Static Alamofire extension point. 51 | static var af: AlamofireExtension.Type { 52 | get { return AlamofireExtension.self } 53 | set {} 54 | } 55 | 56 | /// Instance Alamofire extension point. 57 | var af: AlamofireExtension { 58 | get { return AlamofireExtension(self) } 59 | set {} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/CachedResponseHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CachedResponseHandler.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that handles whether the data task should store the HTTP response in the cache. 28 | public protocol CachedResponseHandler { 29 | /// Determines whether the HTTP response should be stored in the cache. 30 | /// 31 | /// The `completion` closure should be passed one of three possible options: 32 | /// 33 | /// 1. The cached response provided by the server (this is the most common use case). 34 | /// 2. A modified version of the cached response (you may want to modify it in some way before caching). 35 | /// 3. A `nil` value to prevent the cached response from being stored in the cache. 36 | /// 37 | /// - Parameters: 38 | /// - task: The data task whose request resulted in the cached response. 39 | /// - response: The cached response to potentially store in the cache. 40 | /// - completion: The closure to execute containing cached response, a modified response, or `nil`. 41 | func dataTask(_ task: URLSessionDataTask, 42 | willCacheResponse response: CachedURLResponse, 43 | completion: @escaping (CachedURLResponse?) -> Void) 44 | } 45 | 46 | // MARK: - 47 | 48 | /// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached 49 | /// response. 50 | public struct ResponseCacher { 51 | /// Defines the behavior of the `ResponseCacher` type. 52 | public enum Behavior { 53 | /// Stores the cached response in the cache. 54 | case cache 55 | /// Prevents the cached response from being stored in the cache. 56 | case doNotCache 57 | /// Modifies the cached response before storing it in the cache. 58 | case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?) 59 | } 60 | 61 | /// Returns a `ResponseCacher` with a follow `Behavior`. 62 | public static let cache = ResponseCacher(behavior: .cache) 63 | /// Returns a `ResponseCacher` with a do not follow `Behavior`. 64 | public static let doNotCache = ResponseCacher(behavior: .doNotCache) 65 | 66 | /// The `Behavior` of the `ResponseCacher`. 67 | public let behavior: Behavior 68 | 69 | /// Creates a `ResponseCacher` instance from the `Behavior`. 70 | /// 71 | /// - Parameter behavior: The `Behavior`. 72 | public init(behavior: Behavior) { 73 | self.behavior = behavior 74 | } 75 | } 76 | 77 | extension ResponseCacher: CachedResponseHandler { 78 | public func dataTask(_ task: URLSessionDataTask, 79 | willCacheResponse response: CachedURLResponse, 80 | completion: @escaping (CachedURLResponse?) -> Void) { 81 | switch behavior { 82 | case .cache: 83 | completion(response) 84 | case .doNotCache: 85 | completion(nil) 86 | case let .modify(closure): 87 | let response = closure(task, response) 88 | completion(response) 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | /// Execute the provided closure after a `TimeInterval`. 30 | /// 31 | /// - Parameters: 32 | /// - delay: `TimeInterval` to delay execution. 33 | /// - closure: Closure to execute. 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so 26 | /// `HTTPMethod.get != HTTPMethod(rawValue: "get")`. 27 | /// 28 | /// See https://tools.ietf.org/html/rfc7231#section-4.3 29 | public struct HTTPMethod: RawRepresentable, Equatable, Hashable { 30 | /// `CONNECT` method. 31 | public static let connect = HTTPMethod(rawValue: "CONNECT") 32 | /// `DELETE` method. 33 | public static let delete = HTTPMethod(rawValue: "DELETE") 34 | /// `GET` method. 35 | public static let get = HTTPMethod(rawValue: "GET") 36 | /// `HEAD` method. 37 | public static let head = HTTPMethod(rawValue: "HEAD") 38 | /// `OPTIONS` method. 39 | public static let options = HTTPMethod(rawValue: "OPTIONS") 40 | /// `PATCH` method. 41 | public static let patch = HTTPMethod(rawValue: "PATCH") 42 | /// `POST` method. 43 | public static let post = HTTPMethod(rawValue: "POST") 44 | /// `PUT` method. 45 | public static let put = HTTPMethod(rawValue: "PUT") 46 | /// `TRACE` method. 47 | public static let trace = HTTPMethod(rawValue: "TRACE") 48 | 49 | public let rawValue: String 50 | 51 | public init(rawValue: String) { 52 | self.rawValue = rawValue 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/MultipartUpload.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultipartUpload.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Internal type which encapsulates a `MultipartFormData` upload. 28 | final class MultipartUpload { 29 | lazy var result = Result { try build() } 30 | 31 | let isInBackgroundSession: Bool 32 | let multipartFormData: MultipartFormData 33 | let encodingMemoryThreshold: UInt64 34 | let request: URLRequestConvertible 35 | let fileManager: FileManager 36 | 37 | init(isInBackgroundSession: Bool, 38 | encodingMemoryThreshold: UInt64, 39 | request: URLRequestConvertible, 40 | multipartFormData: MultipartFormData) { 41 | self.isInBackgroundSession = isInBackgroundSession 42 | self.encodingMemoryThreshold = encodingMemoryThreshold 43 | self.request = request 44 | fileManager = multipartFormData.fileManager 45 | self.multipartFormData = multipartFormData 46 | } 47 | 48 | func build() throws -> (request: URLRequest, uploadable: UploadRequest.Uploadable) { 49 | var urlRequest = try request.asURLRequest() 50 | urlRequest.setValue(multipartFormData.contentType, forHTTPHeaderField: "Content-Type") 51 | 52 | let uploadable: UploadRequest.Uploadable 53 | if multipartFormData.contentLength < encodingMemoryThreshold && !isInBackgroundSession { 54 | let data = try multipartFormData.encode() 55 | 56 | uploadable = .data(data) 57 | } else { 58 | let tempDirectoryURL = fileManager.temporaryDirectory 59 | let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data") 60 | let fileName = UUID().uuidString 61 | let fileURL = directoryURL.appendingPathComponent(fileName) 62 | 63 | try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) 64 | 65 | do { 66 | try multipartFormData.writeEncodedData(to: fileURL) 67 | } catch { 68 | // Cleanup after attempted write if it fails. 69 | try? fileManager.removeItem(at: fileURL) 70 | } 71 | 72 | uploadable = .file(fileURL, shouldRemove: true) 73 | } 74 | 75 | return (request: urlRequest, uploadable: uploadable) 76 | } 77 | } 78 | 79 | extension MultipartUpload: UploadConvertible { 80 | func asURLRequest() throws -> URLRequest { 81 | return try result.get().request 82 | } 83 | 84 | func createUploadable() throws -> UploadRequest.Uploadable { 85 | return try result.get().uploadable 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/NetworkReachabilityManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManager.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #if !(os(watchOS) || os(Linux)) 26 | 27 | import Foundation 28 | import SystemConfiguration 29 | 30 | /// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both cellular and 31 | /// WiFi network interfaces. 32 | /// 33 | /// Reachability can be used to determine background information about why a network operation failed, or to retry 34 | /// network requests when a connection is established. It should not be used to prevent a user from initiating a network 35 | /// request, as it's possible that an initial request may be required to establish reachability. 36 | open class NetworkReachabilityManager { 37 | /// Defines the various states of network reachability. 38 | public enum NetworkReachabilityStatus { 39 | /// It is unknown whether the network is reachable. 40 | case unknown 41 | /// The network is not reachable. 42 | case notReachable 43 | /// The network is reachable on the associated `ConnectionType`. 44 | case reachable(ConnectionType) 45 | 46 | init(_ flags: SCNetworkReachabilityFlags) { 47 | guard flags.isActuallyReachable else { self = .notReachable; return } 48 | 49 | var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) 50 | 51 | if flags.isCellular { networkStatus = .reachable(.cellular) } 52 | 53 | self = networkStatus 54 | } 55 | 56 | /// Defines the various connection types detected by reachability flags. 57 | public enum ConnectionType { 58 | /// The connection type is either over Ethernet or WiFi. 59 | case ethernetOrWiFi 60 | /// The connection type is a cellular connection. 61 | case cellular 62 | } 63 | } 64 | 65 | /// A closure executed when the network reachability status changes. The closure takes a single argument: the 66 | /// network reachability status. 67 | public typealias Listener = (NetworkReachabilityStatus) -> Void 68 | 69 | /// Default `NetworkReachabilityManager` for the zero address and a `listenerQueue` of `.main`. 70 | public static let `default` = NetworkReachabilityManager() 71 | 72 | // MARK: - Properties 73 | 74 | /// Whether the network is currently reachable. 75 | open var isReachable: Bool { return isReachableOnCellular || isReachableOnEthernetOrWiFi } 76 | 77 | /// Whether the network is currently reachable over the cellular interface. 78 | /// 79 | /// - Note: Using this property to decide whether to make a high or low bandwidth request is not recommended. 80 | /// Instead, set the `allowsCellularAccess` on any `URLRequest`s being issued. 81 | /// 82 | open var isReachableOnCellular: Bool { return status == .reachable(.cellular) } 83 | 84 | /// Whether the network is currently reachable over Ethernet or WiFi interface. 85 | open var isReachableOnEthernetOrWiFi: Bool { return status == .reachable(.ethernetOrWiFi) } 86 | 87 | /// `DispatchQueue` on which reachability will update. 88 | public let reachabilityQueue = DispatchQueue(label: "org.alamofire.reachabilityQueue") 89 | 90 | /// Flags of the current reachability type, if any. 91 | open var flags: SCNetworkReachabilityFlags? { 92 | var flags = SCNetworkReachabilityFlags() 93 | 94 | return (SCNetworkReachabilityGetFlags(reachability, &flags)) ? flags : nil 95 | } 96 | 97 | /// The current network reachability status. 98 | open var status: NetworkReachabilityStatus { 99 | return flags.map(NetworkReachabilityStatus.init) ?? .unknown 100 | } 101 | 102 | /// Mutable state storage. 103 | struct MutableState { 104 | /// A closure executed when the network reachability status changes. 105 | var listener: Listener? 106 | /// `DispatchQueue` on which listeners will be called. 107 | var listenerQueue: DispatchQueue? 108 | /// Previously calculated status. 109 | var previousStatus: NetworkReachabilityStatus? 110 | } 111 | 112 | /// `SCNetworkReachability` instance providing notifications. 113 | private let reachability: SCNetworkReachability 114 | 115 | /// Protected storage for mutable state. 116 | private let mutableState = Protector(MutableState()) 117 | 118 | // MARK: - Initialization 119 | 120 | /// Creates an instance with the specified host. 121 | /// 122 | /// - Note: The `host` value must *not* contain a scheme, just the hostname. 123 | /// 124 | /// - Parameters: 125 | /// - host: Host used to evaluate network reachability. Must *not* include the scheme (e.g. `https`). 126 | public convenience init?(host: String) { 127 | guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } 128 | 129 | self.init(reachability: reachability) 130 | } 131 | 132 | /// Creates an instance that monitors the address 0.0.0.0. 133 | /// 134 | /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing 135 | /// status of the device, both IPv4 and IPv6. 136 | public convenience init?() { 137 | var zero = sockaddr() 138 | zero.sa_len = UInt8(MemoryLayout.size) 139 | zero.sa_family = sa_family_t(AF_INET) 140 | 141 | guard let reachability = SCNetworkReachabilityCreateWithAddress(nil, &zero) else { return nil } 142 | 143 | self.init(reachability: reachability) 144 | } 145 | 146 | private init(reachability: SCNetworkReachability) { 147 | self.reachability = reachability 148 | } 149 | 150 | deinit { 151 | stopListening() 152 | } 153 | 154 | // MARK: - Listening 155 | 156 | /// Starts listening for changes in network reachability status. 157 | /// 158 | /// - Note: Stops and removes any existing listener. 159 | /// 160 | /// - Parameters: 161 | /// - queue: `DispatchQueue` on which to call the `listener` closure. `.main` by default. 162 | /// - listener: `Listener` closure called when reachability changes. 163 | /// 164 | /// - Returns: `true` if listening was started successfully, `false` otherwise. 165 | @discardableResult 166 | open func startListening(onQueue queue: DispatchQueue = .main, 167 | onUpdatePerforming listener: @escaping Listener) -> Bool { 168 | stopListening() 169 | 170 | mutableState.write { state in 171 | state.listenerQueue = queue 172 | state.listener = listener 173 | } 174 | 175 | var context = SCNetworkReachabilityContext(version: 0, 176 | info: Unmanaged.passRetained(self).toOpaque(), 177 | retain: nil, 178 | release: nil, 179 | copyDescription: nil) 180 | let callback: SCNetworkReachabilityCallBack = { _, flags, info in 181 | guard let info = info else { return } 182 | 183 | let instance = Unmanaged.fromOpaque(info).takeUnretainedValue() 184 | instance.notifyListener(flags) 185 | } 186 | 187 | let queueAdded = SCNetworkReachabilitySetDispatchQueue(reachability, reachabilityQueue) 188 | let callbackAdded = SCNetworkReachabilitySetCallback(reachability, callback, &context) 189 | 190 | // Manually call listener to give initial state, since the framework may not. 191 | if let currentFlags = flags { 192 | reachabilityQueue.async { 193 | self.notifyListener(currentFlags) 194 | } 195 | } 196 | 197 | return callbackAdded && queueAdded 198 | } 199 | 200 | /// Stops listening for changes in network reachability status. 201 | open func stopListening() { 202 | SCNetworkReachabilitySetCallback(reachability, nil, nil) 203 | SCNetworkReachabilitySetDispatchQueue(reachability, nil) 204 | mutableState.write { state in 205 | state.listener = nil 206 | state.listenerQueue = nil 207 | state.previousStatus = nil 208 | } 209 | } 210 | 211 | // MARK: - Internal - Listener Notification 212 | 213 | /// Calls the `listener` closure of the `listenerQueue` if the computed status hasn't changed. 214 | /// 215 | /// - Note: Should only be called from the `reachabilityQueue`. 216 | /// 217 | /// - Parameter flags: `SCNetworkReachabilityFlags` to use to calculate the status. 218 | func notifyListener(_ flags: SCNetworkReachabilityFlags) { 219 | let newStatus = NetworkReachabilityStatus(flags) 220 | 221 | mutableState.write { state in 222 | guard state.previousStatus != newStatus else { return } 223 | 224 | state.previousStatus = newStatus 225 | 226 | let listener = state.listener 227 | state.listenerQueue?.async { listener?(newStatus) } 228 | } 229 | } 230 | } 231 | 232 | // MARK: - 233 | 234 | extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} 235 | 236 | extension SCNetworkReachabilityFlags { 237 | var isReachable: Bool { return contains(.reachable) } 238 | var isConnectionRequired: Bool { return contains(.connectionRequired) } 239 | var canConnectAutomatically: Bool { return contains(.connectionOnDemand) || contains(.connectionOnTraffic) } 240 | var canConnectWithoutUserInteraction: Bool { return canConnectAutomatically && !contains(.interventionRequired) } 241 | var isActuallyReachable: Bool { return isReachable && (!isConnectionRequired || canConnectWithoutUserInteraction) } 242 | var isCellular: Bool { 243 | #if os(iOS) || os(tvOS) 244 | return contains(.isWWAN) 245 | #else 246 | return false 247 | #endif 248 | } 249 | 250 | /// Human readable `String` for all states, to help with debugging. 251 | var readableDescription: String { 252 | let W = isCellular ? "W" : "-" 253 | let R = isReachable ? "R" : "-" 254 | let c = isConnectionRequired ? "c" : "-" 255 | let t = contains(.transientConnection) ? "t" : "-" 256 | let i = contains(.interventionRequired) ? "i" : "-" 257 | let C = contains(.connectionOnTraffic) ? "C" : "-" 258 | let D = contains(.connectionOnDemand) ? "D" : "-" 259 | let l = contains(.isLocalAddress) ? "l" : "-" 260 | let d = contains(.isDirect) ? "d" : "-" 261 | let a = contains(.connectionAutomatic) ? "a" : "-" 262 | 263 | return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)\(a)" 264 | } 265 | } 266 | #endif 267 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | public extension Request { 28 | /// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`. 29 | static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume") 30 | /// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`. 31 | static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend") 32 | /// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`. 33 | static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel") 34 | /// Posted when a `Request` is finished. The `Notification` contains the completed `Request`. 35 | static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish") 36 | 37 | /// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`. 38 | static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask") 39 | /// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`. 40 | static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask") 41 | /// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`. 42 | static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask") 43 | /// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`. 44 | static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask") 45 | } 46 | 47 | // MARK: - 48 | 49 | extension Notification { 50 | /// The `Request` contained by the instance's `userInfo`, `nil` otherwise. 51 | public var request: Request? { 52 | return userInfo?[String.requestKey] as? Request 53 | } 54 | 55 | /// Convenience initializer for a `Notification` containing a `Request` payload. 56 | /// 57 | /// - Parameters: 58 | /// - name: The name of the notification. 59 | /// - request: The `Request` payload. 60 | init(name: Notification.Name, request: Request) { 61 | self.init(name: name, object: nil, userInfo: [String.requestKey: request]) 62 | } 63 | } 64 | 65 | extension NotificationCenter { 66 | /// Convenience function for posting notifications with `Request` payloads. 67 | /// 68 | /// - Parameters: 69 | /// - name: The name of the notification. 70 | /// - request: The `Request` payload. 71 | func postNotification(named name: Notification.Name, with request: Request) { 72 | let notification = Notification(name: name, request: request) 73 | post(notification) 74 | } 75 | } 76 | 77 | extension String { 78 | /// User info dictionary key representing the `Request` associated with the notification. 79 | fileprivate static let requestKey = "org.alamofire.notification.key.request" 80 | } 81 | 82 | /// `EventMonitor` that provides Alamofire's notifications. 83 | public final class AlamofireNotifications: EventMonitor { 84 | public func requestDidResume(_ request: Request) { 85 | NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request) 86 | } 87 | 88 | public func requestDidSuspend(_ request: Request) { 89 | NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request) 90 | } 91 | 92 | public func requestDidCancel(_ request: Request) { 93 | NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request) 94 | } 95 | 96 | public func requestDidFinish(_ request: Request) { 97 | NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request) 98 | } 99 | 100 | public func request(_ request: Request, didResumeTask task: URLSessionTask) { 101 | NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request) 102 | } 103 | 104 | public func request(_ request: Request, didSuspendTask task: URLSessionTask) { 105 | NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request) 106 | } 107 | 108 | public func request(_ request: Request, didCancelTask task: URLSessionTask) { 109 | NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request) 110 | } 111 | 112 | public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { 113 | NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/OperationQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperationQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension OperationQueue { 28 | /// Creates an instance using the provided parameters. 29 | /// 30 | /// - Parameters: 31 | /// - qualityOfService: `QualityOfService` to be applied to the queue. `.default` by default. 32 | /// - maxConcurrentOperationCount: Maximum concurrent operations. 33 | /// `OperationQueue.defaultMaxConcurrentOperationCount` by default. 34 | /// - underlyingQueue: Underlying `DispatchQueue`. `nil` by default. 35 | /// - name: Name for the queue. `nil` by default. 36 | /// - startSuspended: Whether the queue starts suspended. `false` by default. 37 | convenience init(qualityOfService: QualityOfService = .default, 38 | maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount, 39 | underlyingQueue: DispatchQueue? = nil, 40 | name: String? = nil, 41 | startSuspended: Bool = false) { 42 | self.init() 43 | self.qualityOfService = qualityOfService 44 | self.maxConcurrentOperationCount = maxConcurrentOperationCount 45 | self.underlyingQueue = underlyingQueue 46 | self.name = name 47 | isSuspended = startSuspended 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/ParameterEncoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParameterEncoder.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that can encode any `Encodable` type into a `URLRequest`. 28 | public protocol ParameterEncoder { 29 | /// Encode the provided `Encodable` parameters into `request`. 30 | /// 31 | /// - Parameters: 32 | /// - parameters: The `Encodable` parameter value. 33 | /// - request: The `URLRequest` into which to encode the parameters. 34 | /// 35 | /// - Returns: A `URLRequest` with the result of the encoding. 36 | /// - Throws: An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of 37 | /// `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`. 38 | func encode(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest 39 | } 40 | 41 | /// A `ParameterEncoder` that encodes types as JSON body data. 42 | /// 43 | /// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`. 44 | open class JSONParameterEncoder: ParameterEncoder { 45 | /// Returns an encoder with default parameters. 46 | public static var `default`: JSONParameterEncoder { return JSONParameterEncoder() } 47 | 48 | /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`. 49 | public static var prettyPrinted: JSONParameterEncoder { 50 | let encoder = JSONEncoder() 51 | encoder.outputFormatting = .prettyPrinted 52 | 53 | return JSONParameterEncoder(encoder: encoder) 54 | } 55 | 56 | /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`. 57 | @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) 58 | public static var sortedKeys: JSONParameterEncoder { 59 | let encoder = JSONEncoder() 60 | encoder.outputFormatting = .sortedKeys 61 | 62 | return JSONParameterEncoder(encoder: encoder) 63 | } 64 | 65 | /// `JSONEncoder` used to encode parameters. 66 | public let encoder: JSONEncoder 67 | 68 | /// Creates an instance with the provided `JSONEncoder`. 69 | /// 70 | /// - Parameter encoder: The `JSONEncoder`. `JSONEncoder()` by default. 71 | public init(encoder: JSONEncoder = JSONEncoder()) { 72 | self.encoder = encoder 73 | } 74 | 75 | open func encode(_ parameters: Parameters?, 76 | into request: URLRequest) throws -> URLRequest { 77 | guard let parameters = parameters else { return request } 78 | 79 | var request = request 80 | 81 | do { 82 | let data = try encoder.encode(parameters) 83 | request.httpBody = data 84 | if request.headers["Content-Type"] == nil { 85 | request.headers.update(.contentType("application/json")) 86 | } 87 | } catch { 88 | throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) 89 | } 90 | 91 | return request 92 | } 93 | } 94 | 95 | /// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending 96 | /// on the `Destination` set. 97 | /// 98 | /// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to 99 | /// `application/x-www-form-urlencoded; charset=utf-8`. 100 | /// 101 | /// Encoding behavior can be customized by passing an instance of `URLEncodedFormEncoder` to the initializer. 102 | open class URLEncodedFormParameterEncoder: ParameterEncoder { 103 | /// Defines where the URL-encoded string should be set for each `URLRequest`. 104 | public enum Destination { 105 | /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request. 106 | /// Sets it to the `httpBody` for all other methods. 107 | case methodDependent 108 | /// Applies the encoded query string to any existing query string from the `URLRequest`. 109 | case queryString 110 | /// Applies the encoded query string to the `httpBody` of the `URLRequest`. 111 | case httpBody 112 | 113 | /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`. 114 | /// 115 | /// - Parameter method: The `HTTPMethod`. 116 | /// 117 | /// - Returns: Whether the URL-encoded string should be applied to a `URL`. 118 | func encodesParametersInURL(for method: HTTPMethod) -> Bool { 119 | switch self { 120 | case .methodDependent: return [.get, .head, .delete].contains(method) 121 | case .queryString: return true 122 | case .httpBody: return false 123 | } 124 | } 125 | } 126 | 127 | /// Returns an encoder with default parameters. 128 | public static var `default`: URLEncodedFormParameterEncoder { return URLEncodedFormParameterEncoder() } 129 | 130 | /// The `URLEncodedFormEncoder` to use. 131 | public let encoder: URLEncodedFormEncoder 132 | 133 | /// The `Destination` for the URL-encoded string. 134 | public let destination: Destination 135 | 136 | /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value. 137 | /// 138 | /// - Parameters: 139 | /// - encoder: The `URLEncodedFormEncoder`. `URLEncodedFormEncoder()` by default. 140 | /// - destination: The `Destination`. `.methodDependent` by default. 141 | public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) { 142 | self.encoder = encoder 143 | self.destination = destination 144 | } 145 | 146 | open func encode(_ parameters: Parameters?, 147 | into request: URLRequest) throws -> URLRequest { 148 | guard let parameters = parameters else { return request } 149 | 150 | var request = request 151 | 152 | guard let url = request.url else { 153 | throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) 154 | } 155 | 156 | guard let method = request.method else { 157 | let rawValue = request.method?.rawValue ?? "nil" 158 | throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue))) 159 | } 160 | 161 | if destination.encodesParametersInURL(for: method), 162 | var components = URLComponents(url: url, resolvingAgainstBaseURL: false) { 163 | let query: String = try Result { try encoder.encode(parameters) } 164 | .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() 165 | let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands() 166 | components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString 167 | 168 | guard let newURL = components.url else { 169 | throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) 170 | } 171 | 172 | request.url = newURL 173 | } else { 174 | if request.headers["Content-Type"] == nil { 175 | request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8")) 176 | } 177 | 178 | request.httpBody = try Result { try encoder.encode(parameters) } 179 | .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() 180 | } 181 | 182 | return request 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/Protector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protector.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | // MARK: - 28 | 29 | /// An `os_unfair_lock` wrapper. 30 | final class UnfairLock { 31 | private let unfairLock: os_unfair_lock_t 32 | 33 | init() { 34 | unfairLock = .allocate(capacity: 1) 35 | unfairLock.initialize(to: os_unfair_lock()) 36 | } 37 | 38 | deinit { 39 | unfairLock.deinitialize(count: 1) 40 | unfairLock.deallocate() 41 | } 42 | 43 | private func lock() { 44 | os_unfair_lock_lock(unfairLock) 45 | } 46 | 47 | private func unlock() { 48 | os_unfair_lock_unlock(unfairLock) 49 | } 50 | 51 | /// Executes a closure returning a value while acquiring the lock. 52 | /// 53 | /// - Parameter closure: The closure to run. 54 | /// 55 | /// - Returns: The value the closure generated. 56 | func around(_ closure: () -> T) -> T { 57 | lock(); defer { unlock() } 58 | return closure() 59 | } 60 | 61 | /// Execute a closure while acquiring the lock. 62 | /// 63 | /// - Parameter closure: The closure to run. 64 | func around(_ closure: () -> Void) { 65 | lock(); defer { unlock() } 66 | return closure() 67 | } 68 | } 69 | 70 | /// A thread-safe wrapper around a value. 71 | final class Protector { 72 | private let lock = UnfairLock() 73 | private var value: T 74 | 75 | init(_ value: T) { 76 | self.value = value 77 | } 78 | 79 | /// The contained value. Unsafe for anything more than direct read or write. 80 | var directValue: T { 81 | get { return lock.around { value } } 82 | set { lock.around { value = newValue } } 83 | } 84 | 85 | /// Synchronously read or transform the contained value. 86 | /// 87 | /// - Parameter closure: The closure to execute. 88 | /// 89 | /// - Returns: The return value of the closure passed. 90 | func read(_ closure: (T) -> U) -> U { 91 | return lock.around { closure(self.value) } 92 | } 93 | 94 | /// Synchronously modify the protected value. 95 | /// 96 | /// - Parameter closure: The closure to execute. 97 | /// 98 | /// - Returns: The modified value. 99 | @discardableResult 100 | func write(_ closure: (inout T) -> U) -> U { 101 | return lock.around { closure(&self.value) } 102 | } 103 | } 104 | 105 | extension Protector where T: RangeReplaceableCollection { 106 | /// Adds a new element to the end of this protected collection. 107 | /// 108 | /// - Parameter newElement: The `Element` to append. 109 | func append(_ newElement: T.Element) { 110 | write { (ward: inout T) in 111 | ward.append(newElement) 112 | } 113 | } 114 | 115 | /// Adds the elements of a sequence to the end of this protected collection. 116 | /// 117 | /// - Parameter newElements: The `Sequence` to append. 118 | func append(contentsOf newElements: S) where S.Element == T.Element { 119 | write { (ward: inout T) in 120 | ward.append(contentsOf: newElements) 121 | } 122 | } 123 | 124 | /// Add the elements of a collection to the end of the protected collection. 125 | /// 126 | /// - Parameter newElements: The `Collection` to append. 127 | func append(contentsOf newElements: C) where C.Element == T.Element { 128 | write { (ward: inout T) in 129 | ward.append(contentsOf: newElements) 130 | } 131 | } 132 | } 133 | 134 | extension Protector where T == Data? { 135 | /// Adds the contents of a `Data` value to the end of the protected `Data`. 136 | /// 137 | /// - Parameter data: The `Data` to be appended. 138 | func append(_ data: Data) { 139 | write { (ward: inout T) in 140 | ward?.append(data) 141 | } 142 | } 143 | } 144 | 145 | extension Protector where T == Request.MutableState { 146 | /// Attempts to transition to the passed `State`. 147 | /// 148 | /// - Parameter state: The `State` to attempt transition to. 149 | /// 150 | /// - Returns: Whether the transition occurred. 151 | func attemptToTransitionTo(_ state: Request.State) -> Bool { 152 | return lock.around { 153 | guard value.state.canTransitionTo(state) else { return false } 154 | 155 | value.state = state 156 | 157 | return true 158 | } 159 | } 160 | 161 | /// Perform a closure while locked with the provided `Request.State`. 162 | /// 163 | /// - Parameter perform: The closure to perform while locked. 164 | func withState(perform: (Request.State) -> Void) { 165 | lock.around { perform(value.state) } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/RedirectHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedirectHandler.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that handles how an HTTP redirect response from a remote server should be redirected to the new request. 28 | public protocol RedirectHandler { 29 | /// Determines how the HTTP redirect response should be redirected to the new request. 30 | /// 31 | /// The `completion` closure should be passed one of three possible options: 32 | /// 33 | /// 1. The new request specified by the redirect (this is the most common use case). 34 | /// 2. A modified version of the new request (you may want to route it somewhere else). 35 | /// 3. A `nil` value to deny the redirect request and return the body of the redirect response. 36 | /// 37 | /// - Parameters: 38 | /// - task: The `URLSessionTask` whose request resulted in a redirect. 39 | /// - request: The `URLRequest` to the new location specified by the redirect response. 40 | /// - response: The `HTTPURLResponse` containing the server's response to the original request. 41 | /// - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`. 42 | func task(_ task: URLSessionTask, 43 | willBeRedirectedTo request: URLRequest, 44 | for response: HTTPURLResponse, 45 | completion: @escaping (URLRequest?) -> Void) 46 | } 47 | 48 | // MARK: - 49 | 50 | /// `Redirector` is a convenience `RedirectHandler` making it easy to follow, not follow, or modify a redirect. 51 | public struct Redirector { 52 | /// Defines the behavior of the `Redirector` type. 53 | public enum Behavior { 54 | /// Follow the redirect as defined in the response. 55 | case follow 56 | /// Do not follow the redirect defined in the response. 57 | case doNotFollow 58 | /// Modify the redirect request defined in the response. 59 | case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) 60 | } 61 | 62 | /// Returns a `Redirector` with a `.follow` `Behavior`. 63 | public static let follow = Redirector(behavior: .follow) 64 | /// Returns a `Redirector` with a `.doNotFollow` `Behavior`. 65 | public static let doNotFollow = Redirector(behavior: .doNotFollow) 66 | 67 | /// The `Behavior` of the `Redirector`. 68 | public let behavior: Behavior 69 | 70 | /// Creates a `Redirector` instance from the `Behavior`. 71 | /// 72 | /// - Parameter behavior: The `Behavior`. 73 | public init(behavior: Behavior) { 74 | self.behavior = behavior 75 | } 76 | } 77 | 78 | // MARK: - 79 | 80 | extension Redirector: RedirectHandler { 81 | public func task(_ task: URLSessionTask, 82 | willBeRedirectedTo request: URLRequest, 83 | for response: HTTPURLResponse, 84 | completion: @escaping (URLRequest?) -> Void) { 85 | switch behavior { 86 | case .follow: 87 | completion(request) 88 | case .doNotFollow: 89 | completion(nil) 90 | case let .modify(closure): 91 | let request = closure(task, request, response) 92 | completion(request) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/RequestInterceptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestInterceptor.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary. 28 | public protocol RequestAdapter { 29 | /// Inspects and adapts the specified `URLRequest` in some manner and calls the completion handler with the Result. 30 | /// 31 | /// - Parameters: 32 | /// - urlRequest: The `URLRequest` to adapt. 33 | /// - session: The `Session` that will execute the `URLRequest`. 34 | /// - completion: The completion handler that must be called when adaptation is complete. 35 | func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) 36 | } 37 | 38 | // MARK: - 39 | 40 | /// Outcome of determination whether retry is necessary. 41 | public enum RetryResult { 42 | /// Retry should be attempted immediately. 43 | case retry 44 | /// Retry should be attempted after the associated `TimeInterval`. 45 | case retryWithDelay(TimeInterval) 46 | /// Do not retry. 47 | case doNotRetry 48 | /// Do not retry due to the associated `Error`. 49 | case doNotRetryWithError(Error) 50 | } 51 | 52 | extension RetryResult { 53 | var retryRequired: Bool { 54 | switch self { 55 | case .retry, .retryWithDelay: return true 56 | default: return false 57 | } 58 | } 59 | 60 | var delay: TimeInterval? { 61 | switch self { 62 | case let .retryWithDelay(delay): return delay 63 | default: return nil 64 | } 65 | } 66 | 67 | var error: Error? { 68 | guard case let .doNotRetryWithError(error) = self else { return nil } 69 | return error 70 | } 71 | } 72 | 73 | /// A type that determines whether a request should be retried after being executed by the specified session manager 74 | /// and encountering an error. 75 | public protocol RequestRetrier { 76 | /// Determines whether the `Request` should be retried by calling the `completion` closure. 77 | /// 78 | /// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs 79 | /// to be retried. The one requirement is that the completion closure is called to ensure the request is properly 80 | /// cleaned up after. 81 | /// 82 | /// - Parameters: 83 | /// - request: `Request` that failed due to the provided `Error`. 84 | /// - session: `Session` that produced the `Request`. 85 | /// - error: `Error` encountered while executing the `Request`. 86 | /// - completion: Completion closure to be executed when a retry decision has been determined. 87 | func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) 88 | } 89 | 90 | // MARK: - 91 | 92 | /// Type that provides both `RequestAdapter` and `RequestRetrier` functionality. 93 | public protocol RequestInterceptor: RequestAdapter, RequestRetrier {} 94 | 95 | extension RequestInterceptor { 96 | public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { 97 | completion(.success(urlRequest)) 98 | } 99 | 100 | public func retry(_ request: Request, 101 | for session: Session, 102 | dueTo error: Error, 103 | completion: @escaping (RetryResult) -> Void) { 104 | completion(.doNotRetry) 105 | } 106 | } 107 | 108 | /// `RequestAdapter` closure definition. 109 | public typealias AdaptHandler = (URLRequest, Session, _ completion: @escaping (Result) -> Void) -> Void 110 | /// `RequestRetrier` closure definition. 111 | public typealias RetryHandler = (Request, Session, Error, _ completion: @escaping (RetryResult) -> Void) -> Void 112 | 113 | // MARK: - 114 | 115 | /// Closure-based `RequestAdapter`. 116 | open class Adapter: RequestInterceptor { 117 | private let adaptHandler: AdaptHandler 118 | 119 | /// Creates an instance using the provided closure. 120 | /// 121 | /// - Parameter adaptHandler: `AdaptHandler` closure to be executed when handling request adaptation. 122 | public init(_ adaptHandler: @escaping AdaptHandler) { 123 | self.adaptHandler = adaptHandler 124 | } 125 | 126 | open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { 127 | adaptHandler(urlRequest, session, completion) 128 | } 129 | } 130 | 131 | // MARK: - 132 | 133 | /// Closure-based `RequestRetrier`. 134 | open class Retrier: RequestInterceptor { 135 | private let retryHandler: RetryHandler 136 | 137 | /// Creates an instance using the provided closure. 138 | /// 139 | /// - Parameter retryHandler: `RetryHandler` closure to be executed when handling request retry. 140 | public init(_ retryHandler: @escaping RetryHandler) { 141 | self.retryHandler = retryHandler 142 | } 143 | 144 | open func retry(_ request: Request, 145 | for session: Session, 146 | dueTo error: Error, 147 | completion: @escaping (RetryResult) -> Void) { 148 | retryHandler(request, session, error, completion) 149 | } 150 | } 151 | 152 | // MARK: - 153 | 154 | /// `RequestInterceptor` which can use multiple `RequestAdapter` and `RequestRetrier` values. 155 | open class Interceptor: RequestInterceptor { 156 | /// All `RequestAdapter`s associated with the instance. These adapters will be run until one fails. 157 | public let adapters: [RequestAdapter] 158 | /// All `RequestRetrier`s associated with the instance. These retriers will be run one at a time until one triggers retry. 159 | public let retriers: [RequestRetrier] 160 | 161 | /// Creates an instance from `AdaptHandler` and `RetryHandler` closures. 162 | /// 163 | /// - Parameters: 164 | /// - adaptHandler: `AdaptHandler` closure to be used. 165 | /// - retryHandler: `RetryHandler` closure to be used. 166 | public init(adaptHandler: @escaping AdaptHandler, retryHandler: @escaping RetryHandler) { 167 | adapters = [Adapter(adaptHandler)] 168 | retriers = [Retrier(retryHandler)] 169 | } 170 | 171 | /// Creates an instance from `RequestAdapter` and `RequestRetrier` values. 172 | /// 173 | /// - Parameters: 174 | /// - adapter: `RequestAdapter` value to be used. 175 | /// - retrier: `RequestRetrier` value to be used. 176 | public init(adapter: RequestAdapter, retrier: RequestRetrier) { 177 | adapters = [adapter] 178 | retriers = [retrier] 179 | } 180 | 181 | /// Creates an instance from the arrays of `RequestAdapter` and `RequestRetrier` values. 182 | /// 183 | /// - Parameters: 184 | /// - adapters: `RequestAdapter` values to be used. 185 | /// - retriers: `RequestRetrier` values to be used. 186 | public init(adapters: [RequestAdapter] = [], retriers: [RequestRetrier] = []) { 187 | self.adapters = adapters 188 | self.retriers = retriers 189 | } 190 | 191 | open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { 192 | adapt(urlRequest, for: session, using: adapters, completion: completion) 193 | } 194 | 195 | private func adapt(_ urlRequest: URLRequest, 196 | for session: Session, 197 | using adapters: [RequestAdapter], 198 | completion: @escaping (Result) -> Void) { 199 | var pendingAdapters = adapters 200 | 201 | guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return } 202 | 203 | let adapter = pendingAdapters.removeFirst() 204 | 205 | adapter.adapt(urlRequest, for: session) { result in 206 | switch result { 207 | case let .success(urlRequest): 208 | self.adapt(urlRequest, for: session, using: pendingAdapters, completion: completion) 209 | case .failure: 210 | completion(result) 211 | } 212 | } 213 | } 214 | 215 | open func retry(_ request: Request, 216 | for session: Session, 217 | dueTo error: Error, 218 | completion: @escaping (RetryResult) -> Void) { 219 | retry(request, for: session, dueTo: error, using: retriers, completion: completion) 220 | } 221 | 222 | private func retry(_ request: Request, 223 | for session: Session, 224 | dueTo error: Error, 225 | using retriers: [RequestRetrier], 226 | completion: @escaping (RetryResult) -> Void) { 227 | var pendingRetriers = retriers 228 | 229 | guard !pendingRetriers.isEmpty else { completion(.doNotRetry); return } 230 | 231 | let retrier = pendingRetriers.removeFirst() 232 | 233 | retrier.retry(request, for: session, dueTo: error) { result in 234 | switch result { 235 | case .retry, .retryWithDelay, .doNotRetryWithError: 236 | completion(result) 237 | case .doNotRetry: 238 | // Only continue to the next retrier if retry was not triggered and no error was encountered 239 | self.retry(request, for: session, dueTo: error, using: pendingRetriers, completion: completion) 240 | } 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/RequestTaskMap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestTaskMap.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that maintains a two way, one to one map of `URLSessionTask`s to `Request`s. 28 | struct RequestTaskMap { 29 | private var tasksToRequests: [URLSessionTask: Request] 30 | private var requestsToTasks: [Request: URLSessionTask] 31 | private var taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] 32 | 33 | var requests: [Request] { 34 | return Array(tasksToRequests.values) 35 | } 36 | 37 | init(tasksToRequests: [URLSessionTask: Request] = [:], 38 | requestsToTasks: [Request: URLSessionTask] = [:], 39 | taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) { 40 | self.tasksToRequests = tasksToRequests 41 | self.requestsToTasks = requestsToTasks 42 | self.taskEvents = taskEvents 43 | } 44 | 45 | subscript(_ request: Request) -> URLSessionTask? { 46 | get { return requestsToTasks[request] } 47 | set { 48 | guard let newValue = newValue else { 49 | guard let task = requestsToTasks[request] else { 50 | fatalError("RequestTaskMap consistency error: no task corresponding to request found.") 51 | } 52 | 53 | requestsToTasks.removeValue(forKey: request) 54 | tasksToRequests.removeValue(forKey: task) 55 | taskEvents.removeValue(forKey: task) 56 | 57 | return 58 | } 59 | 60 | requestsToTasks[request] = newValue 61 | tasksToRequests[newValue] = request 62 | taskEvents[newValue] = (completed: false, metricsGathered: false) 63 | } 64 | } 65 | 66 | subscript(_ task: URLSessionTask) -> Request? { 67 | get { return tasksToRequests[task] } 68 | set { 69 | guard let newValue = newValue else { 70 | guard let request = tasksToRequests[task] else { 71 | fatalError("RequestTaskMap consistency error: no request corresponding to task found.") 72 | } 73 | 74 | tasksToRequests.removeValue(forKey: task) 75 | requestsToTasks.removeValue(forKey: request) 76 | taskEvents.removeValue(forKey: task) 77 | 78 | return 79 | } 80 | 81 | tasksToRequests[task] = newValue 82 | requestsToTasks[newValue] = task 83 | taskEvents[task] = (completed: false, metricsGathered: false) 84 | } 85 | } 86 | 87 | var count: Int { 88 | precondition(tasksToRequests.count == requestsToTasks.count, 89 | "RequestTaskMap.count invalid, requests.count: \(tasksToRequests.count) != tasks.count: \(requestsToTasks.count)") 90 | 91 | return tasksToRequests.count 92 | } 93 | 94 | var eventCount: Int { 95 | precondition(taskEvents.count == count, "RequestTaskMap.eventCount invalid, count: \(count) != taskEvents.count: \(taskEvents.count)") 96 | 97 | return taskEvents.count 98 | } 99 | 100 | var isEmpty: Bool { 101 | precondition(tasksToRequests.isEmpty == requestsToTasks.isEmpty, 102 | "RequestTaskMap.isEmpty invalid, requests.isEmpty: \(tasksToRequests.isEmpty) != tasks.isEmpty: \(requestsToTasks.isEmpty)") 103 | 104 | return tasksToRequests.isEmpty 105 | } 106 | 107 | var isEventsEmpty: Bool { 108 | precondition(taskEvents.isEmpty == isEmpty, "RequestTaskMap.isEventsEmpty invalid, isEmpty: \(isEmpty) != taskEvents.isEmpty: \(taskEvents.isEmpty)") 109 | 110 | return taskEvents.isEmpty 111 | } 112 | 113 | mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) { 114 | guard let events = taskEvents[task] else { 115 | fatalError("RequestTaskMap consistency error: no events corresponding to task found.") 116 | } 117 | 118 | switch (events.completed, events.metricsGathered) { 119 | case (_, true): fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.") 120 | case (false, false): taskEvents[task] = (completed: false, metricsGathered: true) 121 | case (true, false): self[task] = nil 122 | } 123 | } 124 | 125 | mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) { 126 | guard let events = taskEvents[task] else { 127 | fatalError("RequestTaskMap consistency error: no events corresponding to task found.") 128 | } 129 | 130 | switch (events.completed, events.metricsGathered) { 131 | case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.") 132 | case (false, false): taskEvents[task] = (completed: true, metricsGathered: false) 133 | case (false, true): self[task] = nil 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/Result+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result+Alamofire.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Default type of `Result` returned by Alamofire, with an `AFError` `Failure` type. 28 | public typealias AFResult = Result 29 | 30 | // MARK: - Internal APIs 31 | 32 | extension Result { 33 | /// Returns the associated value if the result is a success, `nil` otherwise. 34 | var success: Success? { 35 | guard case let .success(value) = self else { return nil } 36 | return value 37 | } 38 | 39 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 40 | var failure: Failure? { 41 | guard case let .failure(error) = self else { return nil } 42 | return error 43 | } 44 | 45 | /// Initializes a `Result` from value or error. Returns `.failure` if the error is non-nil, `.success` otherwise. 46 | /// 47 | /// - Parameters: 48 | /// - value: A value. 49 | /// - error: An `Error`. 50 | init(value: Success, error: Failure?) { 51 | if let error = error { 52 | self = .failure(error) 53 | } else { 54 | self = .success(value) 55 | } 56 | } 57 | 58 | /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. 59 | /// 60 | /// Use the `tryMap` method with a closure that may throw an error. For example: 61 | /// 62 | /// let possibleData: Result = .success(Data(...)) 63 | /// let possibleObject = possibleData.tryMap { 64 | /// try JSONSerialization.jsonObject(with: $0) 65 | /// } 66 | /// 67 | /// - parameter transform: A closure that takes the success value of the instance. 68 | /// 69 | /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the 70 | /// same failure. 71 | func tryMap(_ transform: (Success) throws -> NewSuccess) -> Result { 72 | switch self { 73 | case let .success(value): 74 | do { 75 | return try .success(transform(value)) 76 | } catch { 77 | return .failure(error) 78 | } 79 | case let .failure(error): 80 | return .failure(error) 81 | } 82 | } 83 | 84 | /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. 85 | /// 86 | /// Use the `tryMapError` function with a closure that may throw an error. For example: 87 | /// 88 | /// let possibleData: Result = .success(Data(...)) 89 | /// let possibleObject = possibleData.tryMapError { 90 | /// try someFailableFunction(taking: $0) 91 | /// } 92 | /// 93 | /// - Parameter transform: A throwing closure that takes the error of the instance. 94 | /// 95 | /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns 96 | /// the same success. 97 | func tryMapError(_ transform: (Failure) throws -> NewFailure) -> Result { 98 | switch self { 99 | case let .failure(error): 100 | do { 101 | return try .failure(transform(error)) 102 | } catch { 103 | return .failure(error) 104 | } 105 | case let .success(value): 106 | return .success(value) 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLConvertible+URLRequestConvertible.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Types adopting the `URLConvertible` protocol can be used to construct `URL`s, which can then be used to construct 28 | /// `URLRequests`. 29 | public protocol URLConvertible { 30 | /// Returns a `URL` from the conforming instance or throws. 31 | /// 32 | /// - Returns: The `URL` created from the instance. 33 | /// - Throws: Any error thrown while creating the `URL`. 34 | func asURL() throws -> URL 35 | } 36 | 37 | extension String: URLConvertible { 38 | /// Returns a `URL` if `self` can be used to initialize a `URL` instance, otherwise throws. 39 | /// 40 | /// - Returns: The `URL` initialized with `self`. 41 | /// - Throws: An `AFError.invalidURL` instance. 42 | public func asURL() throws -> URL { 43 | guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) } 44 | 45 | return url 46 | } 47 | } 48 | 49 | extension URL: URLConvertible { 50 | /// Returns `self`. 51 | public func asURL() throws -> URL { return self } 52 | } 53 | 54 | extension URLComponents: URLConvertible { 55 | /// Returns a `URL` if the `self`'s `url` is not nil, otherwise throws. 56 | /// 57 | /// - Returns: The `URL` from the `url` property. 58 | /// - Throws: An `AFError.invalidURL` instance. 59 | public func asURL() throws -> URL { 60 | guard let url = url else { throw AFError.invalidURL(url: self) } 61 | 62 | return url 63 | } 64 | } 65 | 66 | // MARK: - 67 | 68 | /// Types adopting the `URLRequestConvertible` protocol can be used to safely construct `URLRequest`s. 69 | public protocol URLRequestConvertible { 70 | /// Returns a `URLRequest` or throws if an `Error` was encountered. 71 | /// 72 | /// - Returns: A `URLRequest`. 73 | /// - Throws: Any error thrown while constructing the `URLRequest`. 74 | func asURLRequest() throws -> URLRequest 75 | } 76 | 77 | extension URLRequestConvertible { 78 | /// The `URLRequest` returned by discarding any `Error` encountered. 79 | public var urlRequest: URLRequest? { return try? asURLRequest() } 80 | } 81 | 82 | extension URLRequest: URLRequestConvertible { 83 | /// Returns `self`. 84 | public func asURLRequest() throws -> URLRequest { return self } 85 | } 86 | 87 | // MARK: - 88 | 89 | extension URLRequest { 90 | /// Creates an instance with the specified `url`, `method`, and `headers`. 91 | /// 92 | /// - Parameters: 93 | /// - url: The `URLConvertible` value. 94 | /// - method: The `HTTPMethod`. 95 | /// - headers: The `HTTPHeaders`, `nil` by default. 96 | /// - Throws: Any error thrown while converting the `URLConvertible` to a `URL`. 97 | public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { 98 | let url = try url.asURL() 99 | 100 | self.init(url: url) 101 | 102 | httpMethod = method.rawValue 103 | allHTTPHeaderFields = headers?.dictionary 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/URLRequest+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLRequest+Alamofire.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | public extension URLRequest { 28 | /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type. 29 | var method: HTTPMethod? { 30 | get { return httpMethod.flatMap(HTTPMethod.init) } 31 | set { httpMethod = newValue?.rawValue } 32 | } 33 | 34 | func validate() throws { 35 | if method == .get, let bodyData = httpBody { 36 | throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData)) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionConfiguration+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension URLSessionConfiguration: AlamofireExtended {} 28 | extension AlamofireExtension where ExtendedType: URLSessionConfiguration { 29 | /// Alamofire's default configuration. Same as `URLSessionConfiguration.default` but adds Alamofire default 30 | /// `Accept-Language`, `Accept-Encoding`, and `User-Agent` headers. 31 | public static var `default`: URLSessionConfiguration { 32 | let configuration = URLSessionConfiguration.default 33 | configuration.headers = .default 34 | 35 | return configuration 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/Pods/Alamofire/Source/Validation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Validation.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Request { 28 | // MARK: Helper Types 29 | 30 | fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason 31 | 32 | /// Used to represent whether a validation succeeded or failed. 33 | public typealias ValidationResult = Result 34 | 35 | fileprivate struct MIMEType { 36 | let type: String 37 | let subtype: String 38 | 39 | var isWildcard: Bool { return type == "*" && subtype == "*" } 40 | 41 | init?(_ string: String) { 42 | let components: [String] = { 43 | let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) 44 | let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] 45 | 46 | return split.components(separatedBy: "/") 47 | }() 48 | 49 | if let type = components.first, let subtype = components.last { 50 | self.type = type 51 | self.subtype = subtype 52 | } else { 53 | return nil 54 | } 55 | } 56 | 57 | func matches(_ mime: MIMEType) -> Bool { 58 | switch (type, subtype) { 59 | case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): 60 | return true 61 | default: 62 | return false 63 | } 64 | } 65 | } 66 | 67 | // MARK: Properties 68 | 69 | fileprivate var acceptableStatusCodes: Range { return 200..<300 } 70 | 71 | fileprivate var acceptableContentTypes: [String] { 72 | if let accept = request?.value(forHTTPHeaderField: "Accept") { 73 | return accept.components(separatedBy: ",") 74 | } 75 | 76 | return ["*/*"] 77 | } 78 | 79 | // MARK: Status Code 80 | 81 | fileprivate func validate(statusCode acceptableStatusCodes: S, 82 | response: HTTPURLResponse) 83 | -> ValidationResult 84 | where S.Iterator.Element == Int { 85 | if acceptableStatusCodes.contains(response.statusCode) { 86 | return .success(Void()) 87 | } else { 88 | let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode) 89 | return .failure(AFError.responseValidationFailed(reason: reason)) 90 | } 91 | } 92 | 93 | // MARK: Content Type 94 | 95 | fileprivate func validate(contentType acceptableContentTypes: S, 96 | response: HTTPURLResponse, 97 | data: Data?) 98 | -> ValidationResult 99 | where S.Iterator.Element == String { 100 | guard let data = data, !data.isEmpty else { return .success(Void()) } 101 | 102 | guard 103 | let responseContentType = response.mimeType, 104 | let responseMIMEType = MIMEType(responseContentType) 105 | else { 106 | for contentType in acceptableContentTypes { 107 | if let mimeType = MIMEType(contentType), mimeType.isWildcard { 108 | return .success(Void()) 109 | } 110 | } 111 | 112 | let error: AFError = { 113 | let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes)) 114 | return AFError.responseValidationFailed(reason: reason) 115 | }() 116 | 117 | return .failure(error) 118 | } 119 | 120 | for contentType in acceptableContentTypes { 121 | if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { 122 | return .success(Void()) 123 | } 124 | } 125 | 126 | let error: AFError = { 127 | let reason: ErrorReason = .unacceptableContentType(acceptableContentTypes: Array(acceptableContentTypes), 128 | responseContentType: responseContentType) 129 | 130 | return AFError.responseValidationFailed(reason: reason) 131 | }() 132 | 133 | return .failure(error) 134 | } 135 | } 136 | 137 | // MARK: - 138 | 139 | extension DataRequest { 140 | /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the 141 | /// request was valid. 142 | public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult 143 | 144 | /// Validates that the response has a status code in the specified sequence. 145 | /// 146 | /// If validation fails, subsequent calls to response handlers will have an associated error. 147 | /// 148 | /// - parameter range: The range of acceptable status codes. 149 | /// 150 | /// - returns: The request. 151 | @discardableResult 152 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 153 | return validate { [unowned self] _, response, _ in 154 | self.validate(statusCode: acceptableStatusCodes, response: response) 155 | } 156 | } 157 | 158 | /// Validates that the response has a content type in the specified sequence. 159 | /// 160 | /// If validation fails, subsequent calls to response handlers will have an associated error. 161 | /// 162 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 163 | /// 164 | /// - returns: The request. 165 | @discardableResult 166 | public func validate(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { 167 | return validate { [unowned self] _, response, data in 168 | self.validate(contentType: acceptableContentTypes(), response: response, data: data) 169 | } 170 | } 171 | 172 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 173 | /// type matches any specified in the Accept HTTP header field. 174 | /// 175 | /// If validation fails, subsequent calls to response handlers will have an associated error. 176 | /// 177 | /// - returns: The request. 178 | @discardableResult 179 | public func validate() -> Self { 180 | let contentTypes: () -> [String] = { [unowned self] in 181 | self.acceptableContentTypes 182 | } 183 | return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) 184 | } 185 | } 186 | 187 | // MARK: - 188 | 189 | extension DownloadRequest { 190 | /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a 191 | /// destination URL, and returns whether the request was valid. 192 | public typealias Validation = (_ request: URLRequest?, 193 | _ response: HTTPURLResponse, 194 | _ fileURL: URL?) 195 | -> ValidationResult 196 | 197 | /// Validates that the response has a status code in the specified sequence. 198 | /// 199 | /// If validation fails, subsequent calls to response handlers will have an associated error. 200 | /// 201 | /// - parameter range: The range of acceptable status codes. 202 | /// 203 | /// - returns: The request. 204 | @discardableResult 205 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 206 | return validate { [unowned self] _, response, _ in 207 | self.validate(statusCode: acceptableStatusCodes, response: response) 208 | } 209 | } 210 | 211 | /// Validates that the response has a content type in the specified sequence. 212 | /// 213 | /// If validation fails, subsequent calls to response handlers will have an associated error. 214 | /// 215 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 216 | /// 217 | /// - returns: The request. 218 | @discardableResult 219 | public func validate(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String { 220 | return validate { [unowned self] _, response, fileURL in 221 | guard let validFileURL = fileURL else { 222 | return .failure(AFError.responseValidationFailed(reason: .dataFileNil)) 223 | } 224 | 225 | do { 226 | let data = try Data(contentsOf: validFileURL) 227 | return self.validate(contentType: acceptableContentTypes(), response: response, data: data) 228 | } catch { 229 | return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL))) 230 | } 231 | } 232 | } 233 | 234 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 235 | /// type matches any specified in the Accept HTTP header field. 236 | /// 237 | /// If validation fails, subsequent calls to response handlers will have an associated error. 238 | /// 239 | /// - returns: The request. 240 | @discardableResult 241 | public func validate() -> Self { 242 | let contentTypes = { [unowned self] in 243 | self.acceptableContentTypes 244 | } 245 | return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes()) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/ErrorHandler.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ErrorHandler", 3 | "version": "0.8.4", 4 | "swift_versions": [ 5 | "4.2", 6 | "5.0" 7 | ], 8 | "summary": "Elegant and flexible error handling for Swift", 9 | "description": "> Elegant and flexible error handling for Swift\n\nErrorHandler enables expressing complex error handling logic with a few lines of code using a memorable fluent API.", 10 | "homepage": "https://github.com/Workable/swift-error-handler", 11 | "license": { 12 | "type": "MIT", 13 | "file": "LICENSE" 14 | }, 15 | "authors": { 16 | "Kostas Kremizas": "kremizask@gmail.com", 17 | "Eleni Papanikolopoulou": "eleni.papanikolopoulou@gmail.com" 18 | }, 19 | "platforms": { 20 | "ios": "10.0", 21 | "osx": "10.12", 22 | "tvos": "10.0", 23 | "watchos": "3.0" 24 | }, 25 | "source": { 26 | "git": "https://github.com/Workable/swift-error-handler.git", 27 | "tag": "0.8.4" 28 | }, 29 | "default_subspecs": "Core", 30 | "subspecs": [ 31 | { 32 | "name": "Core", 33 | "source_files": "ErrorHandler/Classes/Core/**/*", 34 | "frameworks": "Foundation" 35 | }, 36 | { 37 | "name": "Alamofire", 38 | "source_files": "ErrorHandler/Classes/Alamofire/**/*", 39 | "dependencies": { 40 | "Alamofire": [ 41 | "~> 5" 42 | ], 43 | "ErrorHandler/Core": [ 44 | 45 | ] 46 | } 47 | } 48 | ], 49 | "swift_version": "5.0" 50 | } 51 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.0.2) 3 | - ErrorHandler (0.8.4): 4 | - ErrorHandler/Core (= 0.8.4) 5 | - ErrorHandler/Alamofire (0.8.4): 6 | - Alamofire (~> 5) 7 | - ErrorHandler/Core 8 | - ErrorHandler/Core (0.8.4) 9 | 10 | DEPENDENCIES: 11 | - ErrorHandler (from `../`) 12 | - ErrorHandler/Alamofire (from `../`) 13 | 14 | SPEC REPOS: 15 | https://github.com/CocoaPods/Specs.git: 16 | - Alamofire 17 | 18 | EXTERNAL SOURCES: 19 | ErrorHandler: 20 | :path: "../" 21 | 22 | SPEC CHECKSUMS: 23 | Alamofire: 3ba7a4db18b4f62c4a1c0e1cb39d7f3d52e10ada 24 | ErrorHandler: f7e8d8236e305978f856eb52bc2f19435072906b 25 | 26 | PODFILE CHECKSUM: 1ecd38147b811789385bbac850686caa2f83077b 27 | 28 | COCOAPODS: 1.8.4 29 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/ErrorHandler.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Alamofire/Alamofire-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 | 5.0.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | framework module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Alamofire/Alamofire.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Alamofire/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 | 4.5.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ErrorHandler/ErrorHandler-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 | 0.8.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ErrorHandler/ErrorHandler-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_ErrorHandler : NSObject 3 | @end 4 | @implementation PodsDummy_ErrorHandler 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ErrorHandler/ErrorHandler-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ErrorHandler/ErrorHandler-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double ErrorHandlerVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char ErrorHandlerVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ErrorHandler/ErrorHandler.modulemap: -------------------------------------------------------------------------------- 1 | framework module ErrorHandler { 2 | umbrella header "ErrorHandler-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ErrorHandler/ErrorHandler.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" 5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ErrorHandler/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 | 0.8.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example-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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | ## ErrorHandler 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2017 Workable SA 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 49 | SOFTWARE. 50 | 51 | Generated by CocoaPods - https://cocoapods.org 52 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Alamofire 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | The MIT License (MIT) 47 | 48 | Copyright (c) 2017 Workable SA 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy 51 | of this software and associated documentation files (the "Software"), to deal 52 | in the Software without restriction, including without limitation the rights 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 54 | copies of the Software, and to permit persons to whom the Software is 55 | furnished to do so, subject to the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be included in all 58 | copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 61 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 63 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 64 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 65 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 66 | SOFTWARE. 67 | 68 | License 69 | MIT 70 | Title 71 | ErrorHandler 72 | Type 73 | PSGroupSpecifier 74 | 75 | 76 | FooterText 77 | Generated by CocoaPods - https://cocoapods.org 78 | Title 79 | 80 | Type 81 | PSGroupSpecifier 82 | 83 | 84 | StringsTable 85 | Acknowledgements 86 | Title 87 | Acknowledgements 88 | 89 | 90 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ErrorHandler_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ErrorHandler_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 165 | install_framework "${BUILT_PRODUCTS_DIR}/ErrorHandler/ErrorHandler.framework" 166 | fi 167 | if [[ "$CONFIGURATION" == "Release" ]]; then 168 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 169 | install_framework "${BUILT_PRODUCTS_DIR}/ErrorHandler/ErrorHandler.framework" 170 | fi 171 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 172 | wait 173 | fi 174 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ErrorHandler_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ErrorHandler_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler/ErrorHandler.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "ErrorHandler" -framework "Foundation" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ErrorHandler_Example { 2 | umbrella header "Pods-ErrorHandler_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Example/Pods-ErrorHandler_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler/ErrorHandler.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "ErrorHandler" -framework "Foundation" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ErrorHandler_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ErrorHandler_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 12 | 13 | install_framework() 14 | { 15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 16 | local source="${BUILT_PRODUCTS_DIR}/$1" 17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 19 | elif [ -r "$1" ]; then 20 | local source="$1" 21 | fi 22 | 23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 24 | 25 | if [ -L "${source}" ]; then 26 | echo "Symlinked..." 27 | source="$(readlink "${source}")" 28 | fi 29 | 30 | # Use filter instead of exclude so missing patterns don't throw errors. 31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 33 | 34 | local basename 35 | basename="$(basename -s .framework "$1")" 36 | binary="${destination}/${basename}.framework/${basename}" 37 | if ! [ -r "$binary" ]; then 38 | binary="${destination}/${basename}" 39 | fi 40 | 41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 43 | strip_invalid_archs "$binary" 44 | fi 45 | 46 | # Resign the code if required by the build settings to avoid unstable apps 47 | code_sign_if_enabled "${destination}/$(basename "$1")" 48 | 49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 51 | local swift_runtime_libs 52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 53 | for lib in $swift_runtime_libs; do 54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 56 | code_sign_if_enabled "${destination}/${lib}" 57 | done 58 | fi 59 | } 60 | 61 | # Copies the dSYM of a vendored framework 62 | install_dsym() { 63 | local source="$1" 64 | if [ -r "$source" ]; then 65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\"" 66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}" 67 | fi 68 | } 69 | 70 | # Signs a framework with the provided identity 71 | code_sign_if_enabled() { 72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 73 | # Use the current code_sign_identitiy 74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 76 | 77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 78 | code_sign_cmd="$code_sign_cmd &" 79 | fi 80 | echo "$code_sign_cmd" 81 | eval "$code_sign_cmd" 82 | fi 83 | } 84 | 85 | # Strip invalid architectures 86 | strip_invalid_archs() { 87 | binary="$1" 88 | # Get architectures for current file 89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 90 | stripped="" 91 | for arch in $archs; do 92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 93 | # Strip non-valid architectures in-place 94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 95 | stripped="$stripped $arch" 96 | fi 97 | done 98 | if [[ "$stripped" ]]; then 99 | echo "Stripped $binary of architectures:$stripped" 100 | fi 101 | } 102 | 103 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 104 | wait 105 | fi 106 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ErrorHandler_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ErrorHandler_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler/ErrorHandler.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "ErrorHandler" -framework "Foundation" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ErrorHandler_Tests { 2 | umbrella header "Pods-ErrorHandler_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ErrorHandler_Tests/Pods-ErrorHandler_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ErrorHandler/ErrorHandler.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "ErrorHandler" -framework "Foundation" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /Example/Tests/AFErrorStatusCodeMatcherTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AFErrorStatusCodeMatcherTests.swift 3 | // ErrorHandler-AFExtensions 4 | // 5 | // Created by Kostas Kremizas on 30/08/2017. 6 | // Copyright © 2017 Workable SA. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import Alamofire 12 | import ErrorHandler 13 | 14 | class AFErrorStatusCodeMatcherTests: XCTestCase { 15 | 16 | func testThatItMatchesErrorWithSameStatus() { 17 | let code = 404 18 | let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: code)) 19 | 20 | let matcher = AFErrorStatusCodeMatcher(statusCode: code) 21 | 22 | XCTAssertTrue(matcher.matches(error)) 23 | } 24 | 25 | func testThatItDoesntMatchErrorWithOtherStatus() { 26 | let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 404)) 27 | 28 | let matcher = AFErrorStatusCodeMatcher(statusCode: 400) 29 | 30 | XCTAssertFalse(matcher.matches(error)) 31 | } 32 | 33 | func testThatItMatchesErrorInCorrectRange() { 34 | let matcher = AFErrorStatusCodeMatcher(400..<430) 35 | let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 412)) 36 | XCTAssertTrue(matcher.matches(error)) 37 | } 38 | 39 | func testThatItDoesNotMatchErrorOutsideRange() { 40 | let matcher = AFErrorStatusCodeMatcher(400..<430) 41 | let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 500)) 42 | XCTAssertFalse(matcher.matches(error)) 43 | } 44 | 45 | func testThatItDoesNotMatchAFErrorOfDifferentReason() { 46 | let error = AFError.responseValidationFailed(reason: .dataFileNil) 47 | let matcher = AFErrorStatusCodeMatcher(400..<500) 48 | XCTAssertFalse(matcher.matches(error)) 49 | } 50 | 51 | func testThatItDoesntMatchOtherTypesOfAFErrorReasons() { 52 | let error = AFError.parameterEncodingFailed(reason: .missingURL) 53 | let matcher = AFErrorStatusCodeMatcher(400..<500) 54 | XCTAssertFalse(matcher.matches(error)) 55 | } 56 | 57 | func testThatItDoesntMatchOtherTypesOfErrors() { 58 | let error = NSError(domain: "foo", code: 9, userInfo: nil) 59 | let matcher = AFErrorStatusCodeMatcher(400..<500) 60 | XCTAssertFalse(matcher.matches(error)) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Example/Tests/ClosureErrorMatcherTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClosureMatcherTests.swift 3 | // ErrorHandler-iOS Tests 4 | // 5 | // Created by Kostas Kremizas on 02/08/2017. 6 | // Copyright © 2017 Workable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ErrorHandler 11 | 12 | class ClosureErrorMatcherTests: XCTestCase { 13 | 14 | func testThatClosureMatcherUsesClosureInInitialiser() { 15 | 16 | struct AnError: Error {} 17 | let error = AnError() 18 | let closure: (Error) -> Bool = { $0 is AnError } 19 | 20 | let sut = ClosureErrorMatcher(matches: closure) 21 | XCTAssertEqual(closure(error), sut.matches(error)) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Example/Tests/ErrorHandler-AFExtensionsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorHandler-AFExtensionsTests.swift 3 | // Workable SA 4 | // 5 | // Created by Kostas Kremizas on 01/09/2017. 6 | // Copyright © 2017 Workable SA. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import Alamofire 12 | import ErrorHandler 13 | 14 | class ErrorHandlerAFExtensionsTests: XCTestCase { 15 | 16 | func testThatItMatchesErrorWithSameStatus() { 17 | let code = 404 18 | let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: code)) 19 | let actionCalled = expectation(description: "Do action was called") 20 | 21 | 22 | ErrorHandler() 23 | .onAFError(withStatus: 404) { (_) -> MatchingPolicy in 24 | actionCalled.fulfill() 25 | return .continueMatching 26 | } 27 | .handle(error) 28 | 29 | waitForExpectations(timeout: 1.0) 30 | } 31 | 32 | func testThatItDoesntMatchErrorWithOtherStatus() { 33 | let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 400)) 34 | let timeout = expectation(description: "Timeout for action to be called has passed") 35 | var actionCalled = false 36 | 37 | ErrorHandler() 38 | .onAFError(withStatus: 404) { (error) -> MatchingPolicy in 39 | actionCalled = true 40 | return .continueMatching 41 | }.handle(error) 42 | 43 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { 44 | timeout.fulfill() 45 | } 46 | 47 | waitForExpectations(timeout: 1.0) 48 | 49 | XCTAssertFalse(actionCalled) 50 | } 51 | 52 | func testThatItMatchesErrorInCorrectRange() { 53 | let code = 404 54 | let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: code)) 55 | let actionCalled = expectation(description: "Do action was called") 56 | 57 | 58 | ErrorHandler() 59 | .onAFError(withStatus: 400..<500) { (_) -> MatchingPolicy in 60 | actionCalled.fulfill() 61 | return .continueMatching 62 | } 63 | .handle(error) 64 | 65 | waitForExpectations(timeout: 1.0) 66 | } 67 | 68 | func testThatItDoesNotMatchErrorOutsideRange() { 69 | let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 500)) 70 | let timeout = expectation(description: "Timeout for action to be called has passed") 71 | var actionCalled = false 72 | 73 | ErrorHandler() 74 | .onAFError(withStatus: 400..<430) { (error) -> MatchingPolicy in 75 | actionCalled = true 76 | return .continueMatching 77 | }.handle(error) 78 | 79 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { 80 | timeout.fulfill() 81 | } 82 | 83 | waitForExpectations(timeout: 1.0) 84 | 85 | XCTAssertFalse(actionCalled) 86 | } 87 | 88 | func testThatItDoesNotMatchAFErrorOfDifferentReason() { 89 | let error = AFError.responseValidationFailed(reason: .dataFileNil) 90 | let timeout = expectation(description: "Timeout for action to be called has passed") 91 | var actionCalled = false 92 | 93 | ErrorHandler() 94 | .onAFError(withStatus: 400..<430) { (error) -> MatchingPolicy in 95 | actionCalled = true 96 | return .continueMatching 97 | }.handle(error) 98 | 99 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { 100 | timeout.fulfill() 101 | } 102 | 103 | waitForExpectations(timeout: 1.0) 104 | 105 | XCTAssertFalse(actionCalled) 106 | } 107 | 108 | func testThatItDoesntMatchOtherTypesOfAFErrorReasons() { 109 | let error = AFError.parameterEncodingFailed(reason: .missingURL) 110 | 111 | let timeout = expectation(description: "Timeout for action to be called has passed") 112 | var actionCalled = false 113 | 114 | ErrorHandler() 115 | .onAFError(withStatus: 400..<430) { (error) -> MatchingPolicy in 116 | actionCalled = true 117 | return .continueMatching 118 | }.handle(error) 119 | 120 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { 121 | timeout.fulfill() 122 | } 123 | 124 | waitForExpectations(timeout: 1.0) 125 | 126 | XCTAssertFalse(actionCalled) 127 | } 128 | 129 | func testThatItDoesntMatchOtherTypesOfErrors() { 130 | let error = NSError(domain: "foo", code: 9, userInfo: nil) 131 | 132 | 133 | let timeout = expectation(description: "Timeout for action to be called has passed") 134 | var actionCalled = false 135 | 136 | ErrorHandler() 137 | .onAFError(withStatus: 400..<430) { (error) -> MatchingPolicy in 138 | actionCalled = true 139 | return .continueMatching 140 | }.handle(error) 141 | 142 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { 143 | timeout.fulfill() 144 | } 145 | 146 | waitForExpectations(timeout: 1.0) 147 | 148 | XCTAssertFalse(actionCalled) 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Example/Tests/ErrorTypeMatcherTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorTypeMatcherTests.swift 3 | // ErrorHandler-iOS Tests 4 | // 5 | // Created by Kostas Kremizas on 25/07/2017. 6 | // Copyright © 2017 Workable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ErrorHandler 11 | 12 | class ErrorTypeMatcherTests: XCTestCase { 13 | 14 | func testThatErrorTypeMatcherMatchesErrorOfSameType() { 15 | 16 | // Arrange 17 | struct CustomError: Error {} 18 | let sut = ErrorTypeMatcher() 19 | let error: Error = CustomError() 20 | 21 | // Act - Assert 22 | XCTAssertTrue(sut.matches(error)) 23 | } 24 | 25 | func testThatErrorTypeMatcherDoesNotMatchErrorOfDifferentType() { 26 | 27 | // Arrange 28 | struct CustomError: Error {} 29 | let sut = ErrorTypeMatcher() 30 | 31 | struct AnotherCustomError: Error {} 32 | let error: Error = AnotherCustomError() 33 | 34 | // Act - Assert 35 | XCTAssertFalse(sut.matches(error)) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/NSErrorMatcherTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSErrorMatcherTests.swift 3 | // ErrorHandler-iOS 4 | // 5 | // Created by Eleni Papanikolopoulou on 05/08/2017. 6 | // Copyright © 2017 Workable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ErrorHandler 11 | 12 | class NSErrorMatcherTests: XCTestCase { 13 | 14 | func testThatItMatchesNsErrorsWithSameDomainAndCode() { 15 | 16 | // Arrange 17 | let domain = "test_domain" 18 | let nsError = NSError(domain: domain, code: 1, userInfo: nil) 19 | let sut = NSErrorMatcher(domain: domain, code: 1) 20 | 21 | // Act - Assert 22 | XCTAssertTrue(sut.matches(nsError)) 23 | } 24 | 25 | func testThatItDoesNotMatchErrorsOfDifferentType() { 26 | // Arrange 27 | let nsError = NSError(domain: "test_domain", code: 1, userInfo: nil) 28 | let sut = NSErrorMatcher(domain: "different_domain", code: 1) 29 | 30 | // Act - Assert 31 | XCTAssertFalse(sut.matches(nsError)) 32 | } 33 | 34 | func testThatItMatchesNsErrorsWithSameDomainIfCodeIsNill() { 35 | // Arrange 36 | let domain = "test_domain" 37 | let nsError = NSError(domain: domain, code: 3, userInfo: nil) 38 | let sut = NSErrorMatcher(domain: domain, code: nil) 39 | 40 | //Act - Assert 41 | XCTAssertTrue(sut.matches(nsError)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Workable SA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "ErrorHandler", 6 | platforms: [ 7 | .iOS(.v10), 8 | .macOS(.v10_12), 9 | .watchOS(.v3), 10 | .tvOS(.v10) 11 | ], 12 | products: [ 13 | .library( 14 | name: "ErrorHandler", 15 | targets: ["ErrorHandler"]), 16 | ], 17 | dependencies: [ 18 | .package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.0.0")) 19 | ], 20 | targets: [ 21 | .target(name: "ErrorHandler", dependencies: ["Alamofire"], path: "./ErrorHandler"), 22 | .testTarget(name: "ErrorHandlerTests", dependencies: ["ErrorHandler"], path: "./Example/Tests") 23 | ], 24 | swiftLanguageVersions: [.v4_2, .v5] 25 | ) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Error Handler](https://github.com/Workable/swift-error-handler/blob/master/ErrorHandler.png) 2 | 3 | [![Travis](https://travis-ci.org/Workable/swift-error-handler.svg?branch=master)](https://travis-ci.org/Workable/swift-error-handler) 4 | 5 | > Elegant and flexible error handling for Swift 6 | 7 | ErrorHandler enables expressing complex error handling logic with a few lines of code using a memorable fluent API. 8 | 9 | 10 | ## Installation 11 | 12 | ### CocoaPods 13 | 14 | To integrate `ErrorHandler` into your Xcode project using CocoaPods, use the following entry in your `Podfile`: 15 | 16 | ```ruby 17 | target '' do 18 | pod 'ErrorHandler' 19 | end 20 | ``` 21 | 22 | or if you are using Alamofire and want to take advantage of the `ErrorHandler`s convenience extensions for handling `Alamofire` errors with invalid http statuses 23 | 24 | 25 | ```ruby 26 | target '' do 27 | pod 'ErrorHandler' 28 | pod 'ErrorHandler/Alamofire' 29 | end 30 | ``` 31 | 32 | Then, run the following command: 33 | 34 | ```bash 35 | $ pod install 36 | ``` 37 | 38 | ### Carthage 39 | 40 | To integrate `ErrorHandler` into your Xcode project using Carthage, specify it in your `Cartfile`: 41 | 42 | ```ogdl 43 | github "Workable/swift-error-handler" 44 | ``` 45 | 46 | Run `carthage update` to build the framework and drag the built `ErrorHandler.framework` into your Xcode project. 47 | 48 | ### Swift Package Manager 49 | 50 | To integrate using Apple's Swift package manager, add the following as a dependency to your Package.swift: 51 | 52 | ```swift 53 | import PackageDescription 54 | 55 | let package = Package( 56 | name: "MyApp", 57 | dependencies: [ 58 | .package(url: "https://github.com/Workable/swift-error-handler.git", from: "0.8") 59 | ] 60 | ) 61 | ``` 62 | 63 | 64 | ## Usage 65 | 66 | Let's say we're building a messaging iOS app that uses both the network and a local database. 67 | 68 | We need to: 69 | 70 | ### Setup a default ErrorHandler once 71 | 72 | The default ErrorHandler will contain the error handling logic that is common across your application and you don't want to duplicate. You can create a factory that creates it so that you can get new instance with the common handling logic from anywhere in your app. 73 | 74 | ```swift 75 | extension ErrorHandler { 76 | class var defaultHandler: ErrorHandler { 77 | 78 | return ErrorHandler() 79 | 80 | // Τhe error matches and the action is called if the matches closure returns true 81 | .on(matches: { (error) -> Bool in 82 | guard let error = error as? InvalidInputsError else { return false } 83 | // we will ignore errors with code == 5 84 | return error.code != 5 85 | }, do: { (error) in 86 | showErrorAlert("Invalid Inputs") 87 | return .continueMatching 88 | }) 89 | 90 | // Variant using ErrorMatcher which is convenient if you want to 91 | // share the same matching logic elsewhere 92 | .on(InvalidStateMatcher(), do: { (_) in 93 | showErrorAlert("An error has occurred. Please restart the app.") 94 | return .continueMatching 95 | }) 96 | 97 | // Handle all errors of the same type the same way 98 | .onError(ofType: ParsingError.self, do: { (error) in 99 | doSomething(with: error) 100 | return .continueMatching 101 | }) 102 | 103 | // Handle a specific instance of an Equatable error type 104 | .on(DBError.migrationNeeded, do: { (_) in 105 | // Db.migrate() 106 | return .continueMatching 107 | }) 108 | 109 | // You can tag matchers or matches functions in order to reuse them with a more memorable alias. 110 | // You can use the same tag for many matchers. This way you can group them and handle their errors together. 111 | .tag(NSErrorMatcher(domain: NSURLErrorDomain, code: NSURLErrorNetworkConnectionLost), 112 | with: "ConnectionError" 113 | ) 114 | .tag(NSErrorMatcher(domain: NSURLErrorDomain, code: NSURLErrorNotConnectedToInternet), 115 | with: "ConnectionError" 116 | ) 117 | .on(tag: "ConnectionError") { (_) in 118 | showErrorAlert("You are not connected to the Internet. Please check your connection and retry.") 119 | return .continueMatching 120 | } 121 | 122 | // You can use the Alamofire extensions to easily handle responses with invalid http status 123 | .onAFError(withStatus: 401, do: { (_) in 124 | showLoginScreen() 125 | return .continueMatching 126 | }) 127 | .onAFError(withStatus: 404, do: { (_) in 128 | showErrorAlert("Resource not found!") 129 | return .continueMatching 130 | }) 131 | 132 | // Handle unknown errors. 133 | .onNoMatch(do: { (_) in 134 | showErrorAlert("An error occurred! Please try again. ") 135 | return .continueMatching 136 | }) 137 | 138 | // Add actions - like logging - that you want to perform each time - whether the error was matched or not 139 | .always(do: { (error) in 140 | Logger.log(error) 141 | return .continueMatching 142 | }) 143 | } 144 | } 145 | ``` 146 | ### Use the default handler to handle common cases 147 | 148 | Often the cases the default handler knows about will be good enough. 149 | 150 | ```swift 151 | do { 152 | try saveStatus() 153 | } catch { 154 | ErrorHandler.defaultHandler.handle(error) 155 | } 156 | ``` 157 | 158 | or use the `tryWith` free function: 159 | 160 | ```swift 161 | tryWith(ErrorHandler.defaultHandler) { 162 | try saveStatus() 163 | } 164 | ``` 165 | ### Customize the error handler when needed. 166 | 167 | In cases where extra context is available you can add more cases or override the ones provided already. 168 | 169 | For example in a SendMessageViewController 170 | 171 | ```swift 172 | sendMessage(message) { (response, error) in 173 | 174 | if let error = error { 175 | ErrorHandler.defaultHandler 176 | .on(ValidationError.invalidEmail, do: { (_) in 177 | updateEmailTextFieldForValidationError() 178 | return .continueMatching 179 | }) 180 | .onAFError(withStatus: 404, do: { (_) in 181 | doSomethingSpecificFor404() 182 | return .stopMatching 183 | }) 184 | .onNoMatch(do: { (_) in 185 | // In the context of the current screen we can provide a better message. 186 | showErrorAlert("An error occurred! The message has not been sent.") 187 | // We want to override the default onNoMatch handling so we stop searching for other matches. 188 | return .stopMatching 189 | }) 190 | .handle(error) 191 | } 192 | } 193 | ``` 194 | 195 | 196 | ## Why? 197 | 198 | When designing for errors, we usually need to: 199 | 200 | 1. have a **default** handler for **expected** errors 201 | // i.e. network, db errors etc. 202 | 2. handle **specific** errors **in a custom manner** given **the context** of where and when they occur 203 | // i.e. network error while uploading a file, invalid login 204 | 3. have a **catch-all** handler for **unknown** errors 205 | // i.e. errors we don't have custom handling for 206 | 4. perform some **actions** for **all errors** both known and unknown like logging 207 | 5. keep our code **DRY** 208 | 209 | Swift's has a very well thought error handling model keeping balance between convenience ([automatic propagation](https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#automatic-propagation)) and clarity-safety ([Typed propagation](https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#id3), [Marked propagation](https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#id4)). As a result, the compiler serves as a reminder of errors that need to be handled and at the same type it is relatively easy to propagate errors and handle them higher up the stack. 210 | 211 | However, even with this help from the language, achieving the goals listed above in an **ad-hoc** manner in an application of a reasonable size can lead to a lot of **boilerplate** which is **tedious** to write and reason about. Because of this friction developers quite often choose to swallow errors or handle them all in the same generic way. 212 | 213 | This library addresses these issues by providing an abstraction over defining flexible error handling rules with an opinionated fluent API. 214 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------