├── 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 ├── Local Podspecs │ └── Alamofire.podspec.json ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── forrestknight.xcuserdatad │ │ └── xcschemes │ │ ├── Alamofire.xcscheme │ │ ├── Pods-RainyShinyCloudy.xcscheme │ │ └── xcschememanagement.plist └── Target Support Files │ ├── Alamofire │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.modulemap │ ├── Alamofire.xcconfig │ └── Info.plist │ └── Pods-RainyShinyCloudy │ ├── Info.plist │ ├── Pods-RainyShinyCloudy-acknowledgements.markdown │ ├── Pods-RainyShinyCloudy-acknowledgements.plist │ ├── Pods-RainyShinyCloudy-dummy.m │ ├── Pods-RainyShinyCloudy-frameworks.sh │ ├── Pods-RainyShinyCloudy-resources.sh │ ├── Pods-RainyShinyCloudy-umbrella.h │ ├── Pods-RainyShinyCloudy.debug.xcconfig │ ├── Pods-RainyShinyCloudy.modulemap │ └── Pods-RainyShinyCloudy.release.xcconfig ├── README.md ├── RainyShinyCloudy.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── forrestknight.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── forrestknight.xcuserdatad │ └── xcschemes │ ├── RainyShinyCloudy.xcscheme │ └── xcschememanagement.plist ├── RainyShinyCloudy.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── forrestknight.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── RainyShinyCloudyTests ├── Info.plist └── RainyShinyCloudyTests.swift └── RainyShinyCloudyUITests ├── Info.plist └── RainyShinyCloudyUITests.swift /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | use_frameworks! 5 | 6 | target 'RainyShinyCloudy' do 7 | pod 'Alamofire', 8 | :git => 'https://github.com/Alamofire/Alamofire.git' 9 | end -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.3.0) 3 | 4 | DEPENDENCIES: 5 | - Alamofire (from `https://github.com/Alamofire/Alamofire.git`) 6 | 7 | EXTERNAL SOURCES: 8 | Alamofire: 9 | :git: https://github.com/Alamofire/Alamofire.git 10 | 11 | CHECKOUT OPTIONS: 12 | Alamofire: 13 | :commit: fa3c6d09b603f417c7788adb51ca0cade75b3d67 14 | :git: https://github.com/Alamofire/Alamofire.git 15 | 16 | SPEC CHECKSUMS: 17 | Alamofire: 1f72088aff8f6b40828dadd61be2e9a31beca01e 18 | 19 | PODFILE CHECKSUM: 4f1874c3a06d7987724145d8a19093240900c2f8 20 | 21 | COCOAPODS: 1.2.0 22 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016 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/AFError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AFError.swift 3 | // 4 | // Copyright (c) 2014-2016 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 | /// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with 28 | /// their own associated reasons. 29 | /// 30 | /// - invalidURL: Returned when a `URLConvertible` type fails to create a valid `URL`. 31 | /// - parameterEncodingFailed: Returned when a parameter encoding object throws an error during the encoding process. 32 | /// - multipartEncodingFailed: Returned when some step in the multipart encoding process fails. 33 | /// - responseValidationFailed: Returned when a `validate()` call fails. 34 | /// - responseSerializationFailed: Returned when a response serializer encounters an error in the serialization process. 35 | public enum AFError: Error { 36 | /// The underlying reason the parameter encoding error occurred. 37 | /// 38 | /// - missingURL: The URL request did not have a URL to encode. 39 | /// - jsonEncodingFailed: JSON serialization failed with an underlying system error during the 40 | /// encoding process. 41 | /// - propertyListEncodingFailed: Property list serialization failed with an underlying system error during 42 | /// encoding process. 43 | public enum ParameterEncodingFailureReason { 44 | case missingURL 45 | case jsonEncodingFailed(error: Error) 46 | case propertyListEncodingFailed(error: Error) 47 | } 48 | 49 | /// The underlying reason the multipart encoding error occurred. 50 | /// 51 | /// - bodyPartURLInvalid: The `fileURL` provided for reading an encodable body part isn't a 52 | /// file URL. 53 | /// - bodyPartFilenameInvalid: The filename of the `fileURL` provided has either an empty 54 | /// `lastPathComponent` or `pathExtension. 55 | /// - bodyPartFileNotReachable: The file at the `fileURL` provided was not reachable. 56 | /// - bodyPartFileNotReachableWithError: Attempting to check the reachability of the `fileURL` provided threw 57 | /// an error. 58 | /// - bodyPartFileIsDirectory: The file at the `fileURL` provided is actually a directory. 59 | /// - bodyPartFileSizeNotAvailable: The size of the file at the `fileURL` provided was not returned by 60 | /// the system. 61 | /// - bodyPartFileSizeQueryFailedWithError: The attempt to find the size of the file at the `fileURL` provided 62 | /// threw an error. 63 | /// - bodyPartInputStreamCreationFailed: An `InputStream` could not be created for the provided `fileURL`. 64 | /// - outputStreamCreationFailed: An `OutputStream` could not be created when attempting to write the 65 | /// encoded data to disk. 66 | /// - outputStreamFileAlreadyExists: The encoded body data could not be writtent disk because a file 67 | /// already exists at the provided `fileURL`. 68 | /// - outputStreamURLInvalid: The `fileURL` provided for writing the encoded body data to disk is 69 | /// not a file URL. 70 | /// - outputStreamWriteFailed: The attempt to write the encoded body data to disk failed with an 71 | /// underlying error. 72 | /// - inputStreamReadFailed: The attempt to read an encoded body part `InputStream` failed with 73 | /// underlying system error. 74 | public enum MultipartEncodingFailureReason { 75 | case bodyPartURLInvalid(url: URL) 76 | case bodyPartFilenameInvalid(in: URL) 77 | case bodyPartFileNotReachable(at: URL) 78 | case bodyPartFileNotReachableWithError(atURL: URL, error: Error) 79 | case bodyPartFileIsDirectory(at: URL) 80 | case bodyPartFileSizeNotAvailable(at: URL) 81 | case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error) 82 | case bodyPartInputStreamCreationFailed(for: URL) 83 | 84 | case outputStreamCreationFailed(for: URL) 85 | case outputStreamFileAlreadyExists(at: URL) 86 | case outputStreamURLInvalid(url: URL) 87 | case outputStreamWriteFailed(error: Error) 88 | 89 | case inputStreamReadFailed(error: Error) 90 | } 91 | 92 | /// The underlying reason the response validation error occurred. 93 | /// 94 | /// - dataFileNil: The data file containing the server response did not exist. 95 | /// - dataFileReadFailed: The data file containing the server response could not be read. 96 | /// - missingContentType: The response did not contain a `Content-Type` and the `acceptableContentTypes` 97 | /// provided did not contain wildcard type. 98 | /// - unacceptableContentType: The response `Content-Type` did not match any type in the provided 99 | /// `acceptableContentTypes`. 100 | /// - unacceptableStatusCode: The response status code was not acceptable. 101 | public enum ResponseValidationFailureReason { 102 | case dataFileNil 103 | case dataFileReadFailed(at: URL) 104 | case missingContentType(acceptableContentTypes: [String]) 105 | case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) 106 | case unacceptableStatusCode(code: Int) 107 | } 108 | 109 | /// The underlying reason the response serialization error occurred. 110 | /// 111 | /// - inputDataNil: The server response contained no data. 112 | /// - inputDataNilOrZeroLength: The server response contained no data or the data was zero length. 113 | /// - inputFileNil: The file containing the server response did not exist. 114 | /// - inputFileReadFailed: The file containing the server response could not be read. 115 | /// - stringSerializationFailed: String serialization failed using the provided `String.Encoding`. 116 | /// - jsonSerializationFailed: JSON serialization failed with an underlying system error. 117 | /// - propertyListSerializationFailed: Property list serialization failed with an underlying system error. 118 | public enum ResponseSerializationFailureReason { 119 | case inputDataNil 120 | case inputDataNilOrZeroLength 121 | case inputFileNil 122 | case inputFileReadFailed(at: URL) 123 | case stringSerializationFailed(encoding: String.Encoding) 124 | case jsonSerializationFailed(error: Error) 125 | case propertyListSerializationFailed(error: Error) 126 | } 127 | 128 | case invalidURL(url: URLConvertible) 129 | case parameterEncodingFailed(reason: ParameterEncodingFailureReason) 130 | case multipartEncodingFailed(reason: MultipartEncodingFailureReason) 131 | case responseValidationFailed(reason: ResponseValidationFailureReason) 132 | case responseSerializationFailed(reason: ResponseSerializationFailureReason) 133 | } 134 | 135 | // MARK: - Adapt Error 136 | 137 | struct AdaptError: Error { 138 | let error: Error 139 | } 140 | 141 | extension Error { 142 | var underlyingAdaptError: Error? { return (self as? AdaptError)?.error } 143 | } 144 | 145 | // MARK: - Error Booleans 146 | 147 | extension AFError { 148 | /// Returns whether the AFError is an invalid URL error. 149 | public var isInvalidURLError: Bool { 150 | if case .invalidURL = self { return true } 151 | return false 152 | } 153 | 154 | /// Returns whether the AFError is a parameter encoding error. When `true`, the `underlyingError` property will 155 | /// contain the associated value. 156 | public var isParameterEncodingError: Bool { 157 | if case .parameterEncodingFailed = self { return true } 158 | return false 159 | } 160 | 161 | /// Returns whether the AFError is a multipart encoding error. When `true`, the `url` and `underlyingError` properties 162 | /// will contain the associated values. 163 | public var isMultipartEncodingError: Bool { 164 | if case .multipartEncodingFailed = self { return true } 165 | return false 166 | } 167 | 168 | /// Returns whether the `AFError` is a response validation error. When `true`, the `acceptableContentTypes`, 169 | /// `responseContentType`, and `responseCode` properties will contain the associated values. 170 | public var isResponseValidationError: Bool { 171 | if case .responseValidationFailed = self { return true } 172 | return false 173 | } 174 | 175 | /// Returns whether the `AFError` is a response serialization error. When `true`, the `failedStringEncoding` and 176 | /// `underlyingError` properties will contain the associated values. 177 | public var isResponseSerializationError: Bool { 178 | if case .responseSerializationFailed = self { return true } 179 | return false 180 | } 181 | } 182 | 183 | // MARK: - Convenience Properties 184 | 185 | extension AFError { 186 | /// The `URLConvertible` associated with the error. 187 | public var urlConvertible: URLConvertible? { 188 | switch self { 189 | case .invalidURL(let url): 190 | return url 191 | default: 192 | return nil 193 | } 194 | } 195 | 196 | /// The `URL` associated with the error. 197 | public var url: URL? { 198 | switch self { 199 | case .multipartEncodingFailed(let reason): 200 | return reason.url 201 | default: 202 | return nil 203 | } 204 | } 205 | 206 | /// The `Error` returned by a system framework associated with a `.parameterEncodingFailed`, 207 | /// `.multipartEncodingFailed` or `.responseSerializationFailed` error. 208 | public var underlyingError: Error? { 209 | switch self { 210 | case .parameterEncodingFailed(let reason): 211 | return reason.underlyingError 212 | case .multipartEncodingFailed(let reason): 213 | return reason.underlyingError 214 | case .responseSerializationFailed(let reason): 215 | return reason.underlyingError 216 | default: 217 | return nil 218 | } 219 | } 220 | 221 | /// The acceptable `Content-Type`s of a `.responseValidationFailed` error. 222 | public var acceptableContentTypes: [String]? { 223 | switch self { 224 | case .responseValidationFailed(let reason): 225 | return reason.acceptableContentTypes 226 | default: 227 | return nil 228 | } 229 | } 230 | 231 | /// The response `Content-Type` of a `.responseValidationFailed` error. 232 | public var responseContentType: String? { 233 | switch self { 234 | case .responseValidationFailed(let reason): 235 | return reason.responseContentType 236 | default: 237 | return nil 238 | } 239 | } 240 | 241 | /// The response code of a `.responseValidationFailed` error. 242 | public var responseCode: Int? { 243 | switch self { 244 | case .responseValidationFailed(let reason): 245 | return reason.responseCode 246 | default: 247 | return nil 248 | } 249 | } 250 | 251 | /// The `String.Encoding` associated with a failed `.stringResponse()` call. 252 | public var failedStringEncoding: String.Encoding? { 253 | switch self { 254 | case .responseSerializationFailed(let reason): 255 | return reason.failedStringEncoding 256 | default: 257 | return nil 258 | } 259 | } 260 | } 261 | 262 | extension AFError.ParameterEncodingFailureReason { 263 | var underlyingError: Error? { 264 | switch self { 265 | case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error): 266 | return error 267 | default: 268 | return nil 269 | } 270 | } 271 | } 272 | 273 | extension AFError.MultipartEncodingFailureReason { 274 | var url: URL? { 275 | switch self { 276 | case .bodyPartURLInvalid(let url), .bodyPartFilenameInvalid(let url), .bodyPartFileNotReachable(let url), 277 | .bodyPartFileIsDirectory(let url), .bodyPartFileSizeNotAvailable(let url), 278 | .bodyPartInputStreamCreationFailed(let url), .outputStreamCreationFailed(let url), 279 | .outputStreamFileAlreadyExists(let url), .outputStreamURLInvalid(let url), 280 | .bodyPartFileNotReachableWithError(let url, _), .bodyPartFileSizeQueryFailedWithError(let url, _): 281 | return url 282 | default: 283 | return nil 284 | } 285 | } 286 | 287 | var underlyingError: Error? { 288 | switch self { 289 | case .bodyPartFileNotReachableWithError(_, let error), .bodyPartFileSizeQueryFailedWithError(_, let error), 290 | .outputStreamWriteFailed(let error), .inputStreamReadFailed(let error): 291 | return error 292 | default: 293 | return nil 294 | } 295 | } 296 | } 297 | 298 | extension AFError.ResponseValidationFailureReason { 299 | var acceptableContentTypes: [String]? { 300 | switch self { 301 | case .missingContentType(let types), .unacceptableContentType(let types, _): 302 | return types 303 | default: 304 | return nil 305 | } 306 | } 307 | 308 | var responseContentType: String? { 309 | switch self { 310 | case .unacceptableContentType(_, let responseType): 311 | return responseType 312 | default: 313 | return nil 314 | } 315 | } 316 | 317 | var responseCode: Int? { 318 | switch self { 319 | case .unacceptableStatusCode(let code): 320 | return code 321 | default: 322 | return nil 323 | } 324 | } 325 | } 326 | 327 | extension AFError.ResponseSerializationFailureReason { 328 | var failedStringEncoding: String.Encoding? { 329 | switch self { 330 | case .stringSerializationFailed(let encoding): 331 | return encoding 332 | default: 333 | return nil 334 | } 335 | } 336 | 337 | var underlyingError: Error? { 338 | switch self { 339 | case .jsonSerializationFailed(let error), .propertyListSerializationFailed(let error): 340 | return error 341 | default: 342 | return nil 343 | } 344 | } 345 | } 346 | 347 | // MARK: - Error Descriptions 348 | 349 | extension AFError: LocalizedError { 350 | public var errorDescription: String? { 351 | switch self { 352 | case .invalidURL(let url): 353 | return "URL is not valid: \(url)" 354 | case .parameterEncodingFailed(let reason): 355 | return reason.localizedDescription 356 | case .multipartEncodingFailed(let reason): 357 | return reason.localizedDescription 358 | case .responseValidationFailed(let reason): 359 | return reason.localizedDescription 360 | case .responseSerializationFailed(let reason): 361 | return reason.localizedDescription 362 | } 363 | } 364 | } 365 | 366 | extension AFError.ParameterEncodingFailureReason { 367 | var localizedDescription: String { 368 | switch self { 369 | case .missingURL: 370 | return "URL request to encode was missing a URL" 371 | case .jsonEncodingFailed(let error): 372 | return "JSON could not be encoded because of error:\n\(error.localizedDescription)" 373 | case .propertyListEncodingFailed(let error): 374 | return "PropertyList could not be encoded because of error:\n\(error.localizedDescription)" 375 | } 376 | } 377 | } 378 | 379 | extension AFError.MultipartEncodingFailureReason { 380 | var localizedDescription: String { 381 | switch self { 382 | case .bodyPartURLInvalid(let url): 383 | return "The URL provided is not a file URL: \(url)" 384 | case .bodyPartFilenameInvalid(let url): 385 | return "The URL provided does not have a valid filename: \(url)" 386 | case .bodyPartFileNotReachable(let url): 387 | return "The URL provided is not reachable: \(url)" 388 | case .bodyPartFileNotReachableWithError(let url, let error): 389 | return ( 390 | "The system returned an error while checking the provided URL for " + 391 | "reachability.\nURL: \(url)\nError: \(error)" 392 | ) 393 | case .bodyPartFileIsDirectory(let url): 394 | return "The URL provided is a directory: \(url)" 395 | case .bodyPartFileSizeNotAvailable(let url): 396 | return "Could not fetch the file size from the provided URL: \(url)" 397 | case .bodyPartFileSizeQueryFailedWithError(let url, let error): 398 | return ( 399 | "The system returned an error while attempting to fetch the file size from the " + 400 | "provided URL.\nURL: \(url)\nError: \(error)" 401 | ) 402 | case .bodyPartInputStreamCreationFailed(let url): 403 | return "Failed to create an InputStream for the provided URL: \(url)" 404 | case .outputStreamCreationFailed(let url): 405 | return "Failed to create an OutputStream for URL: \(url)" 406 | case .outputStreamFileAlreadyExists(let url): 407 | return "A file already exists at the provided URL: \(url)" 408 | case .outputStreamURLInvalid(let url): 409 | return "The provided OutputStream URL is invalid: \(url)" 410 | case .outputStreamWriteFailed(let error): 411 | return "OutputStream write failed with error: \(error)" 412 | case .inputStreamReadFailed(let error): 413 | return "InputStream read failed with error: \(error)" 414 | } 415 | } 416 | } 417 | 418 | extension AFError.ResponseSerializationFailureReason { 419 | var localizedDescription: String { 420 | switch self { 421 | case .inputDataNil: 422 | return "Response could not be serialized, input data was nil." 423 | case .inputDataNilOrZeroLength: 424 | return "Response could not be serialized, input data was nil or zero length." 425 | case .inputFileNil: 426 | return "Response could not be serialized, input file was nil." 427 | case .inputFileReadFailed(let url): 428 | return "Response could not be serialized, input file could not be read: \(url)." 429 | case .stringSerializationFailed(let encoding): 430 | return "String could not be serialized with encoding: \(encoding)." 431 | case .jsonSerializationFailed(let error): 432 | return "JSON could not be serialized because of error:\n\(error.localizedDescription)" 433 | case .propertyListSerializationFailed(let error): 434 | return "PropertyList could not be serialized because of error:\n\(error.localizedDescription)" 435 | } 436 | } 437 | } 438 | 439 | extension AFError.ResponseValidationFailureReason { 440 | var localizedDescription: String { 441 | switch self { 442 | case .dataFileNil: 443 | return "Response could not be validated, data file was nil." 444 | case .dataFileReadFailed(let url): 445 | return "Response could not be validated, data file could not be read: \(url)." 446 | case .missingContentType(let types): 447 | return ( 448 | "Response Content-Type was missing and acceptable content types " + 449 | "(\(types.joined(separator: ","))) do not match \"*/*\"." 450 | ) 451 | case .unacceptableContentType(let acceptableTypes, let responseType): 452 | return ( 453 | "Response Content-Type \"\(responseType)\" does not match any acceptable types: " + 454 | "\(acceptableTypes.joined(separator: ","))." 455 | ) 456 | case .unacceptableStatusCode(let code): 457 | return "Response status code was unacceptable: \(code)." 458 | } 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct 28 | /// URL requests. 29 | public protocol URLConvertible { 30 | /// Returns a URL that conforms to RFC 2396 or throws an `Error`. 31 | /// 32 | /// - throws: An `Error` if the type cannot be converted to a `URL`. 33 | /// 34 | /// - returns: A URL or throws an `Error`. 35 | func asURL() throws -> URL 36 | } 37 | 38 | extension String: URLConvertible { 39 | /// Returns a URL if `self` represents a valid URL string that conforms to RFC 2396 or throws an `AFError`. 40 | /// 41 | /// - throws: An `AFError.invalidURL` if `self` is not a valid URL string. 42 | /// 43 | /// - returns: A URL or throws an `AFError`. 44 | public func asURL() throws -> URL { 45 | guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) } 46 | return url 47 | } 48 | } 49 | 50 | extension URL: URLConvertible { 51 | /// Returns self. 52 | public func asURL() throws -> URL { return self } 53 | } 54 | 55 | extension URLComponents: URLConvertible { 56 | /// Returns a URL if `url` is not nil, otherise throws an `Error`. 57 | /// 58 | /// - throws: An `AFError.invalidURL` if `url` is `nil`. 59 | /// 60 | /// - returns: A URL or throws an `AFError`. 61 | public func asURL() throws -> URL { 62 | guard let url = url else { throw AFError.invalidURL(url: self) } 63 | return url 64 | } 65 | } 66 | 67 | // MARK: - 68 | 69 | /// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. 70 | public protocol URLRequestConvertible { 71 | /// Returns a URL request or throws if an `Error` was encountered. 72 | /// 73 | /// - throws: An `Error` if the underlying `URLRequest` is `nil`. 74 | /// 75 | /// - returns: A URL request. 76 | func asURLRequest() throws -> URLRequest 77 | } 78 | 79 | extension URLRequestConvertible { 80 | /// The URL request. 81 | public var urlRequest: URLRequest? { return try? asURLRequest() } 82 | } 83 | 84 | extension URLRequest: URLRequestConvertible { 85 | /// Returns a URL request or throws if an `Error` was encountered. 86 | public func asURLRequest() throws -> URLRequest { return self } 87 | } 88 | 89 | // MARK: - 90 | 91 | extension URLRequest { 92 | /// Creates an instance with the specified `method`, `urlString` and `headers`. 93 | /// 94 | /// - parameter url: The URL. 95 | /// - parameter method: The HTTP method. 96 | /// - parameter headers: The HTTP headers. `nil` by default. 97 | /// 98 | /// - returns: The new `URLRequest` instance. 99 | public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { 100 | let url = try url.asURL() 101 | 102 | self.init(url: url) 103 | 104 | httpMethod = method.rawValue 105 | 106 | if let headers = headers { 107 | for (headerField, headerValue) in headers { 108 | setValue(headerValue, forHTTPHeaderField: headerField) 109 | } 110 | } 111 | } 112 | 113 | func adapt(using adapter: RequestAdapter?) throws -> URLRequest { 114 | guard let adapter = adapter else { return self } 115 | return try adapter.adapt(self) 116 | } 117 | } 118 | 119 | // MARK: - Data Request 120 | 121 | /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`, 122 | /// `method`, `parameters`, `encoding` and `headers`. 123 | /// 124 | /// - parameter url: The URL. 125 | /// - parameter method: The HTTP method. `.get` by default. 126 | /// - parameter parameters: The parameters. `nil` by default. 127 | /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. 128 | /// - parameter headers: The HTTP headers. `nil` by default. 129 | /// 130 | /// - returns: The created `DataRequest`. 131 | @discardableResult 132 | public func request( 133 | _ url: URLConvertible, 134 | method: HTTPMethod = .get, 135 | parameters: Parameters? = nil, 136 | encoding: ParameterEncoding = URLEncoding.default, 137 | headers: HTTPHeaders? = nil) 138 | -> DataRequest 139 | { 140 | return SessionManager.default.request( 141 | url, 142 | method: method, 143 | parameters: parameters, 144 | encoding: encoding, 145 | headers: headers 146 | ) 147 | } 148 | 149 | /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the 150 | /// specified `urlRequest`. 151 | /// 152 | /// - parameter urlRequest: The URL request 153 | /// 154 | /// - returns: The created `DataRequest`. 155 | @discardableResult 156 | public func request(_ urlRequest: URLRequestConvertible) -> DataRequest { 157 | return SessionManager.default.request(urlRequest) 158 | } 159 | 160 | // MARK: - Download Request 161 | 162 | // MARK: URL Request 163 | 164 | /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of the specified `url`, 165 | /// `method`, `parameters`, `encoding`, `headers` and save them to the `destination`. 166 | /// 167 | /// If `destination` is not specified, the contents will remain in the temporary location determined by the 168 | /// underlying URL session. 169 | /// 170 | /// - parameter url: The URL. 171 | /// - parameter method: The HTTP method. `.get` by default. 172 | /// - parameter parameters: The parameters. `nil` by default. 173 | /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. 174 | /// - parameter headers: The HTTP headers. `nil` by default. 175 | /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. 176 | /// 177 | /// - returns: The created `DownloadRequest`. 178 | @discardableResult 179 | public func download( 180 | _ url: URLConvertible, 181 | method: HTTPMethod = .get, 182 | parameters: Parameters? = nil, 183 | encoding: ParameterEncoding = URLEncoding.default, 184 | headers: HTTPHeaders? = nil, 185 | to destination: DownloadRequest.DownloadFileDestination? = nil) 186 | -> DownloadRequest 187 | { 188 | return SessionManager.default.download( 189 | url, 190 | method: method, 191 | parameters: parameters, 192 | encoding: encoding, 193 | headers: headers, 194 | to: destination 195 | ) 196 | } 197 | 198 | /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the 199 | /// specified `urlRequest` and save them to the `destination`. 200 | /// 201 | /// If `destination` is not specified, the contents will remain in the temporary location determined by the 202 | /// underlying URL session. 203 | /// 204 | /// - parameter urlRequest: The URL request. 205 | /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. 206 | /// 207 | /// - returns: The created `DownloadRequest`. 208 | @discardableResult 209 | public func download( 210 | _ urlRequest: URLRequestConvertible, 211 | to destination: DownloadRequest.DownloadFileDestination? = nil) 212 | -> DownloadRequest 213 | { 214 | return SessionManager.default.download(urlRequest, to: destination) 215 | } 216 | 217 | // MARK: Resume Data 218 | 219 | /// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a 220 | /// previous request cancellation to retrieve the contents of the original request and save them to the `destination`. 221 | /// 222 | /// If `destination` is not specified, the contents will remain in the temporary location determined by the 223 | /// underlying URL session. 224 | /// 225 | /// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken 226 | /// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the 227 | /// data is written incorrectly and will always fail to resume the download. For more information about the bug and 228 | /// possible workarounds, please refer to the following Stack Overflow post: 229 | /// 230 | /// - http://stackoverflow.com/a/39347461/1342462 231 | /// 232 | /// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask` 233 | /// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional 234 | /// information. 235 | /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. 236 | /// 237 | /// - returns: The created `DownloadRequest`. 238 | @discardableResult 239 | public func download( 240 | resumingWith resumeData: Data, 241 | to destination: DownloadRequest.DownloadFileDestination? = nil) 242 | -> DownloadRequest 243 | { 244 | return SessionManager.default.download(resumingWith: resumeData, to: destination) 245 | } 246 | 247 | // MARK: - Upload Request 248 | 249 | // MARK: File 250 | 251 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` 252 | /// for uploading the `file`. 253 | /// 254 | /// - parameter file: The file to upload. 255 | /// - parameter url: The URL. 256 | /// - parameter method: The HTTP method. `.post` by default. 257 | /// - parameter headers: The HTTP headers. `nil` by default. 258 | /// 259 | /// - returns: The created `UploadRequest`. 260 | @discardableResult 261 | public func upload( 262 | _ fileURL: URL, 263 | to url: URLConvertible, 264 | method: HTTPMethod = .post, 265 | headers: HTTPHeaders? = nil) 266 | -> UploadRequest 267 | { 268 | return SessionManager.default.upload(fileURL, to: url, method: method, headers: headers) 269 | } 270 | 271 | /// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for 272 | /// uploading the `file`. 273 | /// 274 | /// - parameter file: The file to upload. 275 | /// - parameter urlRequest: The URL request. 276 | /// 277 | /// - returns: The created `UploadRequest`. 278 | @discardableResult 279 | public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest { 280 | return SessionManager.default.upload(fileURL, with: urlRequest) 281 | } 282 | 283 | // MARK: Data 284 | 285 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` 286 | /// for uploading the `data`. 287 | /// 288 | /// - parameter data: The data to upload. 289 | /// - parameter url: The URL. 290 | /// - parameter method: The HTTP method. `.post` by default. 291 | /// - parameter headers: The HTTP headers. `nil` by default. 292 | /// 293 | /// - returns: The created `UploadRequest`. 294 | @discardableResult 295 | public func upload( 296 | _ data: Data, 297 | to url: URLConvertible, 298 | method: HTTPMethod = .post, 299 | headers: HTTPHeaders? = nil) 300 | -> UploadRequest 301 | { 302 | return SessionManager.default.upload(data, to: url, method: method, headers: headers) 303 | } 304 | 305 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for 306 | /// uploading the `data`. 307 | /// 308 | /// - parameter data: The data to upload. 309 | /// - parameter urlRequest: The URL request. 310 | /// 311 | /// - returns: The created `UploadRequest`. 312 | @discardableResult 313 | public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest { 314 | return SessionManager.default.upload(data, with: urlRequest) 315 | } 316 | 317 | // MARK: InputStream 318 | 319 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` 320 | /// for uploading the `stream`. 321 | /// 322 | /// - parameter stream: The stream to upload. 323 | /// - parameter url: The URL. 324 | /// - parameter method: The HTTP method. `.post` by default. 325 | /// - parameter headers: The HTTP headers. `nil` by default. 326 | /// 327 | /// - returns: The created `UploadRequest`. 328 | @discardableResult 329 | public func upload( 330 | _ stream: InputStream, 331 | to url: URLConvertible, 332 | method: HTTPMethod = .post, 333 | headers: HTTPHeaders? = nil) 334 | -> UploadRequest 335 | { 336 | return SessionManager.default.upload(stream, to: url, method: method, headers: headers) 337 | } 338 | 339 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for 340 | /// uploading the `stream`. 341 | /// 342 | /// - parameter urlRequest: The URL request. 343 | /// - parameter stream: The stream to upload. 344 | /// 345 | /// - returns: The created `UploadRequest`. 346 | @discardableResult 347 | public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest { 348 | return SessionManager.default.upload(stream, with: urlRequest) 349 | } 350 | 351 | // MARK: MultipartFormData 352 | 353 | /// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls 354 | /// `encodingCompletion` with new `UploadRequest` using the `url`, `method` and `headers`. 355 | /// 356 | /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative 357 | /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most 358 | /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to 359 | /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory 360 | /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be 361 | /// used for larger payloads such as video content. 362 | /// 363 | /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory 364 | /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, 365 | /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk 366 | /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding 367 | /// technique was used. 368 | /// 369 | /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. 370 | /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. 371 | /// `multipartFormDataEncodingMemoryThreshold` by default. 372 | /// - parameter url: The URL. 373 | /// - parameter method: The HTTP method. `.post` by default. 374 | /// - parameter headers: The HTTP headers. `nil` by default. 375 | /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. 376 | public func upload( 377 | multipartFormData: @escaping (MultipartFormData) -> Void, 378 | usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, 379 | to url: URLConvertible, 380 | method: HTTPMethod = .post, 381 | headers: HTTPHeaders? = nil, 382 | encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) 383 | { 384 | return SessionManager.default.upload( 385 | multipartFormData: multipartFormData, 386 | usingThreshold: encodingMemoryThreshold, 387 | to: url, 388 | method: method, 389 | headers: headers, 390 | encodingCompletion: encodingCompletion 391 | ) 392 | } 393 | 394 | /// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and 395 | /// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`. 396 | /// 397 | /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative 398 | /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most 399 | /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to 400 | /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory 401 | /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be 402 | /// used for larger payloads such as video content. 403 | /// 404 | /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory 405 | /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, 406 | /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk 407 | /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding 408 | /// technique was used. 409 | /// 410 | /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. 411 | /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. 412 | /// `multipartFormDataEncodingMemoryThreshold` by default. 413 | /// - parameter urlRequest: The URL request. 414 | /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. 415 | public func upload( 416 | multipartFormData: @escaping (MultipartFormData) -> Void, 417 | usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, 418 | with urlRequest: URLRequestConvertible, 419 | encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) 420 | { 421 | return SessionManager.default.upload( 422 | multipartFormData: multipartFormData, 423 | usingThreshold: encodingMemoryThreshold, 424 | with: urlRequest, 425 | encodingCompletion: encodingCompletion 426 | ) 427 | } 428 | 429 | #if !os(watchOS) 430 | 431 | // MARK: - Stream Request 432 | 433 | // MARK: Hostname and Port 434 | 435 | /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname` 436 | /// and `port`. 437 | /// 438 | /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 439 | /// 440 | /// - parameter hostName: The hostname of the server to connect to. 441 | /// - parameter port: The port of the server to connect to. 442 | /// 443 | /// - returns: The created `StreamRequest`. 444 | @discardableResult 445 | @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) 446 | public func stream(withHostName hostName: String, port: Int) -> StreamRequest { 447 | return SessionManager.default.stream(withHostName: hostName, port: port) 448 | } 449 | 450 | // MARK: NetService 451 | 452 | /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`. 453 | /// 454 | /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 455 | /// 456 | /// - parameter netService: The net service used to identify the endpoint. 457 | /// 458 | /// - returns: The created `StreamRequest`. 459 | @discardableResult 460 | @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) 461 | public func stream(with netService: NetService) -> StreamRequest { 462 | return SessionManager.default.stream(with: netService) 463 | } 464 | 465 | #endif 466 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2016 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/MultipartFormData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultipartFormData.swift 3 | // 4 | // Copyright (c) 2014-2016 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 | #if os(iOS) || os(watchOS) || os(tvOS) 28 | import MobileCoreServices 29 | #elseif os(macOS) 30 | import CoreServices 31 | #endif 32 | 33 | /// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode 34 | /// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead 35 | /// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the 36 | /// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for 37 | /// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset. 38 | /// 39 | /// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well 40 | /// and the w3 form documentation. 41 | /// 42 | /// - https://www.ietf.org/rfc/rfc2388.txt 43 | /// - https://www.ietf.org/rfc/rfc2045.txt 44 | /// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13 45 | open class MultipartFormData { 46 | 47 | // MARK: - Helper Types 48 | 49 | struct EncodingCharacters { 50 | static let crlf = "\r\n" 51 | } 52 | 53 | struct BoundaryGenerator { 54 | enum BoundaryType { 55 | case initial, encapsulated, final 56 | } 57 | 58 | static func randomBoundary() -> String { 59 | return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random()) 60 | } 61 | 62 | static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data { 63 | let boundaryText: String 64 | 65 | switch boundaryType { 66 | case .initial: 67 | boundaryText = "--\(boundary)\(EncodingCharacters.crlf)" 68 | case .encapsulated: 69 | boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)" 70 | case .final: 71 | boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)" 72 | } 73 | 74 | return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)! 75 | } 76 | } 77 | 78 | class BodyPart { 79 | let headers: HTTPHeaders 80 | let bodyStream: InputStream 81 | let bodyContentLength: UInt64 82 | var hasInitialBoundary = false 83 | var hasFinalBoundary = false 84 | 85 | init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) { 86 | self.headers = headers 87 | self.bodyStream = bodyStream 88 | self.bodyContentLength = bodyContentLength 89 | } 90 | } 91 | 92 | // MARK: - Properties 93 | 94 | /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`. 95 | open var contentType: String { return "multipart/form-data; boundary=\(boundary)" } 96 | 97 | /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries. 98 | public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } } 99 | 100 | /// The boundary used to separate the body parts in the encoded form data. 101 | public let boundary: String 102 | 103 | private var bodyParts: [BodyPart] 104 | private var bodyPartError: AFError? 105 | private let streamBufferSize: Int 106 | 107 | // MARK: - Lifecycle 108 | 109 | /// Creates a multipart form data object. 110 | /// 111 | /// - returns: The multipart form data object. 112 | public init() { 113 | self.boundary = BoundaryGenerator.randomBoundary() 114 | self.bodyParts = [] 115 | 116 | /// 117 | /// The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more 118 | /// information, please refer to the following article: 119 | /// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html 120 | /// 121 | 122 | self.streamBufferSize = 1024 123 | } 124 | 125 | // MARK: - Body Parts 126 | 127 | /// Creates a body part from the data and appends it to the multipart form data object. 128 | /// 129 | /// The body part data will be encoded using the following format: 130 | /// 131 | /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header) 132 | /// - Encoded data 133 | /// - Multipart form boundary 134 | /// 135 | /// - parameter data: The data to encode into the multipart form data. 136 | /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 137 | public func append(_ data: Data, withName name: String) { 138 | let headers = contentHeaders(withName: name) 139 | let stream = InputStream(data: data) 140 | let length = UInt64(data.count) 141 | 142 | append(stream, withLength: length, headers: headers) 143 | } 144 | 145 | /// Creates a body part from the data and appends it to the multipart form data object. 146 | /// 147 | /// The body part data will be encoded using the following format: 148 | /// 149 | /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header) 150 | /// - `Content-Type: #{generated mimeType}` (HTTP Header) 151 | /// - Encoded data 152 | /// - Multipart form boundary 153 | /// 154 | /// - parameter data: The data to encode into the multipart form data. 155 | /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 156 | /// - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header. 157 | public func append(_ data: Data, withName name: String, mimeType: String) { 158 | let headers = contentHeaders(withName: name, mimeType: mimeType) 159 | let stream = InputStream(data: data) 160 | let length = UInt64(data.count) 161 | 162 | append(stream, withLength: length, headers: headers) 163 | } 164 | 165 | /// Creates a body part from the data and appends it to the multipart form data object. 166 | /// 167 | /// The body part data will be encoded using the following format: 168 | /// 169 | /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) 170 | /// - `Content-Type: #{mimeType}` (HTTP Header) 171 | /// - Encoded file data 172 | /// - Multipart form boundary 173 | /// 174 | /// - parameter data: The data to encode into the multipart form data. 175 | /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 176 | /// - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header. 177 | /// - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header. 178 | public func append(_ data: Data, withName name: String, fileName: String, mimeType: String) { 179 | let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) 180 | let stream = InputStream(data: data) 181 | let length = UInt64(data.count) 182 | 183 | append(stream, withLength: length, headers: headers) 184 | } 185 | 186 | /// Creates a body part from the file and appends it to the multipart form data object. 187 | /// 188 | /// The body part data will be encoded using the following format: 189 | /// 190 | /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header) 191 | /// - `Content-Type: #{generated mimeType}` (HTTP Header) 192 | /// - Encoded file data 193 | /// - Multipart form boundary 194 | /// 195 | /// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the 196 | /// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the 197 | /// system associated MIME type. 198 | /// 199 | /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. 200 | /// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. 201 | public func append(_ fileURL: URL, withName name: String) { 202 | let fileName = fileURL.lastPathComponent 203 | let pathExtension = fileURL.pathExtension 204 | 205 | if !fileName.isEmpty && !pathExtension.isEmpty { 206 | let mime = mimeType(forPathExtension: pathExtension) 207 | append(fileURL, withName: name, fileName: fileName, mimeType: mime) 208 | } else { 209 | setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL)) 210 | } 211 | } 212 | 213 | /// Creates a body part from the file and appends it to the multipart form data object. 214 | /// 215 | /// The body part data will be encoded using the following format: 216 | /// 217 | /// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header) 218 | /// - Content-Type: #{mimeType} (HTTP Header) 219 | /// - Encoded file data 220 | /// - Multipart form boundary 221 | /// 222 | /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. 223 | /// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. 224 | /// - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header. 225 | /// - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header. 226 | public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) { 227 | let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) 228 | 229 | //============================================================ 230 | // Check 1 - is file URL? 231 | //============================================================ 232 | 233 | guard fileURL.isFileURL else { 234 | setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL)) 235 | return 236 | } 237 | 238 | //============================================================ 239 | // Check 2 - is file URL reachable? 240 | //============================================================ 241 | 242 | do { 243 | let isReachable = try fileURL.checkPromisedItemIsReachable() 244 | guard isReachable else { 245 | setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL)) 246 | return 247 | } 248 | } catch { 249 | setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error)) 250 | return 251 | } 252 | 253 | //============================================================ 254 | // Check 3 - is file URL a directory? 255 | //============================================================ 256 | 257 | var isDirectory: ObjCBool = false 258 | let path = fileURL.path 259 | 260 | guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else 261 | { 262 | setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL)) 263 | return 264 | } 265 | 266 | //============================================================ 267 | // Check 4 - can the file size be extracted? 268 | //============================================================ 269 | 270 | let bodyContentLength: UInt64 271 | 272 | do { 273 | guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else { 274 | setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL)) 275 | return 276 | } 277 | 278 | bodyContentLength = fileSize.uint64Value 279 | } 280 | catch { 281 | setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error)) 282 | return 283 | } 284 | 285 | //============================================================ 286 | // Check 5 - can a stream be created from file URL? 287 | //============================================================ 288 | 289 | guard let stream = InputStream(url: fileURL) else { 290 | setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL)) 291 | return 292 | } 293 | 294 | append(stream, withLength: bodyContentLength, headers: headers) 295 | } 296 | 297 | /// Creates a body part from the stream and appends it to the multipart form data object. 298 | /// 299 | /// The body part data will be encoded using the following format: 300 | /// 301 | /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) 302 | /// - `Content-Type: #{mimeType}` (HTTP Header) 303 | /// - Encoded stream data 304 | /// - Multipart form boundary 305 | /// 306 | /// - parameter stream: The input stream to encode in the multipart form data. 307 | /// - parameter length: The content length of the stream. 308 | /// - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header. 309 | /// - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header. 310 | /// - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header. 311 | public func append( 312 | _ stream: InputStream, 313 | withLength length: UInt64, 314 | name: String, 315 | fileName: String, 316 | mimeType: String) 317 | { 318 | let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) 319 | append(stream, withLength: length, headers: headers) 320 | } 321 | 322 | /// Creates a body part with the headers, stream and length and appends it to the multipart form data object. 323 | /// 324 | /// The body part data will be encoded using the following format: 325 | /// 326 | /// - HTTP headers 327 | /// - Encoded stream data 328 | /// - Multipart form boundary 329 | /// 330 | /// - parameter stream: The input stream to encode in the multipart form data. 331 | /// - parameter length: The content length of the stream. 332 | /// - parameter headers: The HTTP headers for the body part. 333 | public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) { 334 | let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length) 335 | bodyParts.append(bodyPart) 336 | } 337 | 338 | // MARK: - Data Encoding 339 | 340 | /// Encodes all the appended body parts into a single `Data` value. 341 | /// 342 | /// It is important to note that this method will load all the appended body parts into memory all at the same 343 | /// time. This method should only be used when the encoded data will have a small memory footprint. For large data 344 | /// cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method. 345 | /// 346 | /// - throws: An `AFError` if encoding encounters an error. 347 | /// 348 | /// - returns: The encoded `Data` if encoding is successful. 349 | public func encode() throws -> Data { 350 | if let bodyPartError = bodyPartError { 351 | throw bodyPartError 352 | } 353 | 354 | var encoded = Data() 355 | 356 | bodyParts.first?.hasInitialBoundary = true 357 | bodyParts.last?.hasFinalBoundary = true 358 | 359 | for bodyPart in bodyParts { 360 | let encodedData = try encode(bodyPart) 361 | encoded.append(encodedData) 362 | } 363 | 364 | return encoded 365 | } 366 | 367 | /// Writes the appended body parts into the given file URL. 368 | /// 369 | /// This process is facilitated by reading and writing with input and output streams, respectively. Thus, 370 | /// this approach is very memory efficient and should be used for large body part data. 371 | /// 372 | /// - parameter fileURL: The file URL to write the multipart form data into. 373 | /// 374 | /// - throws: An `AFError` if encoding encounters an error. 375 | public func writeEncodedData(to fileURL: URL) throws { 376 | if let bodyPartError = bodyPartError { 377 | throw bodyPartError 378 | } 379 | 380 | if FileManager.default.fileExists(atPath: fileURL.path) { 381 | throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL)) 382 | } else if !fileURL.isFileURL { 383 | throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL)) 384 | } 385 | 386 | guard let outputStream = OutputStream(url: fileURL, append: false) else { 387 | throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL)) 388 | } 389 | 390 | outputStream.open() 391 | defer { outputStream.close() } 392 | 393 | self.bodyParts.first?.hasInitialBoundary = true 394 | self.bodyParts.last?.hasFinalBoundary = true 395 | 396 | for bodyPart in self.bodyParts { 397 | try write(bodyPart, to: outputStream) 398 | } 399 | } 400 | 401 | // MARK: - Private - Body Part Encoding 402 | 403 | private func encode(_ bodyPart: BodyPart) throws -> Data { 404 | var encoded = Data() 405 | 406 | let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() 407 | encoded.append(initialData) 408 | 409 | let headerData = encodeHeaders(for: bodyPart) 410 | encoded.append(headerData) 411 | 412 | let bodyStreamData = try encodeBodyStream(for: bodyPart) 413 | encoded.append(bodyStreamData) 414 | 415 | if bodyPart.hasFinalBoundary { 416 | encoded.append(finalBoundaryData()) 417 | } 418 | 419 | return encoded 420 | } 421 | 422 | private func encodeHeaders(for bodyPart: BodyPart) -> Data { 423 | var headerText = "" 424 | 425 | for (key, value) in bodyPart.headers { 426 | headerText += "\(key): \(value)\(EncodingCharacters.crlf)" 427 | } 428 | headerText += EncodingCharacters.crlf 429 | 430 | return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)! 431 | } 432 | 433 | private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data { 434 | let inputStream = bodyPart.bodyStream 435 | inputStream.open() 436 | defer { inputStream.close() } 437 | 438 | var encoded = Data() 439 | 440 | while inputStream.hasBytesAvailable { 441 | var buffer = [UInt8](repeating: 0, count: streamBufferSize) 442 | let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) 443 | 444 | if let error = inputStream.streamError { 445 | throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error)) 446 | } 447 | 448 | if bytesRead > 0 { 449 | encoded.append(buffer, count: bytesRead) 450 | } else { 451 | break 452 | } 453 | } 454 | 455 | return encoded 456 | } 457 | 458 | // MARK: - Private - Writing Body Part to Output Stream 459 | 460 | private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws { 461 | try writeInitialBoundaryData(for: bodyPart, to: outputStream) 462 | try writeHeaderData(for: bodyPart, to: outputStream) 463 | try writeBodyStream(for: bodyPart, to: outputStream) 464 | try writeFinalBoundaryData(for: bodyPart, to: outputStream) 465 | } 466 | 467 | private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws { 468 | let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() 469 | return try write(initialData, to: outputStream) 470 | } 471 | 472 | private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws { 473 | let headerData = encodeHeaders(for: bodyPart) 474 | return try write(headerData, to: outputStream) 475 | } 476 | 477 | private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws { 478 | let inputStream = bodyPart.bodyStream 479 | 480 | inputStream.open() 481 | defer { inputStream.close() } 482 | 483 | while inputStream.hasBytesAvailable { 484 | var buffer = [UInt8](repeating: 0, count: streamBufferSize) 485 | let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) 486 | 487 | if let streamError = inputStream.streamError { 488 | throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError)) 489 | } 490 | 491 | if bytesRead > 0 { 492 | if buffer.count != bytesRead { 493 | buffer = Array(buffer[0.. 0, outputStream.hasSpaceAvailable { 522 | let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite) 523 | 524 | if let error = outputStream.streamError { 525 | throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error)) 526 | } 527 | 528 | bytesToWrite -= bytesWritten 529 | 530 | if bytesToWrite > 0 { 531 | buffer = Array(buffer[bytesWritten.. String { 539 | if 540 | let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(), 541 | let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() 542 | { 543 | return contentType as String 544 | } 545 | 546 | return "application/octet-stream" 547 | } 548 | 549 | // MARK: - Private - Content Headers 550 | 551 | private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> [String: String] { 552 | var disposition = "form-data; name=\"\(name)\"" 553 | if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" } 554 | 555 | var headers = ["Content-Disposition": disposition] 556 | if let mimeType = mimeType { headers["Content-Type"] = mimeType } 557 | 558 | return headers 559 | } 560 | 561 | // MARK: - Private - Boundary Encoding 562 | 563 | private func initialBoundaryData() -> Data { 564 | return BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary) 565 | } 566 | 567 | private func encapsulatedBoundaryData() -> Data { 568 | return BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary) 569 | } 570 | 571 | private func finalBoundaryData() -> Data { 572 | return BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary) 573 | } 574 | 575 | // MARK: - Private - Errors 576 | 577 | private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) { 578 | guard bodyPartError == nil else { return } 579 | bodyPartError = AFError.multipartEncodingFailed(reason: reason) 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/NetworkReachabilityManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManager.swift 3 | // 4 | // Copyright (c) 2014-2016 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 flags.contains(.reachable) else { return .notReachable } 186 | 187 | var networkStatus: NetworkReachabilityStatus = .notReachable 188 | 189 | if !flags.contains(.connectionRequired) { networkStatus = .reachable(.ethernetOrWiFi) } 190 | 191 | if flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) { 192 | if !flags.contains(.interventionRequired) { networkStatus = .reachable(.ethernetOrWiFi) } 193 | } 194 | 195 | #if os(iOS) 196 | if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } 197 | #endif 198 | 199 | return networkStatus 200 | } 201 | } 202 | 203 | // MARK: - 204 | 205 | extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} 206 | 207 | /// Returns whether the two network reachability status values are equal. 208 | /// 209 | /// - parameter lhs: The left-hand side value to compare. 210 | /// - parameter rhs: The right-hand side value to compare. 211 | /// 212 | /// - returns: `true` if the two values are equal, `false` otherwise. 213 | public func ==( 214 | lhs: NetworkReachabilityManager.NetworkReachabilityStatus, 215 | rhs: NetworkReachabilityManager.NetworkReachabilityStatus) 216 | -> Bool 217 | { 218 | switch (lhs, rhs) { 219 | case (.unknown, .unknown): 220 | return true 221 | case (.notReachable, .notReachable): 222 | return true 223 | case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)): 224 | return lhsConnectionType == rhsConnectionType 225 | default: 226 | return false 227 | } 228 | } 229 | 230 | #endif 231 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2016 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-2016 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 | 245 | return components.map { "\($0)=\($1)" }.joined(separator: "&") 246 | } 247 | 248 | private func encodesParametersInURL(with method: HTTPMethod) -> Bool { 249 | switch destination { 250 | case .queryString: 251 | return true 252 | case .httpBody: 253 | return false 254 | default: 255 | break 256 | } 257 | 258 | switch method { 259 | case .get, .head, .delete: 260 | return true 261 | default: 262 | return false 263 | } 264 | } 265 | } 266 | 267 | // MARK: - 268 | 269 | /// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the 270 | /// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. 271 | public struct JSONEncoding: ParameterEncoding { 272 | 273 | // MARK: Properties 274 | 275 | /// Returns a `JSONEncoding` instance with default writing options. 276 | public static var `default`: JSONEncoding { return JSONEncoding() } 277 | 278 | /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options. 279 | public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) } 280 | 281 | /// The options for writing the parameters as JSON data. 282 | public let options: JSONSerialization.WritingOptions 283 | 284 | // MARK: Initialization 285 | 286 | /// Creates a `JSONEncoding` instance using the specified options. 287 | /// 288 | /// - parameter options: The options for writing the parameters as JSON data. 289 | /// 290 | /// - returns: The new `JSONEncoding` instance. 291 | public init(options: JSONSerialization.WritingOptions = []) { 292 | self.options = options 293 | } 294 | 295 | // MARK: Encoding 296 | 297 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 298 | /// 299 | /// - parameter urlRequest: The request to have parameters applied. 300 | /// - parameter parameters: The parameters to apply. 301 | /// 302 | /// - throws: An `Error` if the encoding process encounters an error. 303 | /// 304 | /// - returns: The encoded request. 305 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { 306 | var urlRequest = try urlRequest.asURLRequest() 307 | 308 | guard let parameters = parameters else { return urlRequest } 309 | 310 | do { 311 | let data = try JSONSerialization.data(withJSONObject: parameters, options: options) 312 | 313 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 314 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 315 | } 316 | 317 | urlRequest.httpBody = data 318 | } catch { 319 | throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) 320 | } 321 | 322 | return urlRequest 323 | } 324 | 325 | /// Creates a URL request by encoding the JSON object and setting the resulting data on the HTTP body. 326 | /// 327 | /// - parameter urlRequest: The request to apply the JSON object to. 328 | /// - parameter jsonObject: The JSON object to apply to the request. 329 | /// 330 | /// - throws: An `Error` if the encoding process encounters an error. 331 | /// 332 | /// - returns: The encoded request. 333 | public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest { 334 | var urlRequest = try urlRequest.asURLRequest() 335 | 336 | guard let jsonObject = jsonObject else { return urlRequest } 337 | 338 | do { 339 | let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options) 340 | 341 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 342 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 343 | } 344 | 345 | urlRequest.httpBody = data 346 | } catch { 347 | throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) 348 | } 349 | 350 | return urlRequest 351 | } 352 | } 353 | 354 | // MARK: - 355 | 356 | /// Uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the 357 | /// associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header 358 | /// field of an encoded request is set to `application/x-plist`. 359 | public struct PropertyListEncoding: ParameterEncoding { 360 | 361 | // MARK: Properties 362 | 363 | /// Returns a default `PropertyListEncoding` instance. 364 | public static var `default`: PropertyListEncoding { return PropertyListEncoding() } 365 | 366 | /// Returns a `PropertyListEncoding` instance with xml formatting and default writing options. 367 | public static var xml: PropertyListEncoding { return PropertyListEncoding(format: .xml) } 368 | 369 | /// Returns a `PropertyListEncoding` instance with binary formatting and default writing options. 370 | public static var binary: PropertyListEncoding { return PropertyListEncoding(format: .binary) } 371 | 372 | /// The property list serialization format. 373 | public let format: PropertyListSerialization.PropertyListFormat 374 | 375 | /// The options for writing the parameters as plist data. 376 | public let options: PropertyListSerialization.WriteOptions 377 | 378 | // MARK: Initialization 379 | 380 | /// Creates a `PropertyListEncoding` instance using the specified format and options. 381 | /// 382 | /// - parameter format: The property list serialization format. 383 | /// - parameter options: The options for writing the parameters as plist data. 384 | /// 385 | /// - returns: The new `PropertyListEncoding` instance. 386 | public init( 387 | format: PropertyListSerialization.PropertyListFormat = .xml, 388 | options: PropertyListSerialization.WriteOptions = 0) 389 | { 390 | self.format = format 391 | self.options = options 392 | } 393 | 394 | // MARK: Encoding 395 | 396 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 397 | /// 398 | /// - parameter urlRequest: The request to have parameters applied. 399 | /// - parameter parameters: The parameters to apply. 400 | /// 401 | /// - throws: An `Error` if the encoding process encounters an error. 402 | /// 403 | /// - returns: The encoded request. 404 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { 405 | var urlRequest = try urlRequest.asURLRequest() 406 | 407 | guard let parameters = parameters else { return urlRequest } 408 | 409 | do { 410 | let data = try PropertyListSerialization.data( 411 | fromPropertyList: parameters, 412 | format: format, 413 | options: options 414 | ) 415 | 416 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 417 | urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") 418 | } 419 | 420 | urlRequest.httpBody = data 421 | } catch { 422 | throw AFError.parameterEncodingFailed(reason: .propertyListEncodingFailed(error: error)) 423 | } 424 | 425 | return urlRequest 426 | } 427 | } 428 | 429 | // MARK: - 430 | 431 | extension NSNumber { 432 | fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) } 433 | } 434 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // 4 | // Copyright (c) 2014-2016 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 | /// Used to store all data associated with an non-serialized response of a download request. 149 | public struct DefaultDownloadResponse { 150 | /// The URL request sent to the server. 151 | public let request: URLRequest? 152 | 153 | /// The server's response to the URL request. 154 | public let response: HTTPURLResponse? 155 | 156 | /// The temporary destination URL of the data returned from the server. 157 | public let temporaryURL: URL? 158 | 159 | /// The final destination URL of the data returned from the server if it was moved. 160 | public let destinationURL: URL? 161 | 162 | /// The resume data generated if the request was cancelled. 163 | public let resumeData: Data? 164 | 165 | /// The error encountered while executing or validating the request. 166 | public let error: Error? 167 | 168 | /// The timeline of the complete lifecycle of the request. 169 | public let timeline: Timeline 170 | 171 | var _metrics: AnyObject? 172 | 173 | /// Creates a `DefaultDownloadResponse` instance from the specified parameters. 174 | /// 175 | /// - Parameters: 176 | /// - request: The URL request sent to the server. 177 | /// - response: The server's response to the URL request. 178 | /// - temporaryURL: The temporary destination URL of the data returned from the server. 179 | /// - destinationURL: The final destination URL of the data returned from the server if it was moved. 180 | /// - resumeData: The resume data generated if the request was cancelled. 181 | /// - error: The error encountered while executing or validating the request. 182 | /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default. 183 | /// - metrics: The task metrics containing the request / response statistics. `nil` by default. 184 | public init( 185 | request: URLRequest?, 186 | response: HTTPURLResponse?, 187 | temporaryURL: URL?, 188 | destinationURL: URL?, 189 | resumeData: Data?, 190 | error: Error?, 191 | timeline: Timeline = Timeline(), 192 | metrics: AnyObject? = nil) 193 | { 194 | self.request = request 195 | self.response = response 196 | self.temporaryURL = temporaryURL 197 | self.destinationURL = destinationURL 198 | self.resumeData = resumeData 199 | self.error = error 200 | self.timeline = timeline 201 | } 202 | } 203 | 204 | // MARK: - 205 | 206 | /// Used to store all data associated with a serialized response of a download request. 207 | public struct DownloadResponse { 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 result of response serialization. 224 | public let result: Result 225 | 226 | /// The timeline of the complete lifecycle of the request. 227 | public let timeline: Timeline 228 | 229 | /// Returns the associated value of the result if it is a success, `nil` otherwise. 230 | public var value: Value? { return result.value } 231 | 232 | /// Returns the associated error value if the result if it is a failure, `nil` otherwise. 233 | public var error: Error? { return result.error } 234 | 235 | var _metrics: AnyObject? 236 | 237 | /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization. 238 | /// 239 | /// - parameter request: The URL request sent to the server. 240 | /// - parameter response: The server's response to the URL request. 241 | /// - parameter temporaryURL: The temporary destination URL of the data returned from the server. 242 | /// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved. 243 | /// - parameter resumeData: The resume data generated if the request was cancelled. 244 | /// - parameter result: The result of response serialization. 245 | /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. 246 | /// 247 | /// - returns: The new `DownloadResponse` instance. 248 | public init( 249 | request: URLRequest?, 250 | response: HTTPURLResponse?, 251 | temporaryURL: URL?, 252 | destinationURL: URL?, 253 | resumeData: Data?, 254 | result: Result, 255 | timeline: Timeline = Timeline()) 256 | { 257 | self.request = request 258 | self.response = response 259 | self.temporaryURL = temporaryURL 260 | self.destinationURL = destinationURL 261 | self.resumeData = resumeData 262 | self.result = result 263 | self.timeline = timeline 264 | } 265 | } 266 | 267 | // MARK: - 268 | 269 | extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible { 270 | /// The textual representation used when written to an output stream, which includes whether the result was a 271 | /// success or failure. 272 | public var description: String { 273 | return result.debugDescription 274 | } 275 | 276 | /// The debug textual representation used when written to an output stream, which includes the URL request, the URL 277 | /// response, the temporary and destination URLs, the resume data, the response serialization result and the 278 | /// timeline. 279 | public var debugDescription: String { 280 | var output: [String] = [] 281 | 282 | output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil") 283 | output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") 284 | output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")") 285 | output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")") 286 | output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes") 287 | output.append("[Result]: \(result.debugDescription)") 288 | output.append("[Timeline]: \(timeline.debugDescription)") 289 | 290 | return output.joined(separator: "\n") 291 | } 292 | } 293 | 294 | // MARK: - 295 | 296 | protocol Response { 297 | /// The task metrics containing the request / response statistics. 298 | var _metrics: AnyObject? { get set } 299 | mutating func add(_ metrics: AnyObject?) 300 | } 301 | 302 | extension Response { 303 | mutating func add(_ metrics: AnyObject?) { 304 | #if !os(watchOS) 305 | guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return } 306 | guard let metrics = metrics as? URLSessionTaskMetrics else { return } 307 | 308 | _metrics = metrics 309 | #endif 310 | } 311 | } 312 | 313 | // MARK: - 314 | 315 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 316 | extension DefaultDataResponse: Response { 317 | #if !os(watchOS) 318 | /// The task metrics containing the request / response statistics. 319 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 320 | #endif 321 | } 322 | 323 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 324 | extension DataResponse: Response { 325 | #if !os(watchOS) 326 | /// The task metrics containing the request / response statistics. 327 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 328 | #endif 329 | } 330 | 331 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 332 | extension DefaultDownloadResponse: Response { 333 | #if !os(watchOS) 334 | /// The task metrics containing the request / response statistics. 335 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 336 | #endif 337 | } 338 | 339 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 340 | extension DownloadResponse: Response { 341 | #if !os(watchOS) 342 | /// The task metrics containing the request / response statistics. 343 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 344 | #endif 345 | } 346 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Result.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result.swift 3 | // 4 | // Copyright (c) 2014-2016 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 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ServerTrustPolicy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerTrustPolicy.swift 3 | // 4 | // Copyright (c) 2014-2016 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-2016 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 | didSet { reset() } 44 | } 45 | 46 | var initialResponseTime: CFAbsoluteTime? 47 | var credential: URLCredential? 48 | var metrics: AnyObject? // URLSessionTaskMetrics 49 | 50 | // MARK: Lifecycle 51 | 52 | init(task: URLSessionTask?) { 53 | self.task = task 54 | 55 | self.queue = { 56 | let operationQueue = OperationQueue() 57 | 58 | operationQueue.maxConcurrentOperationCount = 1 59 | operationQueue.isSuspended = true 60 | operationQueue.qualityOfService = .utility 61 | 62 | return operationQueue 63 | }() 64 | } 65 | 66 | func reset() { 67 | error = nil 68 | initialResponseTime = nil 69 | } 70 | 71 | // MARK: URLSessionTaskDelegate 72 | 73 | var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? 74 | var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? 75 | var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? 76 | var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)? 77 | 78 | @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:) 79 | func urlSession( 80 | _ session: URLSession, 81 | task: URLSessionTask, 82 | willPerformHTTPRedirection response: HTTPURLResponse, 83 | newRequest request: URLRequest, 84 | completionHandler: @escaping (URLRequest?) -> Void) 85 | { 86 | var redirectRequest: URLRequest? = request 87 | 88 | if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { 89 | redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) 90 | } 91 | 92 | completionHandler(redirectRequest) 93 | } 94 | 95 | @objc(URLSession:task:didReceiveChallenge:completionHandler:) 96 | func urlSession( 97 | _ session: URLSession, 98 | task: URLSessionTask, 99 | didReceive challenge: URLAuthenticationChallenge, 100 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 101 | { 102 | var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling 103 | var credential: URLCredential? 104 | 105 | if let taskDidReceiveChallenge = taskDidReceiveChallenge { 106 | (disposition, credential) = taskDidReceiveChallenge(session, task, challenge) 107 | } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 108 | let host = challenge.protectionSpace.host 109 | 110 | if 111 | let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host), 112 | let serverTrust = challenge.protectionSpace.serverTrust 113 | { 114 | if serverTrustPolicy.evaluate(serverTrust, forHost: host) { 115 | disposition = .useCredential 116 | credential = URLCredential(trust: serverTrust) 117 | } else { 118 | disposition = .cancelAuthenticationChallenge 119 | } 120 | } 121 | } else { 122 | if challenge.previousFailureCount > 0 { 123 | disposition = .rejectProtectionSpace 124 | } else { 125 | credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace) 126 | 127 | if credential != nil { 128 | disposition = .useCredential 129 | } 130 | } 131 | } 132 | 133 | completionHandler(disposition, credential) 134 | } 135 | 136 | @objc(URLSession:task:needNewBodyStream:) 137 | func urlSession( 138 | _ session: URLSession, 139 | task: URLSessionTask, 140 | needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) 141 | { 142 | var bodyStream: InputStream? 143 | 144 | if let taskNeedNewBodyStream = taskNeedNewBodyStream { 145 | bodyStream = taskNeedNewBodyStream(session, task) 146 | } 147 | 148 | completionHandler(bodyStream) 149 | } 150 | 151 | @objc(URLSession:task:didCompleteWithError:) 152 | func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 153 | if let taskDidCompleteWithError = taskDidCompleteWithError { 154 | taskDidCompleteWithError(session, task, error) 155 | } else { 156 | if let error = error { 157 | if self.error == nil { self.error = error } 158 | 159 | if 160 | let downloadDelegate = self as? DownloadTaskDelegate, 161 | let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data 162 | { 163 | downloadDelegate.resumeData = resumeData 164 | } 165 | } 166 | 167 | queue.isSuspended = false 168 | } 169 | } 170 | } 171 | 172 | // MARK: - 173 | 174 | class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate { 175 | 176 | // MARK: Properties 177 | 178 | var dataTask: URLSessionDataTask { return task as! URLSessionDataTask } 179 | 180 | override var data: Data? { 181 | if dataStream != nil { 182 | return nil 183 | } else { 184 | return mutableData 185 | } 186 | } 187 | 188 | var progress: Progress 189 | var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 190 | 191 | var dataStream: ((_ data: Data) -> Void)? 192 | 193 | private var totalBytesReceived: Int64 = 0 194 | private var mutableData: Data 195 | 196 | private var expectedContentLength: Int64? 197 | 198 | // MARK: Lifecycle 199 | 200 | override init(task: URLSessionTask?) { 201 | mutableData = Data() 202 | progress = Progress(totalUnitCount: 0) 203 | 204 | super.init(task: task) 205 | } 206 | 207 | override func reset() { 208 | super.reset() 209 | 210 | progress = Progress(totalUnitCount: 0) 211 | totalBytesReceived = 0 212 | mutableData = Data() 213 | expectedContentLength = nil 214 | } 215 | 216 | // MARK: URLSessionDataDelegate 217 | 218 | var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)? 219 | var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? 220 | var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? 221 | var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? 222 | 223 | func urlSession( 224 | _ session: URLSession, 225 | dataTask: URLSessionDataTask, 226 | didReceive response: URLResponse, 227 | completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) 228 | { 229 | var disposition: URLSession.ResponseDisposition = .allow 230 | 231 | expectedContentLength = response.expectedContentLength 232 | 233 | if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { 234 | disposition = dataTaskDidReceiveResponse(session, dataTask, response) 235 | } 236 | 237 | completionHandler(disposition) 238 | } 239 | 240 | func urlSession( 241 | _ session: URLSession, 242 | dataTask: URLSessionDataTask, 243 | didBecome downloadTask: URLSessionDownloadTask) 244 | { 245 | dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask) 246 | } 247 | 248 | func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 249 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 250 | 251 | if let dataTaskDidReceiveData = dataTaskDidReceiveData { 252 | dataTaskDidReceiveData(session, dataTask, data) 253 | } else { 254 | if let dataStream = dataStream { 255 | dataStream(data) 256 | } else { 257 | mutableData.append(data) 258 | } 259 | 260 | let bytesReceived = Int64(data.count) 261 | totalBytesReceived += bytesReceived 262 | let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown 263 | 264 | progress.totalUnitCount = totalBytesExpected 265 | progress.completedUnitCount = totalBytesReceived 266 | 267 | if let progressHandler = progressHandler { 268 | progressHandler.queue.async { progressHandler.closure(self.progress) } 269 | } 270 | } 271 | } 272 | 273 | func urlSession( 274 | _ session: URLSession, 275 | dataTask: URLSessionDataTask, 276 | willCacheResponse proposedResponse: CachedURLResponse, 277 | completionHandler: @escaping (CachedURLResponse?) -> Void) 278 | { 279 | var cachedResponse: CachedURLResponse? = proposedResponse 280 | 281 | if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { 282 | cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse) 283 | } 284 | 285 | completionHandler(cachedResponse) 286 | } 287 | } 288 | 289 | // MARK: - 290 | 291 | class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate { 292 | 293 | // MARK: Properties 294 | 295 | var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask } 296 | 297 | var progress: Progress 298 | var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 299 | 300 | var resumeData: Data? 301 | override var data: Data? { return resumeData } 302 | 303 | var destination: DownloadRequest.DownloadFileDestination? 304 | 305 | var temporaryURL: URL? 306 | var destinationURL: URL? 307 | 308 | var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL } 309 | 310 | // MARK: Lifecycle 311 | 312 | override init(task: URLSessionTask?) { 313 | progress = Progress(totalUnitCount: 0) 314 | super.init(task: task) 315 | } 316 | 317 | override func reset() { 318 | super.reset() 319 | 320 | progress = Progress(totalUnitCount: 0) 321 | resumeData = nil 322 | } 323 | 324 | // MARK: URLSessionDownloadDelegate 325 | 326 | var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)? 327 | var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? 328 | var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? 329 | 330 | func urlSession( 331 | _ session: URLSession, 332 | downloadTask: URLSessionDownloadTask, 333 | didFinishDownloadingTo location: URL) 334 | { 335 | temporaryURL = location 336 | 337 | guard 338 | let destination = destination, 339 | let response = downloadTask.response as? HTTPURLResponse 340 | else { return } 341 | 342 | let result = destination(location, response) 343 | let destinationURL = result.destinationURL 344 | let options = result.options 345 | 346 | self.destinationURL = destinationURL 347 | 348 | do { 349 | if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) { 350 | try FileManager.default.removeItem(at: destinationURL) 351 | } 352 | 353 | if options.contains(.createIntermediateDirectories) { 354 | let directory = destinationURL.deletingLastPathComponent() 355 | try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) 356 | } 357 | 358 | try FileManager.default.moveItem(at: location, to: destinationURL) 359 | } catch { 360 | self.error = error 361 | } 362 | } 363 | 364 | func urlSession( 365 | _ session: URLSession, 366 | downloadTask: URLSessionDownloadTask, 367 | didWriteData bytesWritten: Int64, 368 | totalBytesWritten: Int64, 369 | totalBytesExpectedToWrite: Int64) 370 | { 371 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 372 | 373 | if let downloadTaskDidWriteData = downloadTaskDidWriteData { 374 | downloadTaskDidWriteData( 375 | session, 376 | downloadTask, 377 | bytesWritten, 378 | totalBytesWritten, 379 | totalBytesExpectedToWrite 380 | ) 381 | } else { 382 | progress.totalUnitCount = totalBytesExpectedToWrite 383 | progress.completedUnitCount = totalBytesWritten 384 | 385 | if let progressHandler = progressHandler { 386 | progressHandler.queue.async { progressHandler.closure(self.progress) } 387 | } 388 | } 389 | } 390 | 391 | func urlSession( 392 | _ session: URLSession, 393 | downloadTask: URLSessionDownloadTask, 394 | didResumeAtOffset fileOffset: Int64, 395 | expectedTotalBytes: Int64) 396 | { 397 | if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { 398 | downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) 399 | } else { 400 | progress.totalUnitCount = expectedTotalBytes 401 | progress.completedUnitCount = fileOffset 402 | } 403 | } 404 | } 405 | 406 | // MARK: - 407 | 408 | class UploadTaskDelegate: DataTaskDelegate { 409 | 410 | // MARK: Properties 411 | 412 | var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask } 413 | 414 | var uploadProgress: Progress 415 | var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 416 | 417 | // MARK: Lifecycle 418 | 419 | override init(task: URLSessionTask?) { 420 | uploadProgress = Progress(totalUnitCount: 0) 421 | super.init(task: task) 422 | } 423 | 424 | override func reset() { 425 | super.reset() 426 | uploadProgress = Progress(totalUnitCount: 0) 427 | } 428 | 429 | // MARK: URLSessionTaskDelegate 430 | 431 | var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? 432 | 433 | func URLSession( 434 | _ session: URLSession, 435 | task: URLSessionTask, 436 | didSendBodyData bytesSent: Int64, 437 | totalBytesSent: Int64, 438 | totalBytesExpectedToSend: Int64) 439 | { 440 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 441 | 442 | if let taskDidSendBodyData = taskDidSendBodyData { 443 | taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) 444 | } else { 445 | uploadProgress.totalUnitCount = totalBytesExpectedToSend 446 | uploadProgress.completedUnitCount = totalBytesSent 447 | 448 | if let uploadProgressHandler = uploadProgressHandler { 449 | uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) } 450 | } 451 | } 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Timeline.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timeline.swift 3 | // 4 | // Copyright (c) 2014-2016 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-2016 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 | let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex) 52 | return split.components(separatedBy: "/") 53 | }() 54 | 55 | if let type = components.first, let subtype = components.last { 56 | self.type = type 57 | self.subtype = subtype 58 | } else { 59 | return nil 60 | } 61 | } 62 | 63 | func matches(_ mime: MIMEType) -> Bool { 64 | switch (type, subtype) { 65 | case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): 66 | return true 67 | default: 68 | return false 69 | } 70 | } 71 | } 72 | 73 | // MARK: Properties 74 | 75 | fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) } 76 | 77 | fileprivate var acceptableContentTypes: [String] { 78 | if let accept = request?.value(forHTTPHeaderField: "Accept") { 79 | return accept.components(separatedBy: ",") 80 | } 81 | 82 | return ["*/*"] 83 | } 84 | 85 | // MARK: Status Code 86 | 87 | fileprivate func validate( 88 | statusCode acceptableStatusCodes: S, 89 | response: HTTPURLResponse) 90 | -> ValidationResult 91 | where S.Iterator.Element == Int 92 | { 93 | if acceptableStatusCodes.contains(response.statusCode) { 94 | return .success 95 | } else { 96 | let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode) 97 | return .failure(AFError.responseValidationFailed(reason: reason)) 98 | } 99 | } 100 | 101 | // MARK: Content Type 102 | 103 | fileprivate func validate( 104 | contentType acceptableContentTypes: S, 105 | response: HTTPURLResponse, 106 | data: Data?) 107 | -> ValidationResult 108 | where S.Iterator.Element == String 109 | { 110 | guard let data = data, data.count > 0 else { return .success } 111 | 112 | guard 113 | let responseContentType = response.mimeType, 114 | let responseMIMEType = MIMEType(responseContentType) 115 | else { 116 | for contentType in acceptableContentTypes { 117 | if let mimeType = MIMEType(contentType), mimeType.isWildcard { 118 | return .success 119 | } 120 | } 121 | 122 | let error: AFError = { 123 | let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes)) 124 | return AFError.responseValidationFailed(reason: reason) 125 | }() 126 | 127 | return .failure(error) 128 | } 129 | 130 | for contentType in acceptableContentTypes { 131 | if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { 132 | return .success 133 | } 134 | } 135 | 136 | let error: AFError = { 137 | let reason: ErrorReason = .unacceptableContentType( 138 | acceptableContentTypes: Array(acceptableContentTypes), 139 | responseContentType: responseContentType 140 | ) 141 | 142 | return AFError.responseValidationFailed(reason: reason) 143 | }() 144 | 145 | return .failure(error) 146 | } 147 | } 148 | 149 | // MARK: - 150 | 151 | extension DataRequest { 152 | /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the 153 | /// request was valid. 154 | public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult 155 | 156 | /// Validates the request, using the specified closure. 157 | /// 158 | /// If validation fails, subsequent calls to response handlers will have an associated error. 159 | /// 160 | /// - parameter validation: A closure to validate the request. 161 | /// 162 | /// - returns: The request. 163 | @discardableResult 164 | public func validate(_ validation: @escaping Validation) -> Self { 165 | let validationExecution: () -> Void = { [unowned self] in 166 | if 167 | let response = self.response, 168 | self.delegate.error == nil, 169 | case let .failure(error) = validation(self.request, response, self.delegate.data) 170 | { 171 | self.delegate.error = error 172 | } 173 | } 174 | 175 | validations.append(validationExecution) 176 | 177 | return self 178 | } 179 | 180 | /// Validates that the response has a status code in the specified sequence. 181 | /// 182 | /// If validation fails, subsequent calls to response handlers will have an associated error. 183 | /// 184 | /// - parameter range: The range of acceptable status codes. 185 | /// 186 | /// - returns: The request. 187 | @discardableResult 188 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 189 | return validate { [unowned self] _, response, _ in 190 | return self.validate(statusCode: acceptableStatusCodes, response: response) 191 | } 192 | } 193 | 194 | /// Validates that the response has a content type in the specified sequence. 195 | /// 196 | /// If validation fails, subsequent calls to response handlers will have an associated error. 197 | /// 198 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 199 | /// 200 | /// - returns: The request. 201 | @discardableResult 202 | public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { 203 | return validate { [unowned self] _, response, data in 204 | return self.validate(contentType: acceptableContentTypes, response: response, data: data) 205 | } 206 | } 207 | 208 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 209 | /// type matches any specified in the Accept HTTP header field. 210 | /// 211 | /// If validation fails, subsequent calls to response handlers will have an associated error. 212 | /// 213 | /// - returns: The request. 214 | @discardableResult 215 | public func validate() -> Self { 216 | return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) 217 | } 218 | } 219 | 220 | // MARK: - 221 | 222 | extension DownloadRequest { 223 | /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a 224 | /// destination URL, and returns whether the request was valid. 225 | public typealias Validation = ( 226 | _ request: URLRequest?, 227 | _ response: HTTPURLResponse, 228 | _ temporaryURL: URL?, 229 | _ destinationURL: URL?) 230 | -> ValidationResult 231 | 232 | /// Validates the request, using the specified closure. 233 | /// 234 | /// If validation fails, subsequent calls to response handlers will have an associated error. 235 | /// 236 | /// - parameter validation: A closure to validate the request. 237 | /// 238 | /// - returns: The request. 239 | @discardableResult 240 | public func validate(_ validation: @escaping Validation) -> Self { 241 | let validationExecution: () -> Void = { [unowned self] in 242 | let request = self.request 243 | let temporaryURL = self.downloadDelegate.temporaryURL 244 | let destinationURL = self.downloadDelegate.destinationURL 245 | 246 | if 247 | let response = self.response, 248 | self.delegate.error == nil, 249 | case let .failure(error) = validation(request, response, temporaryURL, destinationURL) 250 | { 251 | self.delegate.error = error 252 | } 253 | } 254 | 255 | validations.append(validationExecution) 256 | 257 | return self 258 | } 259 | 260 | /// Validates that the response has a status code in the specified sequence. 261 | /// 262 | /// If validation fails, subsequent calls to response handlers will have an associated error. 263 | /// 264 | /// - parameter range: The range of acceptable status codes. 265 | /// 266 | /// - returns: The request. 267 | @discardableResult 268 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 269 | return validate { [unowned self] _, response, _, _ in 270 | return self.validate(statusCode: acceptableStatusCodes, response: response) 271 | } 272 | } 273 | 274 | /// Validates that the response has a content type in the specified sequence. 275 | /// 276 | /// If validation fails, subsequent calls to response handlers will have an associated error. 277 | /// 278 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 279 | /// 280 | /// - returns: The request. 281 | @discardableResult 282 | public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { 283 | return validate { [unowned self] _, response, _, _ in 284 | let fileURL = self.downloadDelegate.fileURL 285 | 286 | guard let validFileURL = fileURL else { 287 | return .failure(AFError.responseValidationFailed(reason: .dataFileNil)) 288 | } 289 | 290 | do { 291 | let data = try Data(contentsOf: validFileURL) 292 | return self.validate(contentType: acceptableContentTypes, response: response, data: data) 293 | } catch { 294 | return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL))) 295 | } 296 | } 297 | } 298 | 299 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 300 | /// type matches any specified in the Accept HTTP header field. 301 | /// 302 | /// If validation fails, subsequent calls to response handlers will have an associated error. 303 | /// 304 | /// - returns: The request. 305 | @discardableResult 306 | public func validate() -> Self { 307 | return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /Pods/Local Podspecs/Alamofire.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Alamofire", 3 | "version": "4.3.0", 4 | "license": "MIT", 5 | "summary": "Elegant HTTP Networking in Swift", 6 | "homepage": "https://github.com/Alamofire/Alamofire", 7 | "social_media_url": "http://twitter.com/AlamofireSF", 8 | "authors": { 9 | "Alamofire Software Foundation": "info@alamofire.org" 10 | }, 11 | "source": { 12 | "git": "https://github.com/Alamofire/Alamofire.git", 13 | "tag": "4.3.0" 14 | }, 15 | "platforms": { 16 | "ios": "8.0", 17 | "osx": "10.10", 18 | "tvos": "9.0", 19 | "watchos": "2.0" 20 | }, 21 | "source_files": "Source/*.swift" 22 | } 23 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.3.0) 3 | 4 | DEPENDENCIES: 5 | - Alamofire (from `https://github.com/Alamofire/Alamofire.git`) 6 | 7 | EXTERNAL SOURCES: 8 | Alamofire: 9 | :git: https://github.com/Alamofire/Alamofire.git 10 | 11 | CHECKOUT OPTIONS: 12 | Alamofire: 13 | :commit: fa3c6d09b603f417c7788adb51ca0cade75b3d67 14 | :git: https://github.com/Alamofire/Alamofire.git 15 | 16 | SPEC CHECKSUMS: 17 | Alamofire: 1f72088aff8f6b40828dadd61be2e9a31beca01e 18 | 19 | PODFILE CHECKSUM: 4f1874c3a06d7987724145d8a19093240900c2f8 20 | 21 | COCOAPODS: 1.2.0 22 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/forrestknight.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/forrestknight.xcuserdatad/xcschemes/Pods-RainyShinyCloudy.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/forrestknight.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Alamofire.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-RainyShinyCloudy.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 11E589BFAD56C971E55C1C7FC7895500 21 | 22 | primary 23 | 24 | 25 | 88E9EC28B8B46C3631E6B242B50F4442 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /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.3.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/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-RainyShinyCloudy/Pods-RainyShinyCloudy-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014-2016 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 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/Pods-RainyShinyCloudy-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-2016 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 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/Pods-RainyShinyCloudy-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_RainyShinyCloudy : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_RainyShinyCloudy 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/Pods-RainyShinyCloudy-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 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework" 93 | fi 94 | if [[ "$CONFIGURATION" == "Release" ]]; then 95 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework" 96 | fi 97 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 98 | wait 99 | fi 100 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/Pods-RainyShinyCloudy-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 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | *) 25 | TARGET_DEVICE_ARGS="--target-device mac" 26 | ;; 27 | esac 28 | 29 | install_resource() 30 | { 31 | if [[ "$1" = /* ]] ; then 32 | RESOURCE_PATH="$1" 33 | else 34 | RESOURCE_PATH="${PODS_ROOT}/$1" 35 | fi 36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 37 | cat << EOM 38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 39 | EOM 40 | exit 1 41 | fi 42 | case $RESOURCE_PATH in 43 | *.storyboard) 44 | 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}" 45 | 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} 46 | ;; 47 | *.xib) 48 | 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}" 49 | 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} 50 | ;; 51 | *.framework) 52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | ;; 57 | *.xcdatamodel) 58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 60 | ;; 61 | *.xcdatamodeld) 62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 64 | ;; 65 | *.xcmappingmodel) 66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 68 | ;; 69 | *.xcassets) 70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 72 | ;; 73 | *) 74 | echo "$RESOURCE_PATH" 75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 76 | ;; 77 | esac 78 | } 79 | 80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | fi 86 | rm -f "$RESOURCES_TO_COPY" 87 | 88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 89 | then 90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 92 | while read line; do 93 | if [[ $line != "${PODS_ROOT}*" ]]; then 94 | XCASSET_FILES+=("$line") 95 | fi 96 | done <<<"$OTHER_XCASSETS" 97 | 98 | 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}" 99 | fi 100 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/Pods-RainyShinyCloudy-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_RainyShinyCloudyVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_RainyShinyCloudyVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/Pods-RainyShinyCloudy.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" 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" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" 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_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/Pods-RainyShinyCloudy.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_RainyShinyCloudy { 2 | umbrella header "Pods-RainyShinyCloudy-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-RainyShinyCloudy/Pods-RainyShinyCloudy.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" 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" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" 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_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WeatherApp 2 | 3 | An iOS application that forecasts the weather for your current location via OpenWeather API. 4 | 5 | # What I Learned 6 | 7 | * Use CocoaPods & AlamoFire alongside OpenWeather API 8 | * Update UI with API data 9 | * Find user locaiton with CLLocationManager 10 | * Parse JSON data 11 | -------------------------------------------------------------------------------- /RainyShinyCloudy.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RainyShinyCloudy.xcodeproj/project.xcworkspace/xcuserdata/forrestknight.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ForrestKnight/WeatherApp/8012019e2cff996fdea025b58f092433d4735bec/RainyShinyCloudy.xcodeproj/project.xcworkspace/xcuserdata/forrestknight.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /RainyShinyCloudy.xcodeproj/xcuserdata/forrestknight.xcuserdatad/xcschemes/RainyShinyCloudy.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /RainyShinyCloudy.xcodeproj/xcuserdata/forrestknight.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RainyShinyCloudy.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 5E2EB0831E43D52B005AAADB 16 | 17 | primary 18 | 19 | 20 | 5E2EB0971E43D52B005AAADB 21 | 22 | primary 23 | 24 | 25 | 5E2EB0A21E43D52B005AAADB 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /RainyShinyCloudy.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RainyShinyCloudy.xcworkspace/xcuserdata/forrestknight.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ForrestKnight/WeatherApp/8012019e2cff996fdea025b58f092433d4735bec/RainyShinyCloudy.xcworkspace/xcuserdata/forrestknight.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /RainyShinyCloudy.xcworkspace/xcuserdata/forrestknight.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /RainyShinyCloudyTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RainyShinyCloudyTests/RainyShinyCloudyTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RainyShinyCloudyTests.swift 3 | // RainyShinyCloudyTests 4 | // 5 | // Created by Forrest Knight on 2/2/17. 6 | // Copyright © 2017 Forrest Knight. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import RainyShinyCloudy 11 | 12 | class RainyShinyCloudyTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /RainyShinyCloudyUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RainyShinyCloudyUITests/RainyShinyCloudyUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RainyShinyCloudyUITests.swift 3 | // RainyShinyCloudyUITests 4 | // 5 | // Created by Forrest Knight on 2/2/17. 6 | // Copyright © 2017 Forrest Knight. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class RainyShinyCloudyUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | --------------------------------------------------------------------------------