├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── MultipartFormData.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── SessionDelegate.swift │ │ ├── SessionManager.swift │ │ ├── TaskDelegate.swift │ │ ├── Timeline.swift │ │ └── Validation.swift ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── neo.xcuserdatad │ │ └── xcschemes │ │ ├── Alamofire.xcscheme │ │ ├── Pods-reachability-playground.xcscheme │ │ ├── ReachabilitySwift.xcscheme │ │ └── xcschememanagement.plist ├── ReachabilitySwift │ ├── LICENSE │ ├── README.md │ └── Reachability │ │ └── Reachability.swift └── Target Support Files │ ├── Alamofire │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.modulemap │ ├── Alamofire.xcconfig │ └── Info.plist │ ├── Pods-reachability-playground │ ├── Info.plist │ ├── Pods-reachability-playground-acknowledgements.markdown │ ├── Pods-reachability-playground-acknowledgements.plist │ ├── Pods-reachability-playground-dummy.m │ ├── Pods-reachability-playground-frameworks.sh │ ├── Pods-reachability-playground-resources.sh │ ├── Pods-reachability-playground-umbrella.h │ ├── Pods-reachability-playground.debug.xcconfig │ ├── Pods-reachability-playground.modulemap │ └── Pods-reachability-playground.release.xcconfig │ └── ReachabilitySwift │ ├── Info.plist │ ├── ReachabilitySwift-dummy.m │ ├── ReachabilitySwift-prefix.pch │ ├── ReachabilitySwift-umbrella.h │ ├── ReachabilitySwift.modulemap │ └── ReachabilitySwift.xcconfig ├── README.md ├── reachability-playground.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── neo.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── reachability-playground.xcworkspace └── contents.xcworkspacedata └── reachability-playground ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json └── offline.imageset │ ├── Contents.json │ └── offline.png ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Connection.swift ├── Info.plist ├── LaunchViewController.swift ├── OfflineViewController.swift └── PostsTableViewController.swift /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | target 'reachability-playground' do 4 | use_frameworks! 5 | 6 | pod 'ReachabilitySwift' 7 | pod 'Alamofire' 8 | end 9 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.5.1) 3 | - ReachabilitySwift (4.1.0) 4 | 5 | DEPENDENCIES: 6 | - Alamofire 7 | - ReachabilitySwift 8 | 9 | SPEC CHECKSUMS: 10 | Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a 11 | ReachabilitySwift: 6849231cd4e06559f3b9ef4a97a0a0f96d41e09f 12 | 13 | PODFILE CHECKSUM: a18f860cc555a0134c4502f6664d8b54e4f0e089 14 | 15 | COCOAPODS: 1.3.1 16 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2017 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 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) } 30 | static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) } 31 | static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) } 32 | static var background: DispatchQueue { return DispatchQueue.global(qos: .background) } 33 | 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/NetworkReachabilityManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManager.swift 3 | // 4 | // Copyright (c) 2014-2017 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) 26 | 27 | import Foundation 28 | import SystemConfiguration 29 | 30 | /// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN 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 | public class NetworkReachabilityManager { 37 | /// Defines the various states of network reachability. 38 | /// 39 | /// - unknown: It is unknown whether the network is reachable. 40 | /// - notReachable: The network is not reachable. 41 | /// - reachable: The network is reachable. 42 | public enum NetworkReachabilityStatus { 43 | case unknown 44 | case notReachable 45 | case reachable(ConnectionType) 46 | } 47 | 48 | /// Defines the various connection types detected by reachability flags. 49 | /// 50 | /// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi. 51 | /// - wwan: The connection type is a WWAN connection. 52 | public enum ConnectionType { 53 | case ethernetOrWiFi 54 | case wwan 55 | } 56 | 57 | /// A closure executed when the network reachability status changes. The closure takes a single argument: the 58 | /// network reachability status. 59 | public typealias Listener = (NetworkReachabilityStatus) -> Void 60 | 61 | // MARK: - Properties 62 | 63 | /// Whether the network is currently reachable. 64 | public var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } 65 | 66 | /// Whether the network is currently reachable over the WWAN interface. 67 | public var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) } 68 | 69 | /// Whether the network is currently reachable over Ethernet or WiFi interface. 70 | public var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) } 71 | 72 | /// The current network reachability status. 73 | public var networkReachabilityStatus: NetworkReachabilityStatus { 74 | guard let flags = self.flags else { return .unknown } 75 | return networkReachabilityStatusForFlags(flags) 76 | } 77 | 78 | /// The dispatch queue to execute the `listener` closure on. 79 | public var listenerQueue: DispatchQueue = DispatchQueue.main 80 | 81 | /// A closure executed when the network reachability status changes. 82 | public var listener: Listener? 83 | 84 | private var flags: SCNetworkReachabilityFlags? { 85 | var flags = SCNetworkReachabilityFlags() 86 | 87 | if SCNetworkReachabilityGetFlags(reachability, &flags) { 88 | return flags 89 | } 90 | 91 | return nil 92 | } 93 | 94 | private let reachability: SCNetworkReachability 95 | private var previousFlags: SCNetworkReachabilityFlags 96 | 97 | // MARK: - Initialization 98 | 99 | /// Creates a `NetworkReachabilityManager` instance with the specified host. 100 | /// 101 | /// - parameter host: The host used to evaluate network reachability. 102 | /// 103 | /// - returns: The new `NetworkReachabilityManager` instance. 104 | public convenience init?(host: String) { 105 | guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } 106 | self.init(reachability: reachability) 107 | } 108 | 109 | /// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0. 110 | /// 111 | /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing 112 | /// status of the device, both IPv4 and IPv6. 113 | /// 114 | /// - returns: The new `NetworkReachabilityManager` instance. 115 | public convenience init?() { 116 | var address = sockaddr_in() 117 | address.sin_len = UInt8(MemoryLayout.size) 118 | address.sin_family = sa_family_t(AF_INET) 119 | 120 | guard let reachability = withUnsafePointer(to: &address, { pointer in 121 | return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout.size) { 122 | return SCNetworkReachabilityCreateWithAddress(nil, $0) 123 | } 124 | }) else { return nil } 125 | 126 | self.init(reachability: reachability) 127 | } 128 | 129 | private init(reachability: SCNetworkReachability) { 130 | self.reachability = reachability 131 | self.previousFlags = SCNetworkReachabilityFlags() 132 | } 133 | 134 | deinit { 135 | stopListening() 136 | } 137 | 138 | // MARK: - Listening 139 | 140 | /// Starts listening for changes in network reachability status. 141 | /// 142 | /// - returns: `true` if listening was started successfully, `false` otherwise. 143 | @discardableResult 144 | public func startListening() -> Bool { 145 | var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) 146 | context.info = Unmanaged.passUnretained(self).toOpaque() 147 | 148 | let callbackEnabled = SCNetworkReachabilitySetCallback( 149 | reachability, 150 | { (_, flags, info) in 151 | let reachability = Unmanaged.fromOpaque(info!).takeUnretainedValue() 152 | reachability.notifyListener(flags) 153 | }, 154 | &context 155 | ) 156 | 157 | let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue) 158 | 159 | listenerQueue.async { 160 | self.previousFlags = SCNetworkReachabilityFlags() 161 | self.notifyListener(self.flags ?? SCNetworkReachabilityFlags()) 162 | } 163 | 164 | return callbackEnabled && queueEnabled 165 | } 166 | 167 | /// Stops listening for changes in network reachability status. 168 | public func stopListening() { 169 | SCNetworkReachabilitySetCallback(reachability, nil, nil) 170 | SCNetworkReachabilitySetDispatchQueue(reachability, nil) 171 | } 172 | 173 | // MARK: - Internal - Listener Notification 174 | 175 | func notifyListener(_ flags: SCNetworkReachabilityFlags) { 176 | guard previousFlags != flags else { return } 177 | previousFlags = flags 178 | 179 | listener?(networkReachabilityStatusForFlags(flags)) 180 | } 181 | 182 | // MARK: - Internal - Network Reachability Status 183 | 184 | func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus { 185 | guard isNetworkReachable(with: flags) else { return .notReachable } 186 | 187 | var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) 188 | 189 | #if os(iOS) 190 | if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } 191 | #endif 192 | 193 | return networkStatus 194 | } 195 | 196 | func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool { 197 | let isReachable = flags.contains(.reachable) 198 | let needsConnection = flags.contains(.connectionRequired) 199 | let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) 200 | let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired) 201 | 202 | return isReachable && (!needsConnection || canConnectWithoutUserInteraction) 203 | } 204 | } 205 | 206 | // MARK: - 207 | 208 | extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} 209 | 210 | /// Returns whether the two network reachability status values are equal. 211 | /// 212 | /// - parameter lhs: The left-hand side value to compare. 213 | /// - parameter rhs: The right-hand side value to compare. 214 | /// 215 | /// - returns: `true` if the two values are equal, `false` otherwise. 216 | public func ==( 217 | lhs: NetworkReachabilityManager.NetworkReachabilityStatus, 218 | rhs: NetworkReachabilityManager.NetworkReachabilityStatus) 219 | -> Bool 220 | { 221 | switch (lhs, rhs) { 222 | case (.unknown, .unknown): 223 | return true 224 | case (.notReachable, .notReachable): 225 | return true 226 | case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)): 227 | return lhsConnectionType == rhsConnectionType 228 | default: 229 | return false 230 | } 231 | } 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2017 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 Notification.Name { 28 | /// Used as a namespace for all `URLSessionTask` related notifications. 29 | public struct Task { 30 | /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`. 31 | public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume") 32 | 33 | /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`. 34 | public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend") 35 | 36 | /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`. 37 | public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel") 38 | 39 | /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`. 40 | public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete") 41 | } 42 | } 43 | 44 | // MARK: - 45 | 46 | extension Notification { 47 | /// Used as a namespace for all `Notification` user info dictionary keys. 48 | public struct Key { 49 | /// User info dictionary key representing the `URLSessionTask` associated with the notification. 50 | public static let Task = "org.alamofire.notification.key.task" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ParameterEncoding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParameterEncoding.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | /// HTTP method definitions. 28 | /// 29 | /// See https://tools.ietf.org/html/rfc7231#section-4.3 30 | public enum HTTPMethod: String { 31 | case options = "OPTIONS" 32 | case get = "GET" 33 | case head = "HEAD" 34 | case post = "POST" 35 | case put = "PUT" 36 | case patch = "PATCH" 37 | case delete = "DELETE" 38 | case trace = "TRACE" 39 | case connect = "CONNECT" 40 | } 41 | 42 | // MARK: - 43 | 44 | /// A dictionary of parameters to apply to a `URLRequest`. 45 | public typealias Parameters = [String: Any] 46 | 47 | /// A type used to define how a set of parameters are applied to a `URLRequest`. 48 | public protocol ParameterEncoding { 49 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 50 | /// 51 | /// - parameter urlRequest: The request to have parameters applied. 52 | /// - parameter parameters: The parameters to apply. 53 | /// 54 | /// - throws: An `AFError.parameterEncodingFailed` error if encoding fails. 55 | /// 56 | /// - returns: The encoded request. 57 | func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest 58 | } 59 | 60 | // MARK: - 61 | 62 | /// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP 63 | /// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as 64 | /// the HTTP body depends on the destination of the encoding. 65 | /// 66 | /// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to 67 | /// `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification for how to encode 68 | /// collection types, the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending 69 | /// the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`). 70 | public struct URLEncoding: ParameterEncoding { 71 | 72 | // MARK: Helper Types 73 | 74 | /// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the 75 | /// resulting URL request. 76 | /// 77 | /// - methodDependent: Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` 78 | /// requests and sets as the HTTP body for requests with any other HTTP method. 79 | /// - queryString: Sets or appends encoded query string result to existing query string. 80 | /// - httpBody: Sets encoded query string result as the HTTP body of the URL request. 81 | public enum Destination { 82 | case methodDependent, queryString, httpBody 83 | } 84 | 85 | // MARK: Properties 86 | 87 | /// Returns a default `URLEncoding` instance. 88 | public static var `default`: URLEncoding { return URLEncoding() } 89 | 90 | /// Returns a `URLEncoding` instance with a `.methodDependent` destination. 91 | public static var methodDependent: URLEncoding { return URLEncoding() } 92 | 93 | /// Returns a `URLEncoding` instance with a `.queryString` destination. 94 | public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) } 95 | 96 | /// Returns a `URLEncoding` instance with an `.httpBody` destination. 97 | public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) } 98 | 99 | /// The destination defining where the encoded query string is to be applied to the URL request. 100 | public let destination: Destination 101 | 102 | // MARK: Initialization 103 | 104 | /// Creates a `URLEncoding` instance using the specified destination. 105 | /// 106 | /// - parameter destination: The destination defining where the encoded query string is to be applied. 107 | /// 108 | /// - returns: The new `URLEncoding` instance. 109 | public init(destination: Destination = .methodDependent) { 110 | self.destination = destination 111 | } 112 | 113 | // MARK: Encoding 114 | 115 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 116 | /// 117 | /// - parameter urlRequest: The request to have parameters applied. 118 | /// - parameter parameters: The parameters to apply. 119 | /// 120 | /// - throws: An `Error` if the encoding process encounters an error. 121 | /// 122 | /// - returns: The encoded request. 123 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { 124 | var urlRequest = try urlRequest.asURLRequest() 125 | 126 | guard let parameters = parameters else { return urlRequest } 127 | 128 | if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) { 129 | guard let url = urlRequest.url else { 130 | throw AFError.parameterEncodingFailed(reason: .missingURL) 131 | } 132 | 133 | if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty { 134 | let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) 135 | urlComponents.percentEncodedQuery = percentEncodedQuery 136 | urlRequest.url = urlComponents.url 137 | } 138 | } else { 139 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 140 | urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type") 141 | } 142 | 143 | urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false) 144 | } 145 | 146 | return urlRequest 147 | } 148 | 149 | /// Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. 150 | /// 151 | /// - parameter key: The key of the query component. 152 | /// - parameter value: The value of the query component. 153 | /// 154 | /// - returns: The percent-escaped, URL encoded query string components. 155 | public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] { 156 | var components: [(String, String)] = [] 157 | 158 | if let dictionary = value as? [String: Any] { 159 | for (nestedKey, value) in dictionary { 160 | components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) 161 | } 162 | } else if let array = value as? [Any] { 163 | for value in array { 164 | components += queryComponents(fromKey: "\(key)[]", value: value) 165 | } 166 | } else if let value = value as? NSNumber { 167 | if value.isBool { 168 | components.append((escape(key), escape((value.boolValue ? "1" : "0")))) 169 | } else { 170 | components.append((escape(key), escape("\(value)"))) 171 | } 172 | } else if let bool = value as? Bool { 173 | components.append((escape(key), escape((bool ? "1" : "0")))) 174 | } else { 175 | components.append((escape(key), escape("\(value)"))) 176 | } 177 | 178 | return components 179 | } 180 | 181 | /// Returns a percent-escaped string following RFC 3986 for a query string key or value. 182 | /// 183 | /// RFC 3986 states that the following characters are "reserved" characters. 184 | /// 185 | /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/" 186 | /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" 187 | /// 188 | /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow 189 | /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" 190 | /// should be percent-escaped in the query string. 191 | /// 192 | /// - parameter string: The string to be percent-escaped. 193 | /// 194 | /// - returns: The percent-escaped string. 195 | public func escape(_ string: String) -> String { 196 | let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 197 | let subDelimitersToEncode = "!$&'()*+,;=" 198 | 199 | var allowedCharacterSet = CharacterSet.urlQueryAllowed 200 | allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") 201 | 202 | var escaped = "" 203 | 204 | //========================================================================================================== 205 | // 206 | // Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few 207 | // hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no 208 | // longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more 209 | // info, please refer to: 210 | // 211 | // - https://github.com/Alamofire/Alamofire/issues/206 212 | // 213 | //========================================================================================================== 214 | 215 | if #available(iOS 8.3, *) { 216 | escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string 217 | } else { 218 | let batchSize = 50 219 | var index = string.startIndex 220 | 221 | while index != string.endIndex { 222 | let startIndex = index 223 | let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex 224 | let range = startIndex.. String { 238 | var components: [(String, String)] = [] 239 | 240 | for key in parameters.keys.sorted(by: <) { 241 | let value = parameters[key]! 242 | components += queryComponents(fromKey: key, value: value) 243 | } 244 | return components.map { "\($0)=\($1)" }.joined(separator: "&") 245 | } 246 | 247 | private func encodesParametersInURL(with method: HTTPMethod) -> Bool { 248 | switch destination { 249 | case .queryString: 250 | return true 251 | case .httpBody: 252 | return false 253 | default: 254 | break 255 | } 256 | 257 | switch method { 258 | case .get, .head, .delete: 259 | return true 260 | default: 261 | return false 262 | } 263 | } 264 | } 265 | 266 | // MARK: - 267 | 268 | /// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the 269 | /// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. 270 | public struct JSONEncoding: ParameterEncoding { 271 | 272 | // MARK: Properties 273 | 274 | /// Returns a `JSONEncoding` instance with default writing options. 275 | public static var `default`: JSONEncoding { return JSONEncoding() } 276 | 277 | /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options. 278 | public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) } 279 | 280 | /// The options for writing the parameters as JSON data. 281 | public let options: JSONSerialization.WritingOptions 282 | 283 | // MARK: Initialization 284 | 285 | /// Creates a `JSONEncoding` instance using the specified options. 286 | /// 287 | /// - parameter options: The options for writing the parameters as JSON data. 288 | /// 289 | /// - returns: The new `JSONEncoding` instance. 290 | public init(options: JSONSerialization.WritingOptions = []) { 291 | self.options = options 292 | } 293 | 294 | // MARK: Encoding 295 | 296 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 297 | /// 298 | /// - parameter urlRequest: The request to have parameters applied. 299 | /// - parameter parameters: The parameters to apply. 300 | /// 301 | /// - throws: An `Error` if the encoding process encounters an error. 302 | /// 303 | /// - returns: The encoded request. 304 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { 305 | var urlRequest = try urlRequest.asURLRequest() 306 | 307 | guard let parameters = parameters else { return urlRequest } 308 | 309 | do { 310 | let data = try JSONSerialization.data(withJSONObject: parameters, options: options) 311 | 312 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 313 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 314 | } 315 | 316 | urlRequest.httpBody = data 317 | } catch { 318 | throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) 319 | } 320 | 321 | return urlRequest 322 | } 323 | 324 | /// Creates a URL request by encoding the JSON object and setting the resulting data on the HTTP body. 325 | /// 326 | /// - parameter urlRequest: The request to apply the JSON object to. 327 | /// - parameter jsonObject: The JSON object to apply to the request. 328 | /// 329 | /// - throws: An `Error` if the encoding process encounters an error. 330 | /// 331 | /// - returns: The encoded request. 332 | public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest { 333 | var urlRequest = try urlRequest.asURLRequest() 334 | 335 | guard let jsonObject = jsonObject else { return urlRequest } 336 | 337 | do { 338 | let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options) 339 | 340 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 341 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 342 | } 343 | 344 | urlRequest.httpBody = data 345 | } catch { 346 | throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) 347 | } 348 | 349 | return urlRequest 350 | } 351 | } 352 | 353 | // MARK: - 354 | 355 | /// Uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the 356 | /// associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header 357 | /// field of an encoded request is set to `application/x-plist`. 358 | public struct PropertyListEncoding: ParameterEncoding { 359 | 360 | // MARK: Properties 361 | 362 | /// Returns a default `PropertyListEncoding` instance. 363 | public static var `default`: PropertyListEncoding { return PropertyListEncoding() } 364 | 365 | /// Returns a `PropertyListEncoding` instance with xml formatting and default writing options. 366 | public static var xml: PropertyListEncoding { return PropertyListEncoding(format: .xml) } 367 | 368 | /// Returns a `PropertyListEncoding` instance with binary formatting and default writing options. 369 | public static var binary: PropertyListEncoding { return PropertyListEncoding(format: .binary) } 370 | 371 | /// The property list serialization format. 372 | public let format: PropertyListSerialization.PropertyListFormat 373 | 374 | /// The options for writing the parameters as plist data. 375 | public let options: PropertyListSerialization.WriteOptions 376 | 377 | // MARK: Initialization 378 | 379 | /// Creates a `PropertyListEncoding` instance using the specified format and options. 380 | /// 381 | /// - parameter format: The property list serialization format. 382 | /// - parameter options: The options for writing the parameters as plist data. 383 | /// 384 | /// - returns: The new `PropertyListEncoding` instance. 385 | public init( 386 | format: PropertyListSerialization.PropertyListFormat = .xml, 387 | options: PropertyListSerialization.WriteOptions = 0) 388 | { 389 | self.format = format 390 | self.options = options 391 | } 392 | 393 | // MARK: Encoding 394 | 395 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 396 | /// 397 | /// - parameter urlRequest: The request to have parameters applied. 398 | /// - parameter parameters: The parameters to apply. 399 | /// 400 | /// - throws: An `Error` if the encoding process encounters an error. 401 | /// 402 | /// - returns: The encoded request. 403 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { 404 | var urlRequest = try urlRequest.asURLRequest() 405 | 406 | guard let parameters = parameters else { return urlRequest } 407 | 408 | do { 409 | let data = try PropertyListSerialization.data( 410 | fromPropertyList: parameters, 411 | format: format, 412 | options: options 413 | ) 414 | 415 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 416 | urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") 417 | } 418 | 419 | urlRequest.httpBody = data 420 | } catch { 421 | throw AFError.parameterEncodingFailed(reason: .propertyListEncodingFailed(error: error)) 422 | } 423 | 424 | return urlRequest 425 | } 426 | } 427 | 428 | // MARK: - 429 | 430 | extension NSNumber { 431 | fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) } 432 | } 433 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | /// Used to store all data associated with an non-serialized response of a data or upload request. 28 | public struct DefaultDataResponse { 29 | /// The URL request sent to the server. 30 | public let request: URLRequest? 31 | 32 | /// The server's response to the URL request. 33 | public let response: HTTPURLResponse? 34 | 35 | /// The data returned by the server. 36 | public let data: Data? 37 | 38 | /// The error encountered while executing or validating the request. 39 | public let error: Error? 40 | 41 | /// The timeline of the complete lifecycle of the request. 42 | public let timeline: Timeline 43 | 44 | var _metrics: AnyObject? 45 | 46 | /// Creates a `DefaultDataResponse` instance from the specified parameters. 47 | /// 48 | /// - Parameters: 49 | /// - request: The URL request sent to the server. 50 | /// - response: The server's response to the URL request. 51 | /// - data: The data returned by the server. 52 | /// - error: The error encountered while executing or validating the request. 53 | /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default. 54 | /// - metrics: The task metrics containing the request / response statistics. `nil` by default. 55 | public init( 56 | request: URLRequest?, 57 | response: HTTPURLResponse?, 58 | data: Data?, 59 | error: Error?, 60 | timeline: Timeline = Timeline(), 61 | metrics: AnyObject? = nil) 62 | { 63 | self.request = request 64 | self.response = response 65 | self.data = data 66 | self.error = error 67 | self.timeline = timeline 68 | } 69 | } 70 | 71 | // MARK: - 72 | 73 | /// Used to store all data associated with a serialized response of a data or upload request. 74 | public struct DataResponse { 75 | /// The URL request sent to the server. 76 | public let request: URLRequest? 77 | 78 | /// The server's response to the URL request. 79 | public let response: HTTPURLResponse? 80 | 81 | /// The data returned by the server. 82 | public let data: Data? 83 | 84 | /// The result of response serialization. 85 | public let result: Result 86 | 87 | /// The timeline of the complete lifecycle of the request. 88 | public let timeline: Timeline 89 | 90 | /// Returns the associated value of the result if it is a success, `nil` otherwise. 91 | public var value: Value? { return result.value } 92 | 93 | /// Returns the associated error value if the result if it is a failure, `nil` otherwise. 94 | public var error: Error? { return result.error } 95 | 96 | var _metrics: AnyObject? 97 | 98 | /// Creates a `DataResponse` instance with the specified parameters derived from response serialization. 99 | /// 100 | /// - parameter request: The URL request sent to the server. 101 | /// - parameter response: The server's response to the URL request. 102 | /// - parameter data: The data returned by the server. 103 | /// - parameter result: The result of response serialization. 104 | /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. 105 | /// 106 | /// - returns: The new `DataResponse` instance. 107 | public init( 108 | request: URLRequest?, 109 | response: HTTPURLResponse?, 110 | data: Data?, 111 | result: Result, 112 | timeline: Timeline = Timeline()) 113 | { 114 | self.request = request 115 | self.response = response 116 | self.data = data 117 | self.result = result 118 | self.timeline = timeline 119 | } 120 | } 121 | 122 | // MARK: - 123 | 124 | extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible { 125 | /// The textual representation used when written to an output stream, which includes whether the result was a 126 | /// success or failure. 127 | public var description: String { 128 | return result.debugDescription 129 | } 130 | 131 | /// The debug textual representation used when written to an output stream, which includes the URL request, the URL 132 | /// response, the server data, the response serialization result and the timeline. 133 | public var debugDescription: String { 134 | var output: [String] = [] 135 | 136 | output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil") 137 | output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") 138 | output.append("[Data]: \(data?.count ?? 0) bytes") 139 | output.append("[Result]: \(result.debugDescription)") 140 | output.append("[Timeline]: \(timeline.debugDescription)") 141 | 142 | return output.joined(separator: "\n") 143 | } 144 | } 145 | 146 | // MARK: - 147 | 148 | extension DataResponse { 149 | /// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped 150 | /// result value as a parameter. 151 | /// 152 | /// Use the `map` method with a closure that does not throw. For example: 153 | /// 154 | /// let possibleData: DataResponse = ... 155 | /// let possibleInt = possibleData.map { $0.count } 156 | /// 157 | /// - parameter transform: A closure that takes the success value of the instance's result. 158 | /// 159 | /// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's 160 | /// result is a failure, returns a response wrapping the same failure. 161 | public func map(_ transform: (Value) -> T) -> DataResponse { 162 | var response = DataResponse( 163 | request: request, 164 | response: self.response, 165 | data: data, 166 | result: result.map(transform), 167 | timeline: timeline 168 | ) 169 | 170 | response._metrics = _metrics 171 | 172 | return response 173 | } 174 | 175 | /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result 176 | /// value as a parameter. 177 | /// 178 | /// Use the `flatMap` method with a closure that may throw an error. For example: 179 | /// 180 | /// let possibleData: DataResponse = ... 181 | /// let possibleObject = possibleData.flatMap { 182 | /// try JSONSerialization.jsonObject(with: $0) 183 | /// } 184 | /// 185 | /// - parameter transform: A closure that takes the success value of the instance's result. 186 | /// 187 | /// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's 188 | /// result is a failure, returns the same failure. 189 | public func flatMap(_ transform: (Value) throws -> T) -> DataResponse { 190 | var response = DataResponse( 191 | request: request, 192 | response: self.response, 193 | data: data, 194 | result: result.flatMap(transform), 195 | timeline: timeline 196 | ) 197 | 198 | response._metrics = _metrics 199 | 200 | return response 201 | } 202 | } 203 | 204 | // MARK: - 205 | 206 | /// Used to store all data associated with an non-serialized response of a download request. 207 | public struct DefaultDownloadResponse { 208 | /// The URL request sent to the server. 209 | public let request: URLRequest? 210 | 211 | /// The server's response to the URL request. 212 | public let response: HTTPURLResponse? 213 | 214 | /// The temporary destination URL of the data returned from the server. 215 | public let temporaryURL: URL? 216 | 217 | /// The final destination URL of the data returned from the server if it was moved. 218 | public let destinationURL: URL? 219 | 220 | /// The resume data generated if the request was cancelled. 221 | public let resumeData: Data? 222 | 223 | /// The error encountered while executing or validating the request. 224 | public let error: Error? 225 | 226 | /// The timeline of the complete lifecycle of the request. 227 | public let timeline: Timeline 228 | 229 | var _metrics: AnyObject? 230 | 231 | /// Creates a `DefaultDownloadResponse` instance from the specified parameters. 232 | /// 233 | /// - Parameters: 234 | /// - request: The URL request sent to the server. 235 | /// - response: The server's response to the URL request. 236 | /// - temporaryURL: The temporary destination URL of the data returned from the server. 237 | /// - destinationURL: The final destination URL of the data returned from the server if it was moved. 238 | /// - resumeData: The resume data generated if the request was cancelled. 239 | /// - error: The error encountered while executing or validating the request. 240 | /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default. 241 | /// - metrics: The task metrics containing the request / response statistics. `nil` by default. 242 | public init( 243 | request: URLRequest?, 244 | response: HTTPURLResponse?, 245 | temporaryURL: URL?, 246 | destinationURL: URL?, 247 | resumeData: Data?, 248 | error: Error?, 249 | timeline: Timeline = Timeline(), 250 | metrics: AnyObject? = nil) 251 | { 252 | self.request = request 253 | self.response = response 254 | self.temporaryURL = temporaryURL 255 | self.destinationURL = destinationURL 256 | self.resumeData = resumeData 257 | self.error = error 258 | self.timeline = timeline 259 | } 260 | } 261 | 262 | // MARK: - 263 | 264 | /// Used to store all data associated with a serialized response of a download request. 265 | public struct DownloadResponse { 266 | /// The URL request sent to the server. 267 | public let request: URLRequest? 268 | 269 | /// The server's response to the URL request. 270 | public let response: HTTPURLResponse? 271 | 272 | /// The temporary destination URL of the data returned from the server. 273 | public let temporaryURL: URL? 274 | 275 | /// The final destination URL of the data returned from the server if it was moved. 276 | public let destinationURL: URL? 277 | 278 | /// The resume data generated if the request was cancelled. 279 | public let resumeData: Data? 280 | 281 | /// The result of response serialization. 282 | public let result: Result 283 | 284 | /// The timeline of the complete lifecycle of the request. 285 | public let timeline: Timeline 286 | 287 | /// Returns the associated value of the result if it is a success, `nil` otherwise. 288 | public var value: Value? { return result.value } 289 | 290 | /// Returns the associated error value if the result if it is a failure, `nil` otherwise. 291 | public var error: Error? { return result.error } 292 | 293 | var _metrics: AnyObject? 294 | 295 | /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization. 296 | /// 297 | /// - parameter request: The URL request sent to the server. 298 | /// - parameter response: The server's response to the URL request. 299 | /// - parameter temporaryURL: The temporary destination URL of the data returned from the server. 300 | /// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved. 301 | /// - parameter resumeData: The resume data generated if the request was cancelled. 302 | /// - parameter result: The result of response serialization. 303 | /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. 304 | /// 305 | /// - returns: The new `DownloadResponse` instance. 306 | public init( 307 | request: URLRequest?, 308 | response: HTTPURLResponse?, 309 | temporaryURL: URL?, 310 | destinationURL: URL?, 311 | resumeData: Data?, 312 | result: Result, 313 | timeline: Timeline = Timeline()) 314 | { 315 | self.request = request 316 | self.response = response 317 | self.temporaryURL = temporaryURL 318 | self.destinationURL = destinationURL 319 | self.resumeData = resumeData 320 | self.result = result 321 | self.timeline = timeline 322 | } 323 | } 324 | 325 | // MARK: - 326 | 327 | extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible { 328 | /// The textual representation used when written to an output stream, which includes whether the result was a 329 | /// success or failure. 330 | public var description: String { 331 | return result.debugDescription 332 | } 333 | 334 | /// The debug textual representation used when written to an output stream, which includes the URL request, the URL 335 | /// response, the temporary and destination URLs, the resume data, the response serialization result and the 336 | /// timeline. 337 | public var debugDescription: String { 338 | var output: [String] = [] 339 | 340 | output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil") 341 | output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") 342 | output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")") 343 | output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")") 344 | output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes") 345 | output.append("[Result]: \(result.debugDescription)") 346 | output.append("[Timeline]: \(timeline.debugDescription)") 347 | 348 | return output.joined(separator: "\n") 349 | } 350 | } 351 | 352 | // MARK: - 353 | 354 | extension DownloadResponse { 355 | /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped 356 | /// result value as a parameter. 357 | /// 358 | /// Use the `map` method with a closure that does not throw. For example: 359 | /// 360 | /// let possibleData: DownloadResponse = ... 361 | /// let possibleInt = possibleData.map { $0.count } 362 | /// 363 | /// - parameter transform: A closure that takes the success value of the instance's result. 364 | /// 365 | /// - returns: A `DownloadResponse` whose result wraps the value returned by the given closure. If this instance's 366 | /// result is a failure, returns a response wrapping the same failure. 367 | public func map(_ transform: (Value) -> T) -> DownloadResponse { 368 | var response = DownloadResponse( 369 | request: request, 370 | response: self.response, 371 | temporaryURL: temporaryURL, 372 | destinationURL: destinationURL, 373 | resumeData: resumeData, 374 | result: result.map(transform), 375 | timeline: timeline 376 | ) 377 | 378 | response._metrics = _metrics 379 | 380 | return response 381 | } 382 | 383 | /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped 384 | /// result value as a parameter. 385 | /// 386 | /// Use the `flatMap` method with a closure that may throw an error. For example: 387 | /// 388 | /// let possibleData: DownloadResponse = ... 389 | /// let possibleObject = possibleData.flatMap { 390 | /// try JSONSerialization.jsonObject(with: $0) 391 | /// } 392 | /// 393 | /// - parameter transform: A closure that takes the success value of the instance's result. 394 | /// 395 | /// - returns: A success or failure `DownloadResponse` depending on the result of the given closure. If this 396 | /// instance's result is a failure, returns the same failure. 397 | public func flatMap(_ transform: (Value) throws -> T) -> DownloadResponse { 398 | var response = DownloadResponse( 399 | request: request, 400 | response: self.response, 401 | temporaryURL: temporaryURL, 402 | destinationURL: destinationURL, 403 | resumeData: resumeData, 404 | result: result.flatMap(transform), 405 | timeline: timeline 406 | ) 407 | 408 | response._metrics = _metrics 409 | 410 | return response 411 | } 412 | } 413 | 414 | // MARK: - 415 | 416 | protocol Response { 417 | /// The task metrics containing the request / response statistics. 418 | var _metrics: AnyObject? { get set } 419 | mutating func add(_ metrics: AnyObject?) 420 | } 421 | 422 | extension Response { 423 | mutating func add(_ metrics: AnyObject?) { 424 | #if !os(watchOS) 425 | guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return } 426 | guard let metrics = metrics as? URLSessionTaskMetrics else { return } 427 | 428 | _metrics = metrics 429 | #endif 430 | } 431 | } 432 | 433 | // MARK: - 434 | 435 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 436 | extension DefaultDataResponse: Response { 437 | #if !os(watchOS) 438 | /// The task metrics containing the request / response statistics. 439 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 440 | #endif 441 | } 442 | 443 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 444 | extension DataResponse: Response { 445 | #if !os(watchOS) 446 | /// The task metrics containing the request / response statistics. 447 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 448 | #endif 449 | } 450 | 451 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 452 | extension DefaultDownloadResponse: Response { 453 | #if !os(watchOS) 454 | /// The task metrics containing the request / response statistics. 455 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 456 | #endif 457 | } 458 | 459 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 460 | extension DownloadResponse: Response { 461 | #if !os(watchOS) 462 | /// The task metrics containing the request / response statistics. 463 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 464 | #endif 465 | } 466 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Result.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | /// Used to represent whether a request was successful or encountered an error. 28 | /// 29 | /// - success: The request and all post processing operations were successful resulting in the serialization of the 30 | /// provided associated value. 31 | /// 32 | /// - failure: The request encountered an error resulting in a failure. The associated values are the original data 33 | /// provided by the server as well as the error that caused the failure. 34 | public enum Result { 35 | case success(Value) 36 | case failure(Error) 37 | 38 | /// Returns `true` if the result is a success, `false` otherwise. 39 | public var isSuccess: Bool { 40 | switch self { 41 | case .success: 42 | return true 43 | case .failure: 44 | return false 45 | } 46 | } 47 | 48 | /// Returns `true` if the result is a failure, `false` otherwise. 49 | public var isFailure: Bool { 50 | return !isSuccess 51 | } 52 | 53 | /// Returns the associated value if the result is a success, `nil` otherwise. 54 | public var value: Value? { 55 | switch self { 56 | case .success(let value): 57 | return value 58 | case .failure: 59 | return nil 60 | } 61 | } 62 | 63 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 64 | public var error: Error? { 65 | switch self { 66 | case .success: 67 | return nil 68 | case .failure(let error): 69 | return error 70 | } 71 | } 72 | } 73 | 74 | // MARK: - CustomStringConvertible 75 | 76 | extension Result: CustomStringConvertible { 77 | /// The textual representation used when written to an output stream, which includes whether the result was a 78 | /// success or failure. 79 | public var description: String { 80 | switch self { 81 | case .success: 82 | return "SUCCESS" 83 | case .failure: 84 | return "FAILURE" 85 | } 86 | } 87 | } 88 | 89 | // MARK: - CustomDebugStringConvertible 90 | 91 | extension Result: CustomDebugStringConvertible { 92 | /// The debug textual representation used when written to an output stream, which includes whether the result was a 93 | /// success or failure in addition to the value or error. 94 | public var debugDescription: String { 95 | switch self { 96 | case .success(let value): 97 | return "SUCCESS: \(value)" 98 | case .failure(let error): 99 | return "FAILURE: \(error)" 100 | } 101 | } 102 | } 103 | 104 | // MARK: - Functional APIs 105 | 106 | extension Result { 107 | /// Creates a `Result` instance from the result of a closure. 108 | /// 109 | /// A failure result is created when the closure throws, and a success result is created when the closure 110 | /// succeeds without throwing an error. 111 | /// 112 | /// func someString() throws -> String { ... } 113 | /// 114 | /// let result = Result(value: { 115 | /// return try someString() 116 | /// }) 117 | /// 118 | /// // The type of result is Result 119 | /// 120 | /// The trailing closure syntax is also supported: 121 | /// 122 | /// let result = Result { try someString() } 123 | /// 124 | /// - parameter value: The closure to execute and create the result for. 125 | public init(value: () throws -> Value) { 126 | do { 127 | self = try .success(value()) 128 | } catch { 129 | self = .failure(error) 130 | } 131 | } 132 | 133 | /// Returns the success value, or throws the failure error. 134 | /// 135 | /// let possibleString: Result = .success("success") 136 | /// try print(possibleString.unwrap()) 137 | /// // Prints "success" 138 | /// 139 | /// let noString: Result = .failure(error) 140 | /// try print(noString.unwrap()) 141 | /// // Throws error 142 | public func unwrap() throws -> Value { 143 | switch self { 144 | case .success(let value): 145 | return value 146 | case .failure(let error): 147 | throw error 148 | } 149 | } 150 | 151 | /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. 152 | /// 153 | /// Use the `map` method with a closure that does not throw. For example: 154 | /// 155 | /// let possibleData: Result = .success(Data()) 156 | /// let possibleInt = possibleData.map { $0.count } 157 | /// try print(possibleInt.unwrap()) 158 | /// // Prints "0" 159 | /// 160 | /// let noData: Result = .failure(error) 161 | /// let noInt = noData.map { $0.count } 162 | /// try print(noInt.unwrap()) 163 | /// // Throws error 164 | /// 165 | /// - parameter transform: A closure that takes the success value of the `Result` instance. 166 | /// 167 | /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the 168 | /// same failure. 169 | public func map(_ transform: (Value) -> T) -> Result { 170 | switch self { 171 | case .success(let value): 172 | return .success(transform(value)) 173 | case .failure(let error): 174 | return .failure(error) 175 | } 176 | } 177 | 178 | /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. 179 | /// 180 | /// Use the `flatMap` method with a closure that may throw an error. For example: 181 | /// 182 | /// let possibleData: Result = .success(Data(...)) 183 | /// let possibleObject = possibleData.flatMap { 184 | /// try JSONSerialization.jsonObject(with: $0) 185 | /// } 186 | /// 187 | /// - parameter transform: A closure that takes the success value of the instance. 188 | /// 189 | /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the 190 | /// same failure. 191 | public func flatMap(_ transform: (Value) throws -> T) -> Result { 192 | switch self { 193 | case .success(let value): 194 | do { 195 | return try .success(transform(value)) 196 | } catch { 197 | return .failure(error) 198 | } 199 | case .failure(let error): 200 | return .failure(error) 201 | } 202 | } 203 | 204 | /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. 205 | /// 206 | /// Use the `mapError` function with a closure that does not throw. For example: 207 | /// 208 | /// let possibleData: Result = .failure(someError) 209 | /// let withMyError: Result = possibleData.mapError { MyError.error($0) } 210 | /// 211 | /// - Parameter transform: A closure that takes the error of the instance. 212 | /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns 213 | /// the same instance. 214 | public func mapError(_ transform: (Error) -> T) -> Result { 215 | switch self { 216 | case .failure(let error): 217 | return .failure(transform(error)) 218 | case .success: 219 | return self 220 | } 221 | } 222 | 223 | /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. 224 | /// 225 | /// Use the `flatMapError` function with a closure that may throw an error. For example: 226 | /// 227 | /// let possibleData: Result = .success(Data(...)) 228 | /// let possibleObject = possibleData.flatMapError { 229 | /// try someFailableFunction(taking: $0) 230 | /// } 231 | /// 232 | /// - Parameter transform: A throwing closure that takes the error of the instance. 233 | /// 234 | /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns 235 | /// the same instance. 236 | public func flatMapError(_ transform: (Error) throws -> T) -> Result { 237 | switch self { 238 | case .failure(let error): 239 | do { 240 | return try .failure(transform(error)) 241 | } catch { 242 | return .failure(error) 243 | } 244 | case .success: 245 | return self 246 | } 247 | } 248 | 249 | /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. 250 | /// 251 | /// Use the `withValue` function to evaluate the passed closure without modifying the `Result` instance. 252 | /// 253 | /// - Parameter closure: A closure that takes the success value of this instance. 254 | /// - Returns: This `Result` instance, unmodified. 255 | @discardableResult 256 | public func withValue(_ closure: (Value) -> Void) -> Result { 257 | if case let .success(value) = self { closure(value) } 258 | 259 | return self 260 | } 261 | 262 | /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. 263 | /// 264 | /// Use the `withError` function to evaluate the passed closure without modifying the `Result` instance. 265 | /// 266 | /// - Parameter closure: A closure that takes the success value of this instance. 267 | /// - Returns: This `Result` instance, unmodified. 268 | @discardableResult 269 | public func withError(_ closure: (Error) -> Void) -> Result { 270 | if case let .failure(error) = self { closure(error) } 271 | 272 | return self 273 | } 274 | 275 | /// Evaluates the specified closure when the `Result` is a success. 276 | /// 277 | /// Use the `ifSuccess` function to evaluate the passed closure without modifying the `Result` instance. 278 | /// 279 | /// - Parameter closure: A `Void` closure. 280 | /// - Returns: This `Result` instance, unmodified. 281 | @discardableResult 282 | public func ifSuccess(_ closure: () -> Void) -> Result { 283 | if isSuccess { closure() } 284 | 285 | return self 286 | } 287 | 288 | /// Evaluates the specified closure when the `Result` is a failure. 289 | /// 290 | /// Use the `ifFailure` function to evaluate the passed closure without modifying the `Result` instance. 291 | /// 292 | /// - Parameter closure: A `Void` closure. 293 | /// - Returns: This `Result` instance, unmodified. 294 | @discardableResult 295 | public func ifFailure(_ closure: () -> Void) -> Result { 296 | if isFailure { closure() } 297 | 298 | return self 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ServerTrustPolicy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerTrustPolicy.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | /// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host. 28 | open class ServerTrustPolicyManager { 29 | /// The dictionary of policies mapped to a particular host. 30 | open let policies: [String: ServerTrustPolicy] 31 | 32 | /// Initializes the `ServerTrustPolicyManager` instance with the given policies. 33 | /// 34 | /// Since different servers and web services can have different leaf certificates, intermediate and even root 35 | /// certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This 36 | /// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key 37 | /// pinning for host3 and disabling evaluation for host4. 38 | /// 39 | /// - parameter policies: A dictionary of all policies mapped to a particular host. 40 | /// 41 | /// - returns: The new `ServerTrustPolicyManager` instance. 42 | public init(policies: [String: ServerTrustPolicy]) { 43 | self.policies = policies 44 | } 45 | 46 | /// Returns the `ServerTrustPolicy` for the given host if applicable. 47 | /// 48 | /// By default, this method will return the policy that perfectly matches the given host. Subclasses could override 49 | /// this method and implement more complex mapping implementations such as wildcards. 50 | /// 51 | /// - parameter host: The host to use when searching for a matching policy. 52 | /// 53 | /// - returns: The server trust policy for the given host if found. 54 | open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? { 55 | return policies[host] 56 | } 57 | } 58 | 59 | // MARK: - 60 | 61 | extension URLSession { 62 | private struct AssociatedKeys { 63 | static var managerKey = "URLSession.ServerTrustPolicyManager" 64 | } 65 | 66 | var serverTrustPolicyManager: ServerTrustPolicyManager? { 67 | get { 68 | return objc_getAssociatedObject(self, &AssociatedKeys.managerKey) as? ServerTrustPolicyManager 69 | } 70 | set (manager) { 71 | objc_setAssociatedObject(self, &AssociatedKeys.managerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 72 | } 73 | } 74 | } 75 | 76 | // MARK: - ServerTrustPolicy 77 | 78 | /// The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when 79 | /// connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust 80 | /// with a given set of criteria to determine whether the server trust is valid and the connection should be made. 81 | /// 82 | /// Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) attacks and other 83 | /// vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged 84 | /// to route all communication over an HTTPS connection with pinning enabled. 85 | /// 86 | /// - performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to 87 | /// validate the host provided by the challenge. Applications are encouraged to always 88 | /// validate the host in production environments to guarantee the validity of the server's 89 | /// certificate chain. 90 | /// 91 | /// - performRevokedEvaluation: Uses the default and revoked server trust evaluations allowing you to control whether to 92 | /// validate the host provided by the challenge as well as specify the revocation flags for 93 | /// testing for revoked certificates. Apple platforms did not start testing for revoked 94 | /// certificates automatically until iOS 10.1, macOS 10.12 and tvOS 10.1 which is 95 | /// demonstrated in our TLS tests. Applications are encouraged to always validate the host 96 | /// in production environments to guarantee the validity of the server's certificate chain. 97 | /// 98 | /// - pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is 99 | /// considered valid if one of the pinned certificates match one of the server certificates. 100 | /// By validating both the certificate chain and host, certificate pinning provides a very 101 | /// secure form of server trust validation mitigating most, if not all, MITM attacks. 102 | /// Applications are encouraged to always validate the host and require a valid certificate 103 | /// chain in production environments. 104 | /// 105 | /// - pinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered 106 | /// valid if one of the pinned public keys match one of the server certificate public keys. 107 | /// By validating both the certificate chain and host, public key pinning provides a very 108 | /// secure form of server trust validation mitigating most, if not all, MITM attacks. 109 | /// Applications are encouraged to always validate the host and require a valid certificate 110 | /// chain in production environments. 111 | /// 112 | /// - disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid. 113 | /// 114 | /// - customEvaluation: Uses the associated closure to evaluate the validity of the server trust. 115 | public enum ServerTrustPolicy { 116 | case performDefaultEvaluation(validateHost: Bool) 117 | case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags) 118 | case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) 119 | case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) 120 | case disableEvaluation 121 | case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool) 122 | 123 | // MARK: - Bundle Location 124 | 125 | /// Returns all certificates within the given bundle with a `.cer` file extension. 126 | /// 127 | /// - parameter bundle: The bundle to search for all `.cer` files. 128 | /// 129 | /// - returns: All certificates within the given bundle. 130 | public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] { 131 | var certificates: [SecCertificate] = [] 132 | 133 | let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in 134 | bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil) 135 | }.joined()) 136 | 137 | for path in paths { 138 | if 139 | let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData, 140 | let certificate = SecCertificateCreateWithData(nil, certificateData) 141 | { 142 | certificates.append(certificate) 143 | } 144 | } 145 | 146 | return certificates 147 | } 148 | 149 | /// Returns all public keys within the given bundle with a `.cer` file extension. 150 | /// 151 | /// - parameter bundle: The bundle to search for all `*.cer` files. 152 | /// 153 | /// - returns: All public keys within the given bundle. 154 | public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] { 155 | var publicKeys: [SecKey] = [] 156 | 157 | for certificate in certificates(in: bundle) { 158 | if let publicKey = publicKey(for: certificate) { 159 | publicKeys.append(publicKey) 160 | } 161 | } 162 | 163 | return publicKeys 164 | } 165 | 166 | // MARK: - Evaluation 167 | 168 | /// Evaluates whether the server trust is valid for the given host. 169 | /// 170 | /// - parameter serverTrust: The server trust to evaluate. 171 | /// - parameter host: The host of the challenge protection space. 172 | /// 173 | /// - returns: Whether the server trust is valid. 174 | public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool { 175 | var serverTrustIsValid = false 176 | 177 | switch self { 178 | case let .performDefaultEvaluation(validateHost): 179 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 180 | SecTrustSetPolicies(serverTrust, policy) 181 | 182 | serverTrustIsValid = trustIsValid(serverTrust) 183 | case let .performRevokedEvaluation(validateHost, revocationFlags): 184 | let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 185 | let revokedPolicy = SecPolicyCreateRevocation(revocationFlags) 186 | SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef) 187 | 188 | serverTrustIsValid = trustIsValid(serverTrust) 189 | case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost): 190 | if validateCertificateChain { 191 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 192 | SecTrustSetPolicies(serverTrust, policy) 193 | 194 | SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray) 195 | SecTrustSetAnchorCertificatesOnly(serverTrust, true) 196 | 197 | serverTrustIsValid = trustIsValid(serverTrust) 198 | } else { 199 | let serverCertificatesDataArray = certificateData(for: serverTrust) 200 | let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates) 201 | 202 | outerLoop: for serverCertificateData in serverCertificatesDataArray { 203 | for pinnedCertificateData in pinnedCertificatesDataArray { 204 | if serverCertificateData == pinnedCertificateData { 205 | serverTrustIsValid = true 206 | break outerLoop 207 | } 208 | } 209 | } 210 | } 211 | case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost): 212 | var certificateChainEvaluationPassed = true 213 | 214 | if validateCertificateChain { 215 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 216 | SecTrustSetPolicies(serverTrust, policy) 217 | 218 | certificateChainEvaluationPassed = trustIsValid(serverTrust) 219 | } 220 | 221 | if certificateChainEvaluationPassed { 222 | outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] { 223 | for pinnedPublicKey in pinnedPublicKeys as [AnyObject] { 224 | if serverPublicKey.isEqual(pinnedPublicKey) { 225 | serverTrustIsValid = true 226 | break outerLoop 227 | } 228 | } 229 | } 230 | } 231 | case .disableEvaluation: 232 | serverTrustIsValid = true 233 | case let .customEvaluation(closure): 234 | serverTrustIsValid = closure(serverTrust, host) 235 | } 236 | 237 | return serverTrustIsValid 238 | } 239 | 240 | // MARK: - Private - Trust Validation 241 | 242 | private func trustIsValid(_ trust: SecTrust) -> Bool { 243 | var isValid = false 244 | 245 | var result = SecTrustResultType.invalid 246 | let status = SecTrustEvaluate(trust, &result) 247 | 248 | if status == errSecSuccess { 249 | let unspecified = SecTrustResultType.unspecified 250 | let proceed = SecTrustResultType.proceed 251 | 252 | 253 | isValid = result == unspecified || result == proceed 254 | } 255 | 256 | return isValid 257 | } 258 | 259 | // MARK: - Private - Certificate Data 260 | 261 | private func certificateData(for trust: SecTrust) -> [Data] { 262 | var certificates: [SecCertificate] = [] 263 | 264 | for index in 0.. [Data] { 274 | return certificates.map { SecCertificateCopyData($0) as Data } 275 | } 276 | 277 | // MARK: - Private - Public Key Extraction 278 | 279 | private static func publicKeys(for trust: SecTrust) -> [SecKey] { 280 | var publicKeys: [SecKey] = [] 281 | 282 | for index in 0.. SecKey? { 295 | var publicKey: SecKey? 296 | 297 | let policy = SecPolicyCreateBasicX509() 298 | var trust: SecTrust? 299 | let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust) 300 | 301 | if let trust = trust, trustCreationStatus == errSecSuccess { 302 | publicKey = SecTrustCopyPublicKey(trust) 303 | } 304 | 305 | return publicKey 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/TaskDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TaskDelegate.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | /// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as 28 | /// executing all operations attached to the serial operation queue upon task completion. 29 | open class TaskDelegate: NSObject { 30 | 31 | // MARK: Properties 32 | 33 | /// The serial operation queue used to execute all operations after the task completes. 34 | open let queue: OperationQueue 35 | 36 | /// The data returned by the server. 37 | public var data: Data? { return nil } 38 | 39 | /// The error generated throughout the lifecyle of the task. 40 | public var error: Error? 41 | 42 | var task: URLSessionTask? { 43 | set { 44 | taskLock.lock(); defer { taskLock.unlock() } 45 | _task = newValue 46 | } 47 | get { 48 | taskLock.lock(); defer { taskLock.unlock() } 49 | return _task 50 | } 51 | } 52 | 53 | var initialResponseTime: CFAbsoluteTime? 54 | var credential: URLCredential? 55 | var metrics: AnyObject? // URLSessionTaskMetrics 56 | 57 | private var _task: URLSessionTask? { 58 | didSet { reset() } 59 | } 60 | 61 | private let taskLock = NSLock() 62 | 63 | // MARK: Lifecycle 64 | 65 | init(task: URLSessionTask?) { 66 | _task = task 67 | 68 | self.queue = { 69 | let operationQueue = OperationQueue() 70 | 71 | operationQueue.maxConcurrentOperationCount = 1 72 | operationQueue.isSuspended = true 73 | operationQueue.qualityOfService = .utility 74 | 75 | return operationQueue 76 | }() 77 | } 78 | 79 | func reset() { 80 | error = nil 81 | initialResponseTime = nil 82 | } 83 | 84 | // MARK: URLSessionTaskDelegate 85 | 86 | var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? 87 | var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? 88 | var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? 89 | var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)? 90 | 91 | @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:) 92 | func urlSession( 93 | _ session: URLSession, 94 | task: URLSessionTask, 95 | willPerformHTTPRedirection response: HTTPURLResponse, 96 | newRequest request: URLRequest, 97 | completionHandler: @escaping (URLRequest?) -> Void) 98 | { 99 | var redirectRequest: URLRequest? = request 100 | 101 | if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { 102 | redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) 103 | } 104 | 105 | completionHandler(redirectRequest) 106 | } 107 | 108 | @objc(URLSession:task:didReceiveChallenge:completionHandler:) 109 | func urlSession( 110 | _ session: URLSession, 111 | task: URLSessionTask, 112 | didReceive challenge: URLAuthenticationChallenge, 113 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 114 | { 115 | var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling 116 | var credential: URLCredential? 117 | 118 | if let taskDidReceiveChallenge = taskDidReceiveChallenge { 119 | (disposition, credential) = taskDidReceiveChallenge(session, task, challenge) 120 | } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 121 | let host = challenge.protectionSpace.host 122 | 123 | if 124 | let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host), 125 | let serverTrust = challenge.protectionSpace.serverTrust 126 | { 127 | if serverTrustPolicy.evaluate(serverTrust, forHost: host) { 128 | disposition = .useCredential 129 | credential = URLCredential(trust: serverTrust) 130 | } else { 131 | disposition = .cancelAuthenticationChallenge 132 | } 133 | } 134 | } else { 135 | if challenge.previousFailureCount > 0 { 136 | disposition = .rejectProtectionSpace 137 | } else { 138 | credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace) 139 | 140 | if credential != nil { 141 | disposition = .useCredential 142 | } 143 | } 144 | } 145 | 146 | completionHandler(disposition, credential) 147 | } 148 | 149 | @objc(URLSession:task:needNewBodyStream:) 150 | func urlSession( 151 | _ session: URLSession, 152 | task: URLSessionTask, 153 | needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) 154 | { 155 | var bodyStream: InputStream? 156 | 157 | if let taskNeedNewBodyStream = taskNeedNewBodyStream { 158 | bodyStream = taskNeedNewBodyStream(session, task) 159 | } 160 | 161 | completionHandler(bodyStream) 162 | } 163 | 164 | @objc(URLSession:task:didCompleteWithError:) 165 | func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 166 | if let taskDidCompleteWithError = taskDidCompleteWithError { 167 | taskDidCompleteWithError(session, task, error) 168 | } else { 169 | if let error = error { 170 | if self.error == nil { self.error = error } 171 | 172 | if 173 | let downloadDelegate = self as? DownloadTaskDelegate, 174 | let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data 175 | { 176 | downloadDelegate.resumeData = resumeData 177 | } 178 | } 179 | 180 | queue.isSuspended = false 181 | } 182 | } 183 | } 184 | 185 | // MARK: - 186 | 187 | class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate { 188 | 189 | // MARK: Properties 190 | 191 | var dataTask: URLSessionDataTask { return task as! URLSessionDataTask } 192 | 193 | override var data: Data? { 194 | if dataStream != nil { 195 | return nil 196 | } else { 197 | return mutableData 198 | } 199 | } 200 | 201 | var progress: Progress 202 | var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 203 | 204 | var dataStream: ((_ data: Data) -> Void)? 205 | 206 | private var totalBytesReceived: Int64 = 0 207 | private var mutableData: Data 208 | 209 | private var expectedContentLength: Int64? 210 | 211 | // MARK: Lifecycle 212 | 213 | override init(task: URLSessionTask?) { 214 | mutableData = Data() 215 | progress = Progress(totalUnitCount: 0) 216 | 217 | super.init(task: task) 218 | } 219 | 220 | override func reset() { 221 | super.reset() 222 | 223 | progress = Progress(totalUnitCount: 0) 224 | totalBytesReceived = 0 225 | mutableData = Data() 226 | expectedContentLength = nil 227 | } 228 | 229 | // MARK: URLSessionDataDelegate 230 | 231 | var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)? 232 | var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? 233 | var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? 234 | var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? 235 | 236 | func urlSession( 237 | _ session: URLSession, 238 | dataTask: URLSessionDataTask, 239 | didReceive response: URLResponse, 240 | completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) 241 | { 242 | var disposition: URLSession.ResponseDisposition = .allow 243 | 244 | expectedContentLength = response.expectedContentLength 245 | 246 | if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { 247 | disposition = dataTaskDidReceiveResponse(session, dataTask, response) 248 | } 249 | 250 | completionHandler(disposition) 251 | } 252 | 253 | func urlSession( 254 | _ session: URLSession, 255 | dataTask: URLSessionDataTask, 256 | didBecome downloadTask: URLSessionDownloadTask) 257 | { 258 | dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask) 259 | } 260 | 261 | func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 262 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 263 | 264 | if let dataTaskDidReceiveData = dataTaskDidReceiveData { 265 | dataTaskDidReceiveData(session, dataTask, data) 266 | } else { 267 | if let dataStream = dataStream { 268 | dataStream(data) 269 | } else { 270 | mutableData.append(data) 271 | } 272 | 273 | let bytesReceived = Int64(data.count) 274 | totalBytesReceived += bytesReceived 275 | let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown 276 | 277 | progress.totalUnitCount = totalBytesExpected 278 | progress.completedUnitCount = totalBytesReceived 279 | 280 | if let progressHandler = progressHandler { 281 | progressHandler.queue.async { progressHandler.closure(self.progress) } 282 | } 283 | } 284 | } 285 | 286 | func urlSession( 287 | _ session: URLSession, 288 | dataTask: URLSessionDataTask, 289 | willCacheResponse proposedResponse: CachedURLResponse, 290 | completionHandler: @escaping (CachedURLResponse?) -> Void) 291 | { 292 | var cachedResponse: CachedURLResponse? = proposedResponse 293 | 294 | if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { 295 | cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse) 296 | } 297 | 298 | completionHandler(cachedResponse) 299 | } 300 | } 301 | 302 | // MARK: - 303 | 304 | class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate { 305 | 306 | // MARK: Properties 307 | 308 | var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask } 309 | 310 | var progress: Progress 311 | var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 312 | 313 | var resumeData: Data? 314 | override var data: Data? { return resumeData } 315 | 316 | var destination: DownloadRequest.DownloadFileDestination? 317 | 318 | var temporaryURL: URL? 319 | var destinationURL: URL? 320 | 321 | var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL } 322 | 323 | // MARK: Lifecycle 324 | 325 | override init(task: URLSessionTask?) { 326 | progress = Progress(totalUnitCount: 0) 327 | super.init(task: task) 328 | } 329 | 330 | override func reset() { 331 | super.reset() 332 | 333 | progress = Progress(totalUnitCount: 0) 334 | resumeData = nil 335 | } 336 | 337 | // MARK: URLSessionDownloadDelegate 338 | 339 | var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)? 340 | var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? 341 | var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? 342 | 343 | func urlSession( 344 | _ session: URLSession, 345 | downloadTask: URLSessionDownloadTask, 346 | didFinishDownloadingTo location: URL) 347 | { 348 | temporaryURL = location 349 | 350 | guard 351 | let destination = destination, 352 | let response = downloadTask.response as? HTTPURLResponse 353 | else { return } 354 | 355 | let result = destination(location, response) 356 | let destinationURL = result.destinationURL 357 | let options = result.options 358 | 359 | self.destinationURL = destinationURL 360 | 361 | do { 362 | if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) { 363 | try FileManager.default.removeItem(at: destinationURL) 364 | } 365 | 366 | if options.contains(.createIntermediateDirectories) { 367 | let directory = destinationURL.deletingLastPathComponent() 368 | try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) 369 | } 370 | 371 | try FileManager.default.moveItem(at: location, to: destinationURL) 372 | } catch { 373 | self.error = error 374 | } 375 | } 376 | 377 | func urlSession( 378 | _ session: URLSession, 379 | downloadTask: URLSessionDownloadTask, 380 | didWriteData bytesWritten: Int64, 381 | totalBytesWritten: Int64, 382 | totalBytesExpectedToWrite: Int64) 383 | { 384 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 385 | 386 | if let downloadTaskDidWriteData = downloadTaskDidWriteData { 387 | downloadTaskDidWriteData( 388 | session, 389 | downloadTask, 390 | bytesWritten, 391 | totalBytesWritten, 392 | totalBytesExpectedToWrite 393 | ) 394 | } else { 395 | progress.totalUnitCount = totalBytesExpectedToWrite 396 | progress.completedUnitCount = totalBytesWritten 397 | 398 | if let progressHandler = progressHandler { 399 | progressHandler.queue.async { progressHandler.closure(self.progress) } 400 | } 401 | } 402 | } 403 | 404 | func urlSession( 405 | _ session: URLSession, 406 | downloadTask: URLSessionDownloadTask, 407 | didResumeAtOffset fileOffset: Int64, 408 | expectedTotalBytes: Int64) 409 | { 410 | if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { 411 | downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) 412 | } else { 413 | progress.totalUnitCount = expectedTotalBytes 414 | progress.completedUnitCount = fileOffset 415 | } 416 | } 417 | } 418 | 419 | // MARK: - 420 | 421 | class UploadTaskDelegate: DataTaskDelegate { 422 | 423 | // MARK: Properties 424 | 425 | var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask } 426 | 427 | var uploadProgress: Progress 428 | var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 429 | 430 | // MARK: Lifecycle 431 | 432 | override init(task: URLSessionTask?) { 433 | uploadProgress = Progress(totalUnitCount: 0) 434 | super.init(task: task) 435 | } 436 | 437 | override func reset() { 438 | super.reset() 439 | uploadProgress = Progress(totalUnitCount: 0) 440 | } 441 | 442 | // MARK: URLSessionTaskDelegate 443 | 444 | var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? 445 | 446 | func URLSession( 447 | _ session: URLSession, 448 | task: URLSessionTask, 449 | didSendBodyData bytesSent: Int64, 450 | totalBytesSent: Int64, 451 | totalBytesExpectedToSend: Int64) 452 | { 453 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 454 | 455 | if let taskDidSendBodyData = taskDidSendBodyData { 456 | taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) 457 | } else { 458 | uploadProgress.totalUnitCount = totalBytesExpectedToSend 459 | uploadProgress.completedUnitCount = totalBytesSent 460 | 461 | if let uploadProgressHandler = uploadProgressHandler { 462 | uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) } 463 | } 464 | } 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Timeline.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timeline.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | /// Responsible for computing the timing metrics for the complete lifecycle of a `Request`. 28 | public struct Timeline { 29 | /// The time the request was initialized. 30 | public let requestStartTime: CFAbsoluteTime 31 | 32 | /// The time the first bytes were received from or sent to the server. 33 | public let initialResponseTime: CFAbsoluteTime 34 | 35 | /// The time when the request was completed. 36 | public let requestCompletedTime: CFAbsoluteTime 37 | 38 | /// The time when the response serialization was completed. 39 | public let serializationCompletedTime: CFAbsoluteTime 40 | 41 | /// The time interval in seconds from the time the request started to the initial response from the server. 42 | public let latency: TimeInterval 43 | 44 | /// The time interval in seconds from the time the request started to the time the request completed. 45 | public let requestDuration: TimeInterval 46 | 47 | /// The time interval in seconds from the time the request completed to the time response serialization completed. 48 | public let serializationDuration: TimeInterval 49 | 50 | /// The time interval in seconds from the time the request started to the time response serialization completed. 51 | public let totalDuration: TimeInterval 52 | 53 | /// Creates a new `Timeline` instance with the specified request times. 54 | /// 55 | /// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`. 56 | /// - parameter initialResponseTime: The time the first bytes were received from or sent to the server. 57 | /// Defaults to `0.0`. 58 | /// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`. 59 | /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults 60 | /// to `0.0`. 61 | /// 62 | /// - returns: The new `Timeline` instance. 63 | public init( 64 | requestStartTime: CFAbsoluteTime = 0.0, 65 | initialResponseTime: CFAbsoluteTime = 0.0, 66 | requestCompletedTime: CFAbsoluteTime = 0.0, 67 | serializationCompletedTime: CFAbsoluteTime = 0.0) 68 | { 69 | self.requestStartTime = requestStartTime 70 | self.initialResponseTime = initialResponseTime 71 | self.requestCompletedTime = requestCompletedTime 72 | self.serializationCompletedTime = serializationCompletedTime 73 | 74 | self.latency = initialResponseTime - requestStartTime 75 | self.requestDuration = requestCompletedTime - requestStartTime 76 | self.serializationDuration = serializationCompletedTime - requestCompletedTime 77 | self.totalDuration = serializationCompletedTime - requestStartTime 78 | } 79 | } 80 | 81 | // MARK: - CustomStringConvertible 82 | 83 | extension Timeline: CustomStringConvertible { 84 | /// The textual representation used when written to an output stream, which includes the latency, the request 85 | /// duration and the total duration. 86 | public var description: String { 87 | let latency = String(format: "%.3f", self.latency) 88 | let requestDuration = String(format: "%.3f", self.requestDuration) 89 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 90 | let totalDuration = String(format: "%.3f", self.totalDuration) 91 | 92 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is 93 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. 94 | let timings = [ 95 | "\"Latency\": " + latency + " secs", 96 | "\"Request Duration\": " + requestDuration + " secs", 97 | "\"Serialization Duration\": " + serializationDuration + " secs", 98 | "\"Total Duration\": " + totalDuration + " secs" 99 | ] 100 | 101 | return "Timeline: { " + timings.joined(separator: ", ") + " }" 102 | } 103 | } 104 | 105 | // MARK: - CustomDebugStringConvertible 106 | 107 | extension Timeline: CustomDebugStringConvertible { 108 | /// The textual representation used when written to an output stream, which includes the request start time, the 109 | /// initial response time, the request completed time, the serialization completed time, the latency, the request 110 | /// duration and the total duration. 111 | public var debugDescription: String { 112 | let requestStartTime = String(format: "%.3f", self.requestStartTime) 113 | let initialResponseTime = String(format: "%.3f", self.initialResponseTime) 114 | let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime) 115 | let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime) 116 | let latency = String(format: "%.3f", self.latency) 117 | let requestDuration = String(format: "%.3f", self.requestDuration) 118 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 119 | let totalDuration = String(format: "%.3f", self.totalDuration) 120 | 121 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is 122 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. 123 | let timings = [ 124 | "\"Request Start Time\": " + requestStartTime, 125 | "\"Initial Response Time\": " + initialResponseTime, 126 | "\"Request Completed Time\": " + requestCompletedTime, 127 | "\"Serialization Completed Time\": " + serializationCompletedTime, 128 | "\"Latency\": " + latency + " secs", 129 | "\"Request Duration\": " + requestDuration + " secs", 130 | "\"Serialization Duration\": " + serializationDuration + " secs", 131 | "\"Total Duration\": " + totalDuration + " secs" 132 | ] 133 | 134 | return "Timeline: { " + timings.joined(separator: ", ") + " }" 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Validation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Validation.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | 29 | // MARK: Helper Types 30 | 31 | fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason 32 | 33 | /// Used to represent whether validation was successful or encountered an error resulting in a failure. 34 | /// 35 | /// - success: The validation was successful. 36 | /// - failure: The validation failed encountering the provided error. 37 | public enum ValidationResult { 38 | case success 39 | case failure(Error) 40 | } 41 | 42 | fileprivate struct MIMEType { 43 | let type: String 44 | let subtype: String 45 | 46 | var isWildcard: Bool { return type == "*" && subtype == "*" } 47 | 48 | init?(_ string: String) { 49 | let components: [String] = { 50 | let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) 51 | 52 | #if swift(>=3.2) 53 | let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] 54 | #else 55 | let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex) 56 | #endif 57 | 58 | return split.components(separatedBy: "/") 59 | }() 60 | 61 | if let type = components.first, let subtype = components.last { 62 | self.type = type 63 | self.subtype = subtype 64 | } else { 65 | return nil 66 | } 67 | } 68 | 69 | func matches(_ mime: MIMEType) -> Bool { 70 | switch (type, subtype) { 71 | case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): 72 | return true 73 | default: 74 | return false 75 | } 76 | } 77 | } 78 | 79 | // MARK: Properties 80 | 81 | fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) } 82 | 83 | fileprivate var acceptableContentTypes: [String] { 84 | if let accept = request?.value(forHTTPHeaderField: "Accept") { 85 | return accept.components(separatedBy: ",") 86 | } 87 | 88 | return ["*/*"] 89 | } 90 | 91 | // MARK: Status Code 92 | 93 | fileprivate func validate( 94 | statusCode acceptableStatusCodes: S, 95 | response: HTTPURLResponse) 96 | -> ValidationResult 97 | where S.Iterator.Element == Int 98 | { 99 | if acceptableStatusCodes.contains(response.statusCode) { 100 | return .success 101 | } else { 102 | let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode) 103 | return .failure(AFError.responseValidationFailed(reason: reason)) 104 | } 105 | } 106 | 107 | // MARK: Content Type 108 | 109 | fileprivate func validate( 110 | contentType acceptableContentTypes: S, 111 | response: HTTPURLResponse, 112 | data: Data?) 113 | -> ValidationResult 114 | where S.Iterator.Element == String 115 | { 116 | guard let data = data, data.count > 0 else { return .success } 117 | 118 | guard 119 | let responseContentType = response.mimeType, 120 | let responseMIMEType = MIMEType(responseContentType) 121 | else { 122 | for contentType in acceptableContentTypes { 123 | if let mimeType = MIMEType(contentType), mimeType.isWildcard { 124 | return .success 125 | } 126 | } 127 | 128 | let error: AFError = { 129 | let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes)) 130 | return AFError.responseValidationFailed(reason: reason) 131 | }() 132 | 133 | return .failure(error) 134 | } 135 | 136 | for contentType in acceptableContentTypes { 137 | if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { 138 | return .success 139 | } 140 | } 141 | 142 | let error: AFError = { 143 | let reason: ErrorReason = .unacceptableContentType( 144 | acceptableContentTypes: Array(acceptableContentTypes), 145 | responseContentType: responseContentType 146 | ) 147 | 148 | return AFError.responseValidationFailed(reason: reason) 149 | }() 150 | 151 | return .failure(error) 152 | } 153 | } 154 | 155 | // MARK: - 156 | 157 | extension DataRequest { 158 | /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the 159 | /// request was valid. 160 | public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult 161 | 162 | /// Validates the request, using the specified closure. 163 | /// 164 | /// If validation fails, subsequent calls to response handlers will have an associated error. 165 | /// 166 | /// - parameter validation: A closure to validate the request. 167 | /// 168 | /// - returns: The request. 169 | @discardableResult 170 | public func validate(_ validation: @escaping Validation) -> Self { 171 | let validationExecution: () -> Void = { [unowned self] in 172 | if 173 | let response = self.response, 174 | self.delegate.error == nil, 175 | case let .failure(error) = validation(self.request, response, self.delegate.data) 176 | { 177 | self.delegate.error = error 178 | } 179 | } 180 | 181 | validations.append(validationExecution) 182 | 183 | return self 184 | } 185 | 186 | /// Validates that the response has a status code in the specified sequence. 187 | /// 188 | /// If validation fails, subsequent calls to response handlers will have an associated error. 189 | /// 190 | /// - parameter range: The range of acceptable status codes. 191 | /// 192 | /// - returns: The request. 193 | @discardableResult 194 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 195 | return validate { [unowned self] _, response, _ in 196 | return self.validate(statusCode: acceptableStatusCodes, response: response) 197 | } 198 | } 199 | 200 | /// Validates that the response has a content type in the specified sequence. 201 | /// 202 | /// If validation fails, subsequent calls to response handlers will have an associated error. 203 | /// 204 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 205 | /// 206 | /// - returns: The request. 207 | @discardableResult 208 | public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { 209 | return validate { [unowned self] _, response, data in 210 | return self.validate(contentType: acceptableContentTypes, response: response, data: data) 211 | } 212 | } 213 | 214 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 215 | /// type matches any specified in the Accept HTTP header field. 216 | /// 217 | /// If validation fails, subsequent calls to response handlers will have an associated error. 218 | /// 219 | /// - returns: The request. 220 | @discardableResult 221 | public func validate() -> Self { 222 | return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) 223 | } 224 | } 225 | 226 | // MARK: - 227 | 228 | extension DownloadRequest { 229 | /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a 230 | /// destination URL, and returns whether the request was valid. 231 | public typealias Validation = ( 232 | _ request: URLRequest?, 233 | _ response: HTTPURLResponse, 234 | _ temporaryURL: URL?, 235 | _ destinationURL: URL?) 236 | -> ValidationResult 237 | 238 | /// Validates the request, using the specified closure. 239 | /// 240 | /// If validation fails, subsequent calls to response handlers will have an associated error. 241 | /// 242 | /// - parameter validation: A closure to validate the request. 243 | /// 244 | /// - returns: The request. 245 | @discardableResult 246 | public func validate(_ validation: @escaping Validation) -> Self { 247 | let validationExecution: () -> Void = { [unowned self] in 248 | let request = self.request 249 | let temporaryURL = self.downloadDelegate.temporaryURL 250 | let destinationURL = self.downloadDelegate.destinationURL 251 | 252 | if 253 | let response = self.response, 254 | self.delegate.error == nil, 255 | case let .failure(error) = validation(request, response, temporaryURL, destinationURL) 256 | { 257 | self.delegate.error = error 258 | } 259 | } 260 | 261 | validations.append(validationExecution) 262 | 263 | return self 264 | } 265 | 266 | /// Validates that the response has a status code in the specified sequence. 267 | /// 268 | /// If validation fails, subsequent calls to response handlers will have an associated error. 269 | /// 270 | /// - parameter range: The range of acceptable status codes. 271 | /// 272 | /// - returns: The request. 273 | @discardableResult 274 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 275 | return validate { [unowned self] _, response, _, _ in 276 | return self.validate(statusCode: acceptableStatusCodes, response: response) 277 | } 278 | } 279 | 280 | /// Validates that the response has a content type in the specified sequence. 281 | /// 282 | /// If validation fails, subsequent calls to response handlers will have an associated error. 283 | /// 284 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 285 | /// 286 | /// - returns: The request. 287 | @discardableResult 288 | public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { 289 | return validate { [unowned self] _, response, _, _ in 290 | let fileURL = self.downloadDelegate.fileURL 291 | 292 | guard let validFileURL = fileURL else { 293 | return .failure(AFError.responseValidationFailed(reason: .dataFileNil)) 294 | } 295 | 296 | do { 297 | let data = try Data(contentsOf: validFileURL) 298 | return self.validate(contentType: acceptableContentTypes, response: response, data: data) 299 | } catch { 300 | return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL))) 301 | } 302 | } 303 | } 304 | 305 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 306 | /// type matches any specified in the Accept HTTP header field. 307 | /// 308 | /// If validation fails, subsequent calls to response handlers will have an associated error. 309 | /// 310 | /// - returns: The request. 311 | @discardableResult 312 | public func validate() -> Self { 313 | return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.5.1) 3 | - ReachabilitySwift (4.1.0) 4 | 5 | DEPENDENCIES: 6 | - Alamofire 7 | - ReachabilitySwift 8 | 9 | SPEC CHECKSUMS: 10 | Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a 11 | ReachabilitySwift: 6849231cd4e06559f3b9ef4a97a0a0f96d41e09f 12 | 13 | PODFILE CHECKSUM: a18f860cc555a0134c4502f6664d8b54e4f0e089 14 | 15 | COCOAPODS: 1.3.1 16 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/neo.xcuserdatad/xcschemes/Alamofire.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/neo.xcuserdatad/xcschemes/Pods-reachability-playground.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 68 | 69 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/neo.xcuserdatad/xcschemes/ReachabilitySwift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 68 | 69 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/neo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Alamofire.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | Pods-reachability-playground.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 1 20 | 21 | ReachabilitySwift.xcscheme 22 | 23 | isShown 24 | 25 | orderHint 26 | 2 27 | 28 | 29 | SuppressBuildableAutocreation 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Pods/ReachabilitySwift/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Ashley Mills 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 | -------------------------------------------------------------------------------- /Pods/ReachabilitySwift/README.md: -------------------------------------------------------------------------------- 1 | # Reachability.swift 2 | 3 | Reachability.swift is a replacement for Apple's Reachability sample, re-written in Swift with closures. 4 | 5 | It is compatible with **iOS** (8.0 - 11.0), **OSX** (10.9 - 10.13) and **tvOS** (9.0 - 11.0) 6 | 7 | Inspired by https://github.com/tonymillion/Reachability 8 | 9 | ## Supporting **Reachability.swift** 10 | Keeping **Reachability.swift** up-to-date is a time consuming task. Making updates, reviewing pull requests, responding to issues and answering emails all take time. If you'd like to help keep me motivated, please download my free app, [Photo Flipper] from the App Store. (To really motivate me, pay $1.99 for the IAP 😀) 11 | 12 | And don't forget to **★** the repo. This increases its visibility and encourages others to contribute. 13 | 14 | Thanks 15 | Ash 16 | 17 | # IMPORTANT 18 | 19 | ## Version 4.0 breaking changes 20 | 21 | ### CocoaPods: 22 | 23 | If you're adding **Reachability.swift** using CocoaPods, note that the framework name has changed from `ReachabilitySwift` to `Reachability` (for consistency with Carthage) 24 | 25 | ### Previously: 26 | 27 | ```swift 28 | enum NetworkStatus { 29 | case notReachable, reachableViaWiFi, reachableViaWWAN 30 | } 31 | var currentReachabilityStatus: NetworkStatus 32 | ``` 33 | 34 | ### Now: 35 | 36 | ```swift 37 | enum Connection { 38 | case none, wifi, cellular 39 | } 40 | var connection: Connection 41 | ``` 42 | 43 | ### Other changes: 44 | 45 | - `isReachableViaWWAN` has been renamed to `isReachableViaCellular` 46 | 47 | - `reachableOnWWAN` has been renamed to `allowsCellularConnection` 48 | 49 | - `reachability.currentReachabilityString` has been deprecated. Use `"\(reachability.connection)"` instead. 50 | 51 | - `isReachable` has been deprecated. Use `connection != .none` instead. 52 | 53 | - `isReachableViaWWAN` has been deprecated. Use `connection == .cellular` instead. 54 | 55 | - The notification for reachability changes has been renamed from `ReachabilityChangedNotification` to `Notification.Name.reachabilityChanged` 56 | 57 | - All closure callbacks and notification are fired on the main queue (including when `startNotifier()` is called) 58 | 59 | 60 | ## Got a problem? 61 | 62 | Please read https://github.com/ashleymills/Reachability.swift/wiki/Raising-an-issue before raising an issue. 63 | 64 | ## Installation 65 | ### Manual 66 | Just drop the **Reachability.swift** file into your project. That's it! 67 | 68 | ### CocoaPods 69 | [CocoaPods][] is a dependency manager for Cocoa projects. To install Reachability.swift with CocoaPods: 70 | 71 | 1. Make sure CocoaPods is [installed][CocoaPods Installation]. 72 | 73 | 2. Update your Podfile to include the following: 74 | 75 | ``` ruby 76 | use_frameworks! 77 | pod 'ReachabilitySwift' 78 | ``` 79 | 80 | 3. Run `pod install`. 81 | 82 | [CocoaPods]: https://cocoapods.org 83 | [CocoaPods Installation]: https://guides.cocoapods.org/using/getting-started.html#getting-started 84 | 85 | 4. In your code import Reachability like so: 86 | `import Reachability` 87 | 88 | ### Carthage 89 | [Carthage][] is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 90 | To install Reachability.swift with Carthage: 91 | 92 | 1. Install Carthage via [Homebrew][] 93 | ```bash 94 | $ brew update 95 | $ brew install carthage 96 | ``` 97 | 98 | 2. Add `github "ashleymills/Reachability.swift"` to your Cartfile. 99 | 100 | 3. Run `carthage update`. 101 | 102 | 4. Drag `ReachabilitySwift.framework` from the `Carthage/Build/iOS/` directory to the `Linked Frameworks and Libraries` section of your Xcode project’s `General` settings. 103 | 104 | 5. Add `$(SRCROOT)/Carthage/Build/iOS/ReachabilitySwiift.framework` to `Input Files` of Run Script Phase for Carthage. 105 | 106 | 6. In your code import Reachability likse so: 107 | `import Reachability` 108 | 109 | 110 | [Carthage]: https://github.com/Carthage/Carthage 111 | [Homebrew]: http://brew.sh 112 | [Photo Flipper]: https://itunes.apple.com/app/apple-store/id749627884?pt=215893&ct=GitHubReachability&mt=8 113 | 114 | ## Example - closures 115 | 116 | NOTE: All closures are run on the **main queue**. 117 | 118 | ```swift 119 | //declare this property where it won't go out of scope relative to your listener 120 | let reachability = Reachability()! 121 | 122 | reachability.whenReachable = { reachability in 123 | if reachability.connection == .wifi { 124 | print("Reachable via WiFi") 125 | } else { 126 | print("Reachable via Cellular") 127 | } 128 | } 129 | reachability.whenUnreachable = { _ in 130 | print("Not reachable") 131 | } 132 | 133 | do { 134 | try reachability.startNotifier() 135 | } catch { 136 | print("Unable to start notifier") 137 | } 138 | ``` 139 | 140 | and for stopping notifications 141 | 142 | ```swift 143 | reachability.stopNotifier() 144 | ``` 145 | 146 | ## Example - notifications 147 | 148 | NOTE: All notifications are delviered on the **main queue**. 149 | 150 | ```swift 151 | //declare this property where it won't go out of scope relative to your listener 152 | let reachability = Reachability()! 153 | 154 | //declare this inside of viewWillAppear 155 | 156 | NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(_:)), name: .reachabilityChanged, object: reachability) 157 | do{ 158 | try reachability.startNotifier() 159 | }catch{ 160 | print("could not start reachability notifier") 161 | } 162 | ``` 163 | 164 | and 165 | 166 | ```swift 167 | func reachabilityChanged(note: Notification) { 168 | 169 | let reachability = note.object as! Reachability 170 | 171 | switch reachability.connection { 172 | case .wifi: 173 | print("Reachable via WiFi") 174 | case .cellular: 175 | print("Reachable via Cellular") 176 | case .none: 177 | print("Network not reachable") 178 | } 179 | } 180 | ``` 181 | 182 | and for stopping notifications 183 | 184 | ```swift 185 | reachability.stopNotifier() 186 | NotificationCenter.default.removeObserver(self, name: .eachabilityChanged, object: reachability) 187 | ``` 188 | 189 | ## Want to help? 190 | 191 | Got a bug fix, or a new feature? Create a pull request and go for it! 192 | 193 | ## Let me know! 194 | 195 | If you use **Reachability.swift**, please let me know about your app and I'll put a link [here…](https://github.com/ashleymills/Reachability.swift/wiki/Apps-using-Reachability.swift) and tell your friends! 196 | 197 | Cheers, 198 | Ash 199 | -------------------------------------------------------------------------------- /Pods/ReachabilitySwift/Reachability/Reachability.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Ashley Mills 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | import SystemConfiguration 29 | import Foundation 30 | 31 | public enum ReachabilityError: Error { 32 | case FailedToCreateWithAddress(sockaddr_in) 33 | case FailedToCreateWithHostname(String) 34 | case UnableToSetCallback 35 | case UnableToSetDispatchQueue 36 | } 37 | 38 | @available(*, unavailable, renamed: "Notification.Name.reachabilityChanged") 39 | public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification") 40 | 41 | extension Notification.Name { 42 | public static let reachabilityChanged = Notification.Name("reachabilityChanged") 43 | } 44 | 45 | func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) { 46 | 47 | guard let info = info else { return } 48 | 49 | let reachability = Unmanaged.fromOpaque(info).takeUnretainedValue() 50 | reachability.reachabilityChanged() 51 | } 52 | 53 | public class Reachability { 54 | 55 | public typealias NetworkReachable = (Reachability) -> () 56 | public typealias NetworkUnreachable = (Reachability) -> () 57 | 58 | @available(*, unavailable, renamed: "Conection") 59 | public enum NetworkStatus: CustomStringConvertible { 60 | case notReachable, reachableViaWiFi, reachableViaWWAN 61 | public var description: String { 62 | switch self { 63 | case .reachableViaWWAN: return "Cellular" 64 | case .reachableViaWiFi: return "WiFi" 65 | case .notReachable: return "No Connection" 66 | } 67 | } 68 | } 69 | 70 | public enum Connection: CustomStringConvertible { 71 | case none, wifi, cellular 72 | public var description: String { 73 | switch self { 74 | case .cellular: return "Cellular" 75 | case .wifi: return "WiFi" 76 | case .none: return "No Connection" 77 | } 78 | } 79 | } 80 | 81 | public var whenReachable: NetworkReachable? 82 | public var whenUnreachable: NetworkUnreachable? 83 | 84 | @available(*, deprecated: 4.0, renamed: "allowsCellularConnection") 85 | public let reachableOnWWAN: Bool = true 86 | 87 | /// Set to `false` to force Reachability.connection to .none when on cellular connection (default value `true`) 88 | public var allowsCellularConnection: Bool 89 | 90 | // The notification center on which "reachability changed" events are being posted 91 | public var notificationCenter: NotificationCenter = NotificationCenter.default 92 | 93 | @available(*, deprecated: 4.0, renamed: "connection.description") 94 | public var currentReachabilityString: String { 95 | return "\(connection)" 96 | } 97 | 98 | @available(*, unavailable, renamed: "connection") 99 | public var currentReachabilityStatus: Connection { 100 | return connection 101 | } 102 | 103 | public var connection: Connection { 104 | 105 | guard isReachableFlagSet else { return .none } 106 | 107 | // If we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi 108 | guard isRunningOnDevice else { return .wifi } 109 | 110 | var connection = Connection.none 111 | 112 | if !isConnectionRequiredFlagSet { 113 | connection = .wifi 114 | } 115 | 116 | if isConnectionOnTrafficOrDemandFlagSet { 117 | if !isInterventionRequiredFlagSet { 118 | connection = .wifi 119 | } 120 | } 121 | 122 | if isOnWWANFlagSet { 123 | if !allowsCellularConnection { 124 | connection = .none 125 | } else { 126 | connection = .cellular 127 | } 128 | } 129 | 130 | return connection 131 | } 132 | 133 | fileprivate var previousFlags: SCNetworkReachabilityFlags? 134 | 135 | fileprivate var isRunningOnDevice: Bool = { 136 | #if (arch(i386) || arch(x86_64)) && os(iOS) 137 | return false 138 | #else 139 | return true 140 | #endif 141 | }() 142 | 143 | fileprivate var notifierRunning = false 144 | fileprivate let reachabilityRef: SCNetworkReachability 145 | 146 | fileprivate let reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability") 147 | 148 | required public init(reachabilityRef: SCNetworkReachability) { 149 | allowsCellularConnection = true 150 | self.reachabilityRef = reachabilityRef 151 | } 152 | 153 | public convenience init?(hostname: String) { 154 | 155 | guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { return nil } 156 | 157 | self.init(reachabilityRef: ref) 158 | } 159 | 160 | public convenience init?() { 161 | 162 | var zeroAddress = sockaddr() 163 | zeroAddress.sa_len = UInt8(MemoryLayout.size) 164 | zeroAddress.sa_family = sa_family_t(AF_INET) 165 | 166 | guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else { return nil } 167 | 168 | self.init(reachabilityRef: ref) 169 | } 170 | 171 | deinit { 172 | stopNotifier() 173 | } 174 | } 175 | 176 | public extension Reachability { 177 | 178 | // MARK: - *** Notifier methods *** 179 | func startNotifier() throws { 180 | 181 | guard !notifierRunning else { return } 182 | 183 | var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) 184 | context.info = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) 185 | if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) { 186 | stopNotifier() 187 | throw ReachabilityError.UnableToSetCallback 188 | } 189 | 190 | if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) { 191 | stopNotifier() 192 | throw ReachabilityError.UnableToSetDispatchQueue 193 | } 194 | 195 | // Perform an initial check 196 | reachabilitySerialQueue.async { 197 | self.reachabilityChanged() 198 | } 199 | 200 | notifierRunning = true 201 | } 202 | 203 | func stopNotifier() { 204 | defer { notifierRunning = false } 205 | 206 | SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) 207 | SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil) 208 | } 209 | 210 | // MARK: - *** Connection test methods *** 211 | @available(*, deprecated: 4.0, message: "Please use `connection != .none`") 212 | var isReachable: Bool { 213 | 214 | guard isReachableFlagSet else { return false } 215 | 216 | if isConnectionRequiredAndTransientFlagSet { 217 | return false 218 | } 219 | 220 | if isRunningOnDevice { 221 | if isOnWWANFlagSet && !reachableOnWWAN { 222 | // We don't want to connect when on cellular connection 223 | return false 224 | } 225 | } 226 | 227 | return true 228 | } 229 | 230 | @available(*, deprecated: 4.0, message: "Please use `connection == .cellular`") 231 | var isReachableViaWWAN: Bool { 232 | // Check we're not on the simulator, we're REACHABLE and check we're on WWAN 233 | return isRunningOnDevice && isReachableFlagSet && isOnWWANFlagSet 234 | } 235 | 236 | @available(*, deprecated: 4.0, message: "Please use `connection == .wifi`") 237 | var isReachableViaWiFi: Bool { 238 | 239 | // Check we're reachable 240 | guard isReachableFlagSet else { return false } 241 | 242 | // If reachable we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi 243 | guard isRunningOnDevice else { return true } 244 | 245 | // Check we're NOT on WWAN 246 | return !isOnWWANFlagSet 247 | } 248 | 249 | var description: String { 250 | 251 | let W = isRunningOnDevice ? (isOnWWANFlagSet ? "W" : "-") : "X" 252 | let R = isReachableFlagSet ? "R" : "-" 253 | let c = isConnectionRequiredFlagSet ? "c" : "-" 254 | let t = isTransientConnectionFlagSet ? "t" : "-" 255 | let i = isInterventionRequiredFlagSet ? "i" : "-" 256 | let C = isConnectionOnTrafficFlagSet ? "C" : "-" 257 | let D = isConnectionOnDemandFlagSet ? "D" : "-" 258 | let l = isLocalAddressFlagSet ? "l" : "-" 259 | let d = isDirectFlagSet ? "d" : "-" 260 | 261 | return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" 262 | } 263 | } 264 | 265 | fileprivate extension Reachability { 266 | 267 | func reachabilityChanged() { 268 | guard previousFlags != flags else { return } 269 | 270 | let block = connection != .none ? whenReachable : whenUnreachable 271 | 272 | DispatchQueue.main.async { 273 | block?(self) 274 | self.notificationCenter.post(name: .reachabilityChanged, object:self) 275 | } 276 | 277 | previousFlags = flags 278 | } 279 | 280 | var isOnWWANFlagSet: Bool { 281 | #if os(iOS) 282 | return flags.contains(.isWWAN) 283 | #else 284 | return false 285 | #endif 286 | } 287 | var isReachableFlagSet: Bool { 288 | return flags.contains(.reachable) 289 | } 290 | var isConnectionRequiredFlagSet: Bool { 291 | return flags.contains(.connectionRequired) 292 | } 293 | var isInterventionRequiredFlagSet: Bool { 294 | return flags.contains(.interventionRequired) 295 | } 296 | var isConnectionOnTrafficFlagSet: Bool { 297 | return flags.contains(.connectionOnTraffic) 298 | } 299 | var isConnectionOnDemandFlagSet: Bool { 300 | return flags.contains(.connectionOnDemand) 301 | } 302 | var isConnectionOnTrafficOrDemandFlagSet: Bool { 303 | return !flags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty 304 | } 305 | var isTransientConnectionFlagSet: Bool { 306 | return flags.contains(.transientConnection) 307 | } 308 | var isLocalAddressFlagSet: Bool { 309 | return flags.contains(.isLocalAddress) 310 | } 311 | var isDirectFlagSet: Bool { 312 | return flags.contains(.isDirect) 313 | } 314 | var isConnectionRequiredAndTransientFlagSet: Bool { 315 | return flags.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection] 316 | } 317 | 318 | var flags: SCNetworkReachabilityFlags { 319 | var flags = SCNetworkReachabilityFlags() 320 | if SCNetworkReachabilityGetFlags(reachabilityRef, &flags) { 321 | return flags 322 | } else { 323 | return SCNetworkReachabilityFlags() 324 | } 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 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 | -------------------------------------------------------------------------------- /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.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/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 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014-2017 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 | ## ReachabilitySwift 28 | 29 | Copyright (c) 2016 Ashley Mills 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the "Software"), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in 39 | all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | THE SOFTWARE. 48 | 49 | Generated by CocoaPods - https://cocoapods.org 50 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground-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-2017 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 | Copyright (c) 2016 Ashley Mills 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy 49 | of this software and associated documentation files (the "Software"), to deal 50 | in the Software without restriction, including without limitation the rights 51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 52 | copies of the Software, and to permit persons to whom the Software is 53 | furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included in 56 | all copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 64 | THE SOFTWARE. 65 | 66 | License 67 | MIT 68 | Title 69 | ReachabilitySwift 70 | Type 71 | PSGroupSpecifier 72 | 73 | 74 | FooterText 75 | Generated by CocoaPods - https://cocoapods.org 76 | Title 77 | 78 | Type 79 | PSGroupSpecifier 80 | 81 | 82 | StringsTable 83 | Acknowledgements 84 | Title 85 | Acknowledgements 86 | 87 | 88 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_reachability_playground : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_reachability_playground 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground-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 | 104 | if [[ "$CONFIGURATION" == "Debug" ]]; then 105 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 106 | install_framework "${BUILT_PRODUCTS_DIR}/ReachabilitySwift/Reachability.framework" 107 | fi 108 | if [[ "$CONFIGURATION" == "Release" ]]; then 109 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 110 | install_framework "${BUILT_PRODUCTS_DIR}/ReachabilitySwift/Reachability.framework" 111 | fi 112 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 113 | wait 114 | fi 115 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground-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 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground-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_reachability_playgroundVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_reachability_playgroundVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/ReachabilitySwift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ReachabilitySwift/Reachability.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "Reachability" 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 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_reachability_playground { 2 | umbrella header "Pods-reachability-playground-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-reachability-playground/Pods-reachability-playground.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/ReachabilitySwift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ReachabilitySwift/Reachability.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "Reachability" 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 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ReachabilitySwift/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.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_ReachabilitySwift : NSObject 3 | @end 4 | @implementation PodsDummy_ReachabilitySwift 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-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 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-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 ReachabilityVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char ReachabilityVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift.modulemap: -------------------------------------------------------------------------------- 1 | framework module Reachability { 2 | umbrella header "ReachabilitySwift-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/ReachabilitySwift 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "SystemConfiguration" 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}/ReachabilitySwift 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using Pusher Chatkit with SlackTextViewController 2 | 3 | [![Get help on Codementor](https://cdn.codementor.io/badges/get_help_github.svg)](https://www.codementor.io/neoighodaro?utm_source=github&utm_medium=button&utm_term=neoighodaro&utm_campaign=github) 4 | 5 | ![](https://www.dropbox.com/s/mbvdm2yxy2a4etb/Handling-internet-connection-reachability-in-Swift-4.gif?raw=1) 6 | 7 | ----- 8 | 9 | ### Requirements 10 | For this to work, you need to have 11 | 12 | - Xcode installed on your machine 13 | - Basic knowledge of the Swift programming language 14 | - Cocoapods installed on your machine. 15 | 16 | You can follow the tutorial [here](tutorial.md) 17 | -------------------------------------------------------------------------------- /reachability-playground.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /reachability-playground.xcodeproj/xcuserdata/neo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | reachability-playground.xcscheme 8 | 9 | orderHint 10 | 3 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /reachability-playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /reachability-playground/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // reachability-playground 4 | // 5 | // Created by Neo Ighodaro on 25/10/2017. 6 | // Copyright © 2017 CreativityKills Co. 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: [UIApplicationLaunchOptionsKey: 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 | -------------------------------------------------------------------------------- /reachability-playground/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /reachability-playground/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /reachability-playground/Assets.xcassets/offline.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "offline.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /reachability-playground/Assets.xcassets/offline.imageset/offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neoighodaro/Handling-internet-connection-reachability-in-Swift/a6f20d0c2f0099bcb16a1c22c2d8da41b4d75748/reachability-playground/Assets.xcassets/offline.imageset/offline.png -------------------------------------------------------------------------------- /reachability-playground/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /reachability-playground/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 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 | 62 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 124 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /reachability-playground/Connection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Connection.swift 3 | // reachability-playground 4 | // 5 | // Created by Neo Ighodaro on 27/10/2017. 6 | // Copyright © 2017 CreativityKills Co. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Reachability 11 | 12 | class NetworkManager: NSObject { 13 | 14 | var reachability: Reachability! 15 | 16 | static let sharedInstance: NetworkManager = { return NetworkManager() }() 17 | 18 | 19 | override init() { 20 | super.init() 21 | 22 | reachability = Reachability()! 23 | 24 | NotificationCenter.default.addObserver( 25 | self, 26 | selector: #selector(networkStatusChanged(_:)), 27 | name: .reachabilityChanged, 28 | object: reachability 29 | ) 30 | 31 | do { 32 | try reachability.startNotifier() 33 | } catch { 34 | print("Unable to start notifier") 35 | } 36 | } 37 | 38 | @objc func networkStatusChanged(_ notification: Notification) { 39 | // Do something globally here! 40 | } 41 | 42 | static func stopNotifier() -> Void { 43 | do { 44 | try (NetworkManager.sharedInstance.reachability).startNotifier() 45 | } catch { 46 | print("Error stopping notifier") 47 | } 48 | } 49 | 50 | static func isReachable(completed: @escaping (NetworkManager) -> Void) { 51 | if (NetworkManager.sharedInstance.reachability).connection != .none { 52 | completed(NetworkManager.sharedInstance) 53 | } 54 | } 55 | 56 | static func isUnreachable(completed: @escaping (NetworkManager) -> Void) { 57 | if (NetworkManager.sharedInstance.reachability).connection == .none { 58 | completed(NetworkManager.sharedInstance) 59 | } 60 | } 61 | 62 | static func isReachableViaWWAN(completed: @escaping (NetworkManager) -> Void) { 63 | if (NetworkManager.sharedInstance.reachability).connection == .cellular { 64 | completed(NetworkManager.sharedInstance) 65 | } 66 | } 67 | 68 | static func isReachableViaWiFi(completed: @escaping (NetworkManager) -> Void) { 69 | if (NetworkManager.sharedInstance.reachability).connection == .wifi { 70 | completed(NetworkManager.sharedInstance) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /reachability-playground/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /reachability-playground/LaunchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // reachability-playground 4 | // 5 | // Created by Neo Ighodaro on 25/10/2017. 6 | // Copyright © 2017 CreativityKills Co. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LaunchViewController: UIViewController { 12 | 13 | let network: NetworkManager = NetworkManager.sharedInstance 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | NetworkManager.isUnreachable { _ in 19 | self.showOfflinePage() 20 | } 21 | 22 | NetworkManager.isReachable { _ in 23 | self.showMainPage() 24 | } 25 | } 26 | 27 | private func showOfflinePage() -> Void { 28 | DispatchQueue.main.async { 29 | self.performSegue(withIdentifier: "NetworkUnavailable", sender: self) 30 | } 31 | } 32 | 33 | private func showMainPage() -> Void { 34 | DispatchQueue.main.async { 35 | self.performSegue(withIdentifier: "MainController", sender: self) 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /reachability-playground/OfflineViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OfflineViewController.swift 3 | // reachability-playground 4 | // 5 | // Created by Neo Ighodaro on 28/10/2017. 6 | // Copyright © 2017 CreativityKills Co. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class OfflineViewController: UIViewController { 12 | 13 | let network = NetworkManager.sharedInstance 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | network.reachability.whenReachable = { reachability in 19 | self.showMainController() 20 | } 21 | } 22 | 23 | override func viewWillAppear(_ animated: Bool) { 24 | super.viewWillAppear(animated) 25 | navigationController?.setNavigationBarHidden(true, animated: animated) 26 | } 27 | 28 | override func viewWillDisappear(_ animated: Bool) { 29 | super.viewWillDisappear(animated) 30 | navigationController?.setNavigationBarHidden(false, animated: animated) 31 | } 32 | 33 | private func showMainController() -> Void { 34 | DispatchQueue.main.async { 35 | self.performSegue(withIdentifier: "MainController", sender: self) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /reachability-playground/PostsTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListTableViewController.swift 3 | // reachability-playground 4 | // 5 | // Created by Neo Ighodaro on 28/10/2017. 6 | // Copyright © 2017 CreativityKills Co. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Alamofire 11 | 12 | struct RedditPost { 13 | let title: String! 14 | let subreddit: String! 15 | } 16 | 17 | class PostsTableViewController: UITableViewController { 18 | 19 | var posts = [RedditPost]() 20 | let network = NetworkManager.sharedInstance 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | navigationItem.title = "Latest Posts" 26 | 27 | fetchPosts { posts in 28 | self.posts = posts 29 | self.tableView.reloadData() 30 | } 31 | 32 | network.reachability.whenUnreachable = { reachability in 33 | self.showOfflinePage() 34 | } 35 | } 36 | 37 | private func showOfflinePage() -> Void { 38 | DispatchQueue.main.async { 39 | self.performSegue(withIdentifier: "NetworkUnavailable", sender: self) 40 | } 41 | } 42 | 43 | private func fetchPosts(completion: @escaping (_ posts: [RedditPost]) -> Void) -> Void { 44 | Alamofire.request("https://api.reddit.com").validate().responseJSON { response in 45 | switch response.result { 46 | case .success(let JSON): 47 | let data = JSON as! [String:AnyObject] 48 | 49 | guard let children = data["data"]!["children"] as? [AnyObject] else { return } 50 | 51 | var posts = [RedditPost]() 52 | 53 | for child in 0...children.count-1 { 54 | let post = children[child]["data"] as! [String: AnyObject] 55 | 56 | posts.append(RedditPost( 57 | title: post["title"] as! String, 58 | subreddit: "/r/" + (post["subreddit"] as! String) 59 | )) 60 | } 61 | 62 | DispatchQueue.main.async { 63 | completion(posts) 64 | } 65 | 66 | case .failure(let error): 67 | print(error) 68 | } 69 | } 70 | } 71 | 72 | override func didReceiveMemoryWarning() { 73 | super.didReceiveMemoryWarning() 74 | } 75 | 76 | // MARK: - Table view data source 77 | 78 | override func numberOfSections(in tableView: UITableView) -> Int { 79 | return 1 80 | } 81 | 82 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 83 | return self.posts.count 84 | } 85 | 86 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 87 | let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) 88 | 89 | let post = posts[indexPath.row] as RedditPost 90 | 91 | cell.textLabel?.text = post.title 92 | cell.detailTextLabel?.text = post.subreddit 93 | 94 | return cell 95 | } 96 | } 97 | --------------------------------------------------------------------------------