├── .gitignore ├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── Alamofire.swift │ │ ├── Download.swift │ │ ├── Error.swift │ │ ├── Manager.swift │ │ ├── MultipartFormData.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── Stream.swift │ │ ├── Upload.swift │ │ └── Validation.swift ├── Manifest.lock ├── Pods.xcodeproj │ └── project.pbxproj └── Target Support Files │ ├── Alamofire │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.modulemap │ ├── Alamofire.xcconfig │ └── Info.plist │ └── Pods-SSL-Pinning │ ├── Info.plist │ ├── Pods-SSL-Pinning-acknowledgements.markdown │ ├── Pods-SSL-Pinning-acknowledgements.plist │ ├── Pods-SSL-Pinning-dummy.m │ ├── Pods-SSL-Pinning-frameworks.sh │ ├── Pods-SSL-Pinning-resources.sh │ ├── Pods-SSL-Pinning-umbrella.h │ ├── Pods-SSL-Pinning.debug.xcconfig │ ├── Pods-SSL-Pinning.modulemap │ └── Pods-SSL-Pinning.release.xcconfig ├── Resources ├── corrupted.cer └── github.com.cer ├── SSL-Pinning.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── SSL-Pinning.xcworkspace └── contents.xcworkspacedata └── SSL-Pinning ├── AppDelegate.swift ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist └── ViewController.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/ 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | xcuserdata 12 | *.xccheckout 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | 19 | # OSX Noise 20 | .DS_STORE 21 | 22 | # Builds 23 | Builds/ 24 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'SSL-Pinning' do 4 | 5 | pod 'Alamofire', '~> 3.0' 6 | 7 | end 8 | 9 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (3.1.5) 3 | 4 | DEPENDENCIES: 5 | - Alamofire (~> 3.0) 6 | 7 | SPEC CHECKSUMS: 8 | Alamofire: 5f730ba29fd113b7ddd71c1e65d0c630acf5d7b0 9 | 10 | COCOAPODS: 0.39.0 11 | -------------------------------------------------------------------------------- /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/Alamofire.swift: -------------------------------------------------------------------------------- 1 | // Alamofire.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | // MARK: - URLStringConvertible 26 | 27 | /** 28 | Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to 29 | construct URL requests. 30 | */ 31 | public protocol URLStringConvertible { 32 | /** 33 | A URL that conforms to RFC 2396. 34 | 35 | Methods accepting a `URLStringConvertible` type parameter parse it according to RFCs 1738 and 1808. 36 | 37 | See https://tools.ietf.org/html/rfc2396 38 | See https://tools.ietf.org/html/rfc1738 39 | See https://tools.ietf.org/html/rfc1808 40 | */ 41 | var URLString: String { get } 42 | } 43 | 44 | extension String: URLStringConvertible { 45 | public var URLString: String { 46 | return self 47 | } 48 | } 49 | 50 | extension NSURL: URLStringConvertible { 51 | public var URLString: String { 52 | return absoluteString 53 | } 54 | } 55 | 56 | extension NSURLComponents: URLStringConvertible { 57 | public var URLString: String { 58 | return URL!.URLString 59 | } 60 | } 61 | 62 | extension NSURLRequest: URLStringConvertible { 63 | public var URLString: String { 64 | return URL!.URLString 65 | } 66 | } 67 | 68 | // MARK: - URLRequestConvertible 69 | 70 | /** 71 | Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. 72 | */ 73 | public protocol URLRequestConvertible { 74 | /// The URL request. 75 | var URLRequest: NSMutableURLRequest { get } 76 | } 77 | 78 | extension NSURLRequest: URLRequestConvertible { 79 | public var URLRequest: NSMutableURLRequest { 80 | return self.mutableCopy() as! NSMutableURLRequest 81 | } 82 | } 83 | 84 | // MARK: - Convenience 85 | 86 | func URLRequest( 87 | method: Method, 88 | _ URLString: URLStringConvertible, 89 | headers: [String: String]? = nil) 90 | -> NSMutableURLRequest 91 | { 92 | let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: URLString.URLString)!) 93 | mutableURLRequest.HTTPMethod = method.rawValue 94 | 95 | if let headers = headers { 96 | for (headerField, headerValue) in headers { 97 | mutableURLRequest.setValue(headerValue, forHTTPHeaderField: headerField) 98 | } 99 | } 100 | 101 | return mutableURLRequest 102 | } 103 | 104 | // MARK: - Request Methods 105 | 106 | /** 107 | Creates a request using the shared manager instance for the specified method, URL string, parameters, and 108 | parameter encoding. 109 | 110 | - parameter method: The HTTP method. 111 | - parameter URLString: The URL string. 112 | - parameter parameters: The parameters. `nil` by default. 113 | - parameter encoding: The parameter encoding. `.URL` by default. 114 | - parameter headers: The HTTP headers. `nil` by default. 115 | 116 | - returns: The created request. 117 | */ 118 | public func request( 119 | method: Method, 120 | _ URLString: URLStringConvertible, 121 | parameters: [String: AnyObject]? = nil, 122 | encoding: ParameterEncoding = .URL, 123 | headers: [String: String]? = nil) 124 | -> Request 125 | { 126 | return Manager.sharedInstance.request( 127 | method, 128 | URLString, 129 | parameters: parameters, 130 | encoding: encoding, 131 | headers: headers 132 | ) 133 | } 134 | 135 | /** 136 | Creates a request using the shared manager instance for the specified URL request. 137 | 138 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 139 | 140 | - parameter URLRequest: The URL request 141 | 142 | - returns: The created request. 143 | */ 144 | public func request(URLRequest: URLRequestConvertible) -> Request { 145 | return Manager.sharedInstance.request(URLRequest.URLRequest) 146 | } 147 | 148 | // MARK: - Upload Methods 149 | 150 | // MARK: File 151 | 152 | /** 153 | Creates an upload request using the shared manager instance for the specified method, URL string, and file. 154 | 155 | - parameter method: The HTTP method. 156 | - parameter URLString: The URL string. 157 | - parameter headers: The HTTP headers. `nil` by default. 158 | - parameter file: The file to upload. 159 | 160 | - returns: The created upload request. 161 | */ 162 | public func upload( 163 | method: Method, 164 | _ URLString: URLStringConvertible, 165 | headers: [String: String]? = nil, 166 | file: NSURL) 167 | -> Request 168 | { 169 | return Manager.sharedInstance.upload(method, URLString, headers: headers, file: file) 170 | } 171 | 172 | /** 173 | Creates an upload request using the shared manager instance for the specified URL request and file. 174 | 175 | - parameter URLRequest: The URL request. 176 | - parameter file: The file to upload. 177 | 178 | - returns: The created upload request. 179 | */ 180 | public func upload(URLRequest: URLRequestConvertible, file: NSURL) -> Request { 181 | return Manager.sharedInstance.upload(URLRequest, file: file) 182 | } 183 | 184 | // MARK: Data 185 | 186 | /** 187 | Creates an upload request using the shared manager instance for the specified method, URL string, and data. 188 | 189 | - parameter method: The HTTP method. 190 | - parameter URLString: The URL string. 191 | - parameter headers: The HTTP headers. `nil` by default. 192 | - parameter data: The data to upload. 193 | 194 | - returns: The created upload request. 195 | */ 196 | public func upload( 197 | method: Method, 198 | _ URLString: URLStringConvertible, 199 | headers: [String: String]? = nil, 200 | data: NSData) 201 | -> Request 202 | { 203 | return Manager.sharedInstance.upload(method, URLString, headers: headers, data: data) 204 | } 205 | 206 | /** 207 | Creates an upload request using the shared manager instance for the specified URL request and data. 208 | 209 | - parameter URLRequest: The URL request. 210 | - parameter data: The data to upload. 211 | 212 | - returns: The created upload request. 213 | */ 214 | public func upload(URLRequest: URLRequestConvertible, data: NSData) -> Request { 215 | return Manager.sharedInstance.upload(URLRequest, data: data) 216 | } 217 | 218 | // MARK: Stream 219 | 220 | /** 221 | Creates an upload request using the shared manager instance for the specified method, URL string, and stream. 222 | 223 | - parameter method: The HTTP method. 224 | - parameter URLString: The URL string. 225 | - parameter headers: The HTTP headers. `nil` by default. 226 | - parameter stream: The stream to upload. 227 | 228 | - returns: The created upload request. 229 | */ 230 | public func upload( 231 | method: Method, 232 | _ URLString: URLStringConvertible, 233 | headers: [String: String]? = nil, 234 | stream: NSInputStream) 235 | -> Request 236 | { 237 | return Manager.sharedInstance.upload(method, URLString, headers: headers, stream: stream) 238 | } 239 | 240 | /** 241 | Creates an upload request using the shared manager instance for the specified URL request and stream. 242 | 243 | - parameter URLRequest: The URL request. 244 | - parameter stream: The stream to upload. 245 | 246 | - returns: The created upload request. 247 | */ 248 | public func upload(URLRequest: URLRequestConvertible, stream: NSInputStream) -> Request { 249 | return Manager.sharedInstance.upload(URLRequest, stream: stream) 250 | } 251 | 252 | // MARK: MultipartFormData 253 | 254 | /** 255 | Creates an upload request using the shared manager instance for the specified method and URL string. 256 | 257 | - parameter method: The HTTP method. 258 | - parameter URLString: The URL string. 259 | - parameter headers: The HTTP headers. `nil` by default. 260 | - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. 261 | - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. 262 | `MultipartFormDataEncodingMemoryThreshold` by default. 263 | - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. 264 | */ 265 | public func upload( 266 | method: Method, 267 | _ URLString: URLStringConvertible, 268 | headers: [String: String]? = nil, 269 | multipartFormData: MultipartFormData -> Void, 270 | encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold, 271 | encodingCompletion: (Manager.MultipartFormDataEncodingResult -> Void)?) 272 | { 273 | return Manager.sharedInstance.upload( 274 | method, 275 | URLString, 276 | headers: headers, 277 | multipartFormData: multipartFormData, 278 | encodingMemoryThreshold: encodingMemoryThreshold, 279 | encodingCompletion: encodingCompletion 280 | ) 281 | } 282 | 283 | /** 284 | Creates an upload request using the shared manager instance for the specified method and URL string. 285 | 286 | - parameter URLRequest: The URL request. 287 | - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. 288 | - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. 289 | `MultipartFormDataEncodingMemoryThreshold` by default. 290 | - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. 291 | */ 292 | public func upload( 293 | URLRequest: URLRequestConvertible, 294 | multipartFormData: MultipartFormData -> Void, 295 | encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold, 296 | encodingCompletion: (Manager.MultipartFormDataEncodingResult -> Void)?) 297 | { 298 | return Manager.sharedInstance.upload( 299 | URLRequest, 300 | multipartFormData: multipartFormData, 301 | encodingMemoryThreshold: encodingMemoryThreshold, 302 | encodingCompletion: encodingCompletion 303 | ) 304 | } 305 | 306 | // MARK: - Download Methods 307 | 308 | // MARK: URL Request 309 | 310 | /** 311 | Creates a download request using the shared manager instance for the specified method and URL string. 312 | 313 | - parameter method: The HTTP method. 314 | - parameter URLString: The URL string. 315 | - parameter parameters: The parameters. `nil` by default. 316 | - parameter encoding: The parameter encoding. `.URL` by default. 317 | - parameter headers: The HTTP headers. `nil` by default. 318 | - parameter destination: The closure used to determine the destination of the downloaded file. 319 | 320 | - returns: The created download request. 321 | */ 322 | public func download( 323 | method: Method, 324 | _ URLString: URLStringConvertible, 325 | parameters: [String: AnyObject]? = nil, 326 | encoding: ParameterEncoding = .URL, 327 | headers: [String: String]? = nil, 328 | destination: Request.DownloadFileDestination) 329 | -> Request 330 | { 331 | return Manager.sharedInstance.download( 332 | method, 333 | URLString, 334 | parameters: parameters, 335 | encoding: encoding, 336 | headers: headers, 337 | destination: destination 338 | ) 339 | } 340 | 341 | /** 342 | Creates a download request using the shared manager instance for the specified URL request. 343 | 344 | - parameter URLRequest: The URL request. 345 | - parameter destination: The closure used to determine the destination of the downloaded file. 346 | 347 | - returns: The created download request. 348 | */ 349 | public func download(URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request { 350 | return Manager.sharedInstance.download(URLRequest, destination: destination) 351 | } 352 | 353 | // MARK: Resume Data 354 | 355 | /** 356 | Creates a request using the shared manager instance for downloading from the resume data produced from a 357 | previous request cancellation. 358 | 359 | - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` 360 | when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional 361 | information. 362 | - parameter destination: The closure used to determine the destination of the downloaded file. 363 | 364 | - returns: The created download request. 365 | */ 366 | public func download(resumeData data: NSData, destination: Request.DownloadFileDestination) -> Request { 367 | return Manager.sharedInstance.download(data, destination: destination) 368 | } 369 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Download.swift: -------------------------------------------------------------------------------- 1 | // Download.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | extension Manager { 26 | private enum Downloadable { 27 | case Request(NSURLRequest) 28 | case ResumeData(NSData) 29 | } 30 | 31 | private func download(downloadable: Downloadable, destination: Request.DownloadFileDestination) -> Request { 32 | var downloadTask: NSURLSessionDownloadTask! 33 | 34 | switch downloadable { 35 | case .Request(let request): 36 | dispatch_sync(queue) { 37 | downloadTask = self.session.downloadTaskWithRequest(request) 38 | } 39 | case .ResumeData(let resumeData): 40 | dispatch_sync(queue) { 41 | downloadTask = self.session.downloadTaskWithResumeData(resumeData) 42 | } 43 | } 44 | 45 | let request = Request(session: session, task: downloadTask) 46 | 47 | if let downloadDelegate = request.delegate as? Request.DownloadTaskDelegate { 48 | downloadDelegate.downloadTaskDidFinishDownloadingToURL = { session, downloadTask, URL in 49 | return destination(URL, downloadTask.response as! NSHTTPURLResponse) 50 | } 51 | } 52 | 53 | delegate[request.delegate.task] = request.delegate 54 | 55 | if startRequestsImmediately { 56 | request.resume() 57 | } 58 | 59 | return request 60 | } 61 | 62 | // MARK: Request 63 | 64 | /** 65 | Creates a download request for the specified method, URL string, parameters, parameter encoding, headers 66 | and destination. 67 | 68 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 69 | 70 | - parameter method: The HTTP method. 71 | - parameter URLString: The URL string. 72 | - parameter parameters: The parameters. `nil` by default. 73 | - parameter encoding: The parameter encoding. `.URL` by default. 74 | - parameter headers: The HTTP headers. `nil` by default. 75 | - parameter destination: The closure used to determine the destination of the downloaded file. 76 | 77 | - returns: The created download request. 78 | */ 79 | public func download( 80 | method: Method, 81 | _ URLString: URLStringConvertible, 82 | parameters: [String: AnyObject]? = nil, 83 | encoding: ParameterEncoding = .URL, 84 | headers: [String: String]? = nil, 85 | destination: Request.DownloadFileDestination) 86 | -> Request 87 | { 88 | let mutableURLRequest = URLRequest(method, URLString, headers: headers) 89 | let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0 90 | 91 | return download(encodedURLRequest, destination: destination) 92 | } 93 | 94 | /** 95 | Creates a request for downloading from the specified URL request. 96 | 97 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 98 | 99 | - parameter URLRequest: The URL request 100 | - parameter destination: The closure used to determine the destination of the downloaded file. 101 | 102 | - returns: The created download request. 103 | */ 104 | public func download(URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request { 105 | return download(.Request(URLRequest.URLRequest), destination: destination) 106 | } 107 | 108 | // MARK: Resume Data 109 | 110 | /** 111 | Creates a request for downloading from the resume data produced from a previous request cancellation. 112 | 113 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 114 | 115 | - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` 116 | when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for 117 | additional information. 118 | - parameter destination: The closure used to determine the destination of the downloaded file. 119 | 120 | - returns: The created download request. 121 | */ 122 | public func download(resumeData: NSData, destination: Request.DownloadFileDestination) -> Request { 123 | return download(.ResumeData(resumeData), destination: destination) 124 | } 125 | } 126 | 127 | // MARK: - 128 | 129 | extension Request { 130 | /** 131 | A closure executed once a request has successfully completed in order to determine where to move the temporary 132 | file written to during the download process. The closure takes two arguments: the temporary file URL and the URL 133 | response, and returns a single argument: the file URL where the temporary file should be moved. 134 | */ 135 | public typealias DownloadFileDestination = (NSURL, NSHTTPURLResponse) -> NSURL 136 | 137 | /** 138 | Creates a download file destination closure which uses the default file manager to move the temporary file to a 139 | file URL in the first available directory with the specified search path directory and search path domain mask. 140 | 141 | - parameter directory: The search path directory. `.DocumentDirectory` by default. 142 | - parameter domain: The search path domain mask. `.UserDomainMask` by default. 143 | 144 | - returns: A download file destination closure. 145 | */ 146 | public class func suggestedDownloadDestination( 147 | directory directory: NSSearchPathDirectory = .DocumentDirectory, 148 | domain: NSSearchPathDomainMask = .UserDomainMask) 149 | -> DownloadFileDestination 150 | { 151 | return { temporaryURL, response -> NSURL in 152 | let directoryURLs = NSFileManager.defaultManager().URLsForDirectory(directory, inDomains: domain) 153 | 154 | if !directoryURLs.isEmpty { 155 | return directoryURLs[0].URLByAppendingPathComponent(response.suggestedFilename!) 156 | } 157 | 158 | return temporaryURL 159 | } 160 | } 161 | 162 | /// The resume data of the underlying download task if available after a failure. 163 | public var resumeData: NSData? { 164 | var data: NSData? 165 | 166 | if let delegate = delegate as? DownloadTaskDelegate { 167 | data = delegate.resumeData 168 | } 169 | 170 | return data 171 | } 172 | 173 | // MARK: - DownloadTaskDelegate 174 | 175 | class DownloadTaskDelegate: TaskDelegate, NSURLSessionDownloadDelegate { 176 | var downloadTask: NSURLSessionDownloadTask? { return task as? NSURLSessionDownloadTask } 177 | var downloadProgress: ((Int64, Int64, Int64) -> Void)? 178 | 179 | var resumeData: NSData? 180 | override var data: NSData? { return resumeData } 181 | 182 | // MARK: - NSURLSessionDownloadDelegate 183 | 184 | // MARK: Override Closures 185 | 186 | var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> NSURL)? 187 | var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)? 188 | var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)? 189 | 190 | // MARK: Delegate Methods 191 | 192 | func URLSession( 193 | session: NSURLSession, 194 | downloadTask: NSURLSessionDownloadTask, 195 | didFinishDownloadingToURL location: NSURL) 196 | { 197 | if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL { 198 | do { 199 | let destination = downloadTaskDidFinishDownloadingToURL(session, downloadTask, location) 200 | try NSFileManager.defaultManager().moveItemAtURL(location, toURL: destination) 201 | } catch { 202 | self.error = error as NSError 203 | } 204 | } 205 | } 206 | 207 | func URLSession( 208 | session: NSURLSession, 209 | downloadTask: NSURLSessionDownloadTask, 210 | didWriteData bytesWritten: Int64, 211 | totalBytesWritten: Int64, 212 | totalBytesExpectedToWrite: Int64) 213 | { 214 | if let downloadTaskDidWriteData = downloadTaskDidWriteData { 215 | downloadTaskDidWriteData( 216 | session, 217 | downloadTask, 218 | bytesWritten, 219 | totalBytesWritten, 220 | totalBytesExpectedToWrite 221 | ) 222 | } else { 223 | progress.totalUnitCount = totalBytesExpectedToWrite 224 | progress.completedUnitCount = totalBytesWritten 225 | 226 | downloadProgress?(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) 227 | } 228 | } 229 | 230 | func URLSession( 231 | session: NSURLSession, 232 | downloadTask: NSURLSessionDownloadTask, 233 | didResumeAtOffset fileOffset: Int64, 234 | expectedTotalBytes: Int64) 235 | { 236 | if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { 237 | downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) 238 | } else { 239 | progress.totalUnitCount = expectedTotalBytes 240 | progress.completedUnitCount = fileOffset 241 | } 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Error.swift: -------------------------------------------------------------------------------- 1 | // Error.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /// The `Error` struct provides a convenience for creating custom Alamofire NSErrors. 26 | public struct Error { 27 | /// The domain used for creating all Alamofire errors. 28 | public static let Domain = "com.alamofire.error" 29 | 30 | /// The custom error codes generated by Alamofire. 31 | public enum Code: Int { 32 | case InputStreamReadFailed = -6000 33 | case OutputStreamWriteFailed = -6001 34 | case ContentTypeValidationFailed = -6002 35 | case StatusCodeValidationFailed = -6003 36 | case DataSerializationFailed = -6004 37 | case StringSerializationFailed = -6005 38 | case JSONSerializationFailed = -6006 39 | case PropertyListSerializationFailed = -6007 40 | } 41 | 42 | /** 43 | Creates an `NSError` with the given error code and failure reason. 44 | 45 | - parameter code: The error code. 46 | - parameter failureReason: The failure reason. 47 | 48 | - returns: An `NSError` with the given error code and failure reason. 49 | */ 50 | public static func errorWithCode(code: Code, failureReason: String) -> NSError { 51 | return errorWithCode(code.rawValue, failureReason: failureReason) 52 | } 53 | 54 | /** 55 | Creates an `NSError` with the given error code and failure reason. 56 | 57 | - parameter code: The error code. 58 | - parameter failureReason: The failure reason. 59 | 60 | - returns: An `NSError` with the given error code and failure reason. 61 | */ 62 | public static func errorWithCode(code: Int, failureReason: String) -> NSError { 63 | let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason] 64 | return NSError(domain: Domain, code: code, userInfo: userInfo) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/MultipartFormData.swift: -------------------------------------------------------------------------------- 1 | // MultipartFormData.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | #if os(iOS) || os(watchOS) || os(tvOS) 26 | import MobileCoreServices 27 | #elseif os(OSX) 28 | import CoreServices 29 | #endif 30 | 31 | /** 32 | Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode 33 | multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead 34 | to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the 35 | data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for 36 | larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset. 37 | 38 | For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well 39 | and the w3 form documentation. 40 | 41 | - https://www.ietf.org/rfc/rfc2388.txt 42 | - https://www.ietf.org/rfc/rfc2045.txt 43 | - https://www.w3.org/TR/html401/interact/forms.html#h-17.13 44 | */ 45 | public 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(boundaryType boundaryType: BoundaryType, boundary: String) -> NSData { 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.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 75 | } 76 | } 77 | 78 | class BodyPart { 79 | let headers: [String: String] 80 | let bodyStream: NSInputStream 81 | let bodyContentLength: UInt64 82 | var hasInitialBoundary = false 83 | var hasFinalBoundary = false 84 | 85 | init(headers: [String: String], bodyStream: NSInputStream, 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 | public 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: NSError? 105 | private let streamBufferSize: Int 106 | 107 | // MARK: - Lifecycle 108 | 109 | /** 110 | Creates a multipart form data object. 111 | 112 | - returns: The multipart form data object. 113 | */ 114 | public init() { 115 | self.boundary = BoundaryGenerator.randomBoundary() 116 | self.bodyParts = [] 117 | 118 | /** 119 | * The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more 120 | * information, please refer to the following article: 121 | * - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html 122 | */ 123 | 124 | self.streamBufferSize = 1024 125 | } 126 | 127 | // MARK: - Body Parts 128 | 129 | /** 130 | Creates a body part from the data and appends it to the multipart form data object. 131 | 132 | The body part data will be encoded using the following format: 133 | 134 | - `Content-Disposition: form-data; name=#{name}` (HTTP Header) 135 | - Encoded data 136 | - Multipart form boundary 137 | 138 | - parameter data: The data to encode into the multipart form data. 139 | - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 140 | */ 141 | public func appendBodyPart(data data: NSData, name: String) { 142 | let headers = contentHeaders(name: name) 143 | let stream = NSInputStream(data: data) 144 | let length = UInt64(data.length) 145 | 146 | appendBodyPart(stream: stream, length: length, headers: headers) 147 | } 148 | 149 | /** 150 | Creates a body part from the data and appends it to the multipart form data object. 151 | 152 | The body part data will be encoded using the following format: 153 | 154 | - `Content-Disposition: form-data; name=#{name}` (HTTP Header) 155 | - `Content-Type: #{generated mimeType}` (HTTP Header) 156 | - Encoded data 157 | - Multipart form boundary 158 | 159 | - parameter data: The data to encode into the multipart form data. 160 | - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 161 | - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header. 162 | */ 163 | public func appendBodyPart(data data: NSData, name: String, mimeType: String) { 164 | let headers = contentHeaders(name: name, mimeType: mimeType) 165 | let stream = NSInputStream(data: data) 166 | let length = UInt64(data.length) 167 | 168 | appendBodyPart(stream: stream, length: length, headers: headers) 169 | } 170 | 171 | /** 172 | Creates a body part from the data and appends it to the multipart form data object. 173 | 174 | The body part data will be encoded using the following format: 175 | 176 | - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) 177 | - `Content-Type: #{mimeType}` (HTTP Header) 178 | - Encoded file data 179 | - Multipart form boundary 180 | 181 | - parameter data: The data to encode into the multipart form data. 182 | - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 183 | - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header. 184 | - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header. 185 | */ 186 | public func appendBodyPart(data data: NSData, name: String, fileName: String, mimeType: String) { 187 | let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) 188 | let stream = NSInputStream(data: data) 189 | let length = UInt64(data.length) 190 | 191 | appendBodyPart(stream: stream, length: length, headers: headers) 192 | } 193 | 194 | /** 195 | Creates a body part from the file and appends it to the multipart form data object. 196 | 197 | The body part data will be encoded using the following format: 198 | 199 | - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header) 200 | - `Content-Type: #{generated mimeType}` (HTTP Header) 201 | - Encoded file data 202 | - Multipart form boundary 203 | 204 | The filename in the `Content-Disposition` HTTP header is generated from the last path component of the 205 | `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the 206 | system associated MIME type. 207 | 208 | - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. 209 | - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. 210 | */ 211 | public func appendBodyPart(fileURL fileURL: NSURL, name: String) { 212 | if let 213 | fileName = fileURL.lastPathComponent, 214 | pathExtension = fileURL.pathExtension 215 | { 216 | let mimeType = mimeTypeForPathExtension(pathExtension) 217 | appendBodyPart(fileURL: fileURL, name: name, fileName: fileName, mimeType: mimeType) 218 | } else { 219 | let failureReason = "Failed to extract the fileName of the provided URL: \(fileURL)" 220 | setBodyPartError(Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason)) 221 | } 222 | } 223 | 224 | /** 225 | Creates a body part from the file and appends it to the multipart form data object. 226 | 227 | The body part data will be encoded using the following format: 228 | 229 | - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header) 230 | - Content-Type: #{mimeType} (HTTP Header) 231 | - Encoded file data 232 | - Multipart form boundary 233 | 234 | - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. 235 | - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. 236 | - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header. 237 | - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header. 238 | */ 239 | public func appendBodyPart(fileURL fileURL: NSURL, name: String, fileName: String, mimeType: String) { 240 | let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) 241 | 242 | //============================================================ 243 | // Check 1 - is file URL? 244 | //============================================================ 245 | 246 | guard fileURL.fileURL else { 247 | let failureReason = "The file URL does not point to a file URL: \(fileURL)" 248 | let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 249 | setBodyPartError(error) 250 | return 251 | } 252 | 253 | //============================================================ 254 | // Check 2 - is file URL reachable? 255 | //============================================================ 256 | 257 | var isReachable = true 258 | 259 | if #available(OSX 10.10, *) { 260 | isReachable = fileURL.checkPromisedItemIsReachableAndReturnError(nil) 261 | } 262 | 263 | guard isReachable else { 264 | let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: "The file URL is not reachable: \(fileURL)") 265 | setBodyPartError(error) 266 | return 267 | } 268 | 269 | //============================================================ 270 | // Check 3 - is file URL a directory? 271 | //============================================================ 272 | 273 | var isDirectory: ObjCBool = false 274 | 275 | guard let 276 | path = fileURL.path 277 | where NSFileManager.defaultManager().fileExistsAtPath(path, isDirectory: &isDirectory) && !isDirectory else 278 | { 279 | let failureReason = "The file URL is a directory, not a file: \(fileURL)" 280 | let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 281 | setBodyPartError(error) 282 | return 283 | } 284 | 285 | //============================================================ 286 | // Check 4 - can the file size be extracted? 287 | //============================================================ 288 | 289 | var bodyContentLength: UInt64? 290 | 291 | do { 292 | if let 293 | path = fileURL.path, 294 | fileSize = try NSFileManager.defaultManager().attributesOfItemAtPath(path)[NSFileSize] as? NSNumber 295 | { 296 | bodyContentLength = fileSize.unsignedLongLongValue 297 | } 298 | } catch { 299 | // No-op 300 | } 301 | 302 | guard let length = bodyContentLength else { 303 | let failureReason = "Could not fetch attributes from the file URL: \(fileURL)" 304 | let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 305 | setBodyPartError(error) 306 | return 307 | } 308 | 309 | //============================================================ 310 | // Check 5 - can a stream be created from file URL? 311 | //============================================================ 312 | 313 | guard let stream = NSInputStream(URL: fileURL) else { 314 | let failureReason = "Failed to create an input stream from the file URL: \(fileURL)" 315 | let error = Error.errorWithCode(NSURLErrorCannotOpenFile, failureReason: failureReason) 316 | setBodyPartError(error) 317 | return 318 | } 319 | 320 | appendBodyPart(stream: stream, length: length, headers: headers) 321 | } 322 | 323 | /** 324 | Creates a body part from the stream and appends it to the multipart form data object. 325 | 326 | The body part data will be encoded using the following format: 327 | 328 | - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) 329 | - `Content-Type: #{mimeType}` (HTTP Header) 330 | - Encoded stream data 331 | - Multipart form boundary 332 | 333 | - parameter stream: The input stream to encode in the multipart form data. 334 | - parameter length: The content length of the stream. 335 | - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header. 336 | - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header. 337 | - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header. 338 | */ 339 | public func appendBodyPart( 340 | stream stream: NSInputStream, 341 | length: UInt64, 342 | name: String, 343 | fileName: String, 344 | mimeType: String) 345 | { 346 | let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) 347 | appendBodyPart(stream: stream, length: length, headers: headers) 348 | } 349 | 350 | /** 351 | Creates a body part with the headers, stream and length and appends it to the multipart form data object. 352 | 353 | The body part data will be encoded using the following format: 354 | 355 | - HTTP headers 356 | - Encoded stream data 357 | - Multipart form boundary 358 | 359 | - parameter stream: The input stream to encode in the multipart form data. 360 | - parameter length: The content length of the stream. 361 | - parameter headers: The HTTP headers for the body part. 362 | */ 363 | public func appendBodyPart(stream stream: NSInputStream, length: UInt64, headers: [String: String]) { 364 | let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length) 365 | bodyParts.append(bodyPart) 366 | } 367 | 368 | // MARK: - Data Encoding 369 | 370 | /** 371 | Encodes all the appended body parts into a single `NSData` object. 372 | 373 | It is important to note that this method will load all the appended body parts into memory all at the same 374 | time. This method should only be used when the encoded data will have a small memory footprint. For large data 375 | cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method. 376 | 377 | - throws: An `NSError` if encoding encounters an error. 378 | 379 | - returns: The encoded `NSData` if encoding is successful. 380 | */ 381 | public func encode() throws -> NSData { 382 | if let bodyPartError = bodyPartError { 383 | throw bodyPartError 384 | } 385 | 386 | let encoded = NSMutableData() 387 | 388 | bodyParts.first?.hasInitialBoundary = true 389 | bodyParts.last?.hasFinalBoundary = true 390 | 391 | for bodyPart in bodyParts { 392 | let encodedData = try encodeBodyPart(bodyPart) 393 | encoded.appendData(encodedData) 394 | } 395 | 396 | return encoded 397 | } 398 | 399 | /** 400 | Writes the appended body parts into the given file URL. 401 | 402 | This process is facilitated by reading and writing with input and output streams, respectively. Thus, 403 | this approach is very memory efficient and should be used for large body part data. 404 | 405 | - parameter fileURL: The file URL to write the multipart form data into. 406 | 407 | - throws: An `NSError` if encoding encounters an error. 408 | */ 409 | public func writeEncodedDataToDisk(fileURL: NSURL) throws { 410 | if let bodyPartError = bodyPartError { 411 | throw bodyPartError 412 | } 413 | 414 | if let path = fileURL.path where NSFileManager.defaultManager().fileExistsAtPath(path) { 415 | let failureReason = "A file already exists at the given file URL: \(fileURL)" 416 | throw Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 417 | } else if !fileURL.fileURL { 418 | let failureReason = "The URL does not point to a valid file: \(fileURL)" 419 | throw Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 420 | } 421 | 422 | let outputStream: NSOutputStream 423 | 424 | if let possibleOutputStream = NSOutputStream(URL: fileURL, append: false) { 425 | outputStream = possibleOutputStream 426 | } else { 427 | let failureReason = "Failed to create an output stream with the given URL: \(fileURL)" 428 | throw Error.errorWithCode(NSURLErrorCannotOpenFile, failureReason: failureReason) 429 | } 430 | 431 | outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 432 | outputStream.open() 433 | 434 | self.bodyParts.first?.hasInitialBoundary = true 435 | self.bodyParts.last?.hasFinalBoundary = true 436 | 437 | for bodyPart in self.bodyParts { 438 | try writeBodyPart(bodyPart, toOutputStream: outputStream) 439 | } 440 | 441 | outputStream.close() 442 | outputStream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 443 | } 444 | 445 | // MARK: - Private - Body Part Encoding 446 | 447 | private func encodeBodyPart(bodyPart: BodyPart) throws -> NSData { 448 | let encoded = NSMutableData() 449 | 450 | let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() 451 | encoded.appendData(initialData) 452 | 453 | let headerData = encodeHeaderDataForBodyPart(bodyPart) 454 | encoded.appendData(headerData) 455 | 456 | let bodyStreamData = try encodeBodyStreamDataForBodyPart(bodyPart) 457 | encoded.appendData(bodyStreamData) 458 | 459 | if bodyPart.hasFinalBoundary { 460 | encoded.appendData(finalBoundaryData()) 461 | } 462 | 463 | return encoded 464 | } 465 | 466 | private func encodeHeaderDataForBodyPart(bodyPart: BodyPart) -> NSData { 467 | var headerText = "" 468 | 469 | for (key, value) in bodyPart.headers { 470 | headerText += "\(key): \(value)\(EncodingCharacters.CRLF)" 471 | } 472 | headerText += EncodingCharacters.CRLF 473 | 474 | return headerText.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 475 | } 476 | 477 | private func encodeBodyStreamDataForBodyPart(bodyPart: BodyPart) throws -> NSData { 478 | let inputStream = bodyPart.bodyStream 479 | inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 480 | inputStream.open() 481 | 482 | var error: NSError? 483 | let encoded = NSMutableData() 484 | 485 | while inputStream.hasBytesAvailable { 486 | var buffer = [UInt8](count: streamBufferSize, repeatedValue: 0) 487 | let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) 488 | 489 | if inputStream.streamError != nil { 490 | error = inputStream.streamError 491 | break 492 | } 493 | 494 | if bytesRead > 0 { 495 | encoded.appendBytes(buffer, length: bytesRead) 496 | } else if bytesRead < 0 { 497 | let failureReason = "Failed to read from input stream: \(inputStream)" 498 | error = Error.errorWithCode(.InputStreamReadFailed, failureReason: failureReason) 499 | break 500 | } else { 501 | break 502 | } 503 | } 504 | 505 | inputStream.close() 506 | inputStream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 507 | 508 | if let error = error { 509 | throw error 510 | } 511 | 512 | return encoded 513 | } 514 | 515 | // MARK: - Private - Writing Body Part to Output Stream 516 | 517 | private func writeBodyPart(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws { 518 | try writeInitialBoundaryDataForBodyPart(bodyPart, toOutputStream: outputStream) 519 | try writeHeaderDataForBodyPart(bodyPart, toOutputStream: outputStream) 520 | try writeBodyStreamForBodyPart(bodyPart, toOutputStream: outputStream) 521 | try writeFinalBoundaryDataForBodyPart(bodyPart, toOutputStream: outputStream) 522 | } 523 | 524 | private func writeInitialBoundaryDataForBodyPart( 525 | bodyPart: BodyPart, 526 | toOutputStream outputStream: NSOutputStream) 527 | throws 528 | { 529 | let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() 530 | return try writeData(initialData, toOutputStream: outputStream) 531 | } 532 | 533 | private func writeHeaderDataForBodyPart(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws { 534 | let headerData = encodeHeaderDataForBodyPart(bodyPart) 535 | return try writeData(headerData, toOutputStream: outputStream) 536 | } 537 | 538 | private func writeBodyStreamForBodyPart(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws { 539 | let inputStream = bodyPart.bodyStream 540 | inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 541 | inputStream.open() 542 | 543 | while inputStream.hasBytesAvailable { 544 | var buffer = [UInt8](count: streamBufferSize, repeatedValue: 0) 545 | let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) 546 | 547 | if let streamError = inputStream.streamError { 548 | throw streamError 549 | } 550 | 551 | if bytesRead > 0 { 552 | if buffer.count != bytesRead { 553 | buffer = Array(buffer[0.. 0 { 592 | if outputStream.hasSpaceAvailable { 593 | let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite) 594 | 595 | if let streamError = outputStream.streamError { 596 | throw streamError 597 | } 598 | 599 | if bytesWritten < 0 { 600 | let failureReason = "Failed to write to output stream: \(outputStream)" 601 | throw Error.errorWithCode(.OutputStreamWriteFailed, failureReason: failureReason) 602 | } 603 | 604 | bytesToWrite -= bytesWritten 605 | 606 | if bytesToWrite > 0 { 607 | buffer = Array(buffer[bytesWritten.. String { 618 | if let 619 | id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, nil)?.takeRetainedValue(), 620 | contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() 621 | { 622 | return contentType as String 623 | } 624 | 625 | return "application/octet-stream" 626 | } 627 | 628 | // MARK: - Private - Content Headers 629 | 630 | private func contentHeaders(name name: String) -> [String: String] { 631 | return ["Content-Disposition": "form-data; name=\"\(name)\""] 632 | } 633 | 634 | private func contentHeaders(name name: String, mimeType: String) -> [String: String] { 635 | return [ 636 | "Content-Disposition": "form-data; name=\"\(name)\"", 637 | "Content-Type": "\(mimeType)" 638 | ] 639 | } 640 | 641 | private func contentHeaders(name name: String, fileName: String, mimeType: String) -> [String: String] { 642 | return [ 643 | "Content-Disposition": "form-data; name=\"\(name)\"; filename=\"\(fileName)\"", 644 | "Content-Type": "\(mimeType)" 645 | ] 646 | } 647 | 648 | // MARK: - Private - Boundary Encoding 649 | 650 | private func initialBoundaryData() -> NSData { 651 | return BoundaryGenerator.boundaryData(boundaryType: .Initial, boundary: boundary) 652 | } 653 | 654 | private func encapsulatedBoundaryData() -> NSData { 655 | return BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundary: boundary) 656 | } 657 | 658 | private func finalBoundaryData() -> NSData { 659 | return BoundaryGenerator.boundaryData(boundaryType: .Final, boundary: boundary) 660 | } 661 | 662 | // MARK: - Private - Errors 663 | 664 | private func setBodyPartError(error: NSError) { 665 | if bodyPartError == nil { 666 | bodyPartError = error 667 | } 668 | } 669 | } 670 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ParameterEncoding.swift: -------------------------------------------------------------------------------- 1 | // ParameterEncoding.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | HTTP method definitions. 27 | 28 | See https://tools.ietf.org/html/rfc7231#section-4.3 29 | */ 30 | public enum Method: String { 31 | case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT 32 | } 33 | 34 | // MARK: ParameterEncoding 35 | 36 | /** 37 | Used to specify the way in which a set of parameters are applied to a URL request. 38 | 39 | - `URL`: Creates a query string to be set as or appended to any existing URL query for `GET`, `HEAD`, 40 | and `DELETE` requests, or set as the body for requests with any other HTTP method. The 41 | `Content-Type` HTTP header field of an encoded request with HTTP body is set to 42 | `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification 43 | for how to encode collection types, the convention of appending `[]` to the key for array 44 | values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested 45 | dictionary values (`foo[bar]=baz`). 46 | 47 | - `URLEncodedInURL`: Creates query string to be set as or appended to any existing URL query. Uses the same 48 | implementation as the `.URL` case, but always applies the encoded result to the URL. 49 | 50 | - `JSON`: Uses `NSJSONSerialization` to create a JSON representation of the parameters object, which is 51 | set as the body of the request. The `Content-Type` HTTP header field of an encoded request is 52 | set to `application/json`. 53 | 54 | - `PropertyList`: Uses `NSPropertyListSerialization` to create a plist representation of the parameters object, 55 | according to the associated format and write options values, which is set as the body of the 56 | request. The `Content-Type` HTTP header field of an encoded request is set to 57 | `application/x-plist`. 58 | 59 | - `Custom`: Uses the associated closure value to construct a new request given an existing request and 60 | parameters. 61 | */ 62 | public enum ParameterEncoding { 63 | case URL 64 | case URLEncodedInURL 65 | case JSON 66 | case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions) 67 | case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?)) 68 | 69 | /** 70 | Creates a URL request by encoding parameters and applying them onto an existing request. 71 | 72 | - parameter URLRequest: The request to have parameters applied 73 | - parameter parameters: The parameters to apply 74 | 75 | - returns: A tuple containing the constructed request and the error that occurred during parameter encoding, 76 | if any. 77 | */ 78 | public func encode( 79 | URLRequest: URLRequestConvertible, 80 | parameters: [String: AnyObject]?) 81 | -> (NSMutableURLRequest, NSError?) 82 | { 83 | var mutableURLRequest = URLRequest.URLRequest 84 | 85 | guard let parameters = parameters where !parameters.isEmpty else { 86 | return (mutableURLRequest, nil) 87 | } 88 | 89 | var encodingError: NSError? = nil 90 | 91 | switch self { 92 | case .URL, .URLEncodedInURL: 93 | func query(parameters: [String: AnyObject]) -> String { 94 | var components: [(String, String)] = [] 95 | 96 | for key in parameters.keys.sort(<) { 97 | let value = parameters[key]! 98 | components += queryComponents(key, value) 99 | } 100 | 101 | return (components.map { "\($0)=\($1)" } as [String]).joinWithSeparator("&") 102 | } 103 | 104 | func encodesParametersInURL(method: Method) -> Bool { 105 | switch self { 106 | case .URLEncodedInURL: 107 | return true 108 | default: 109 | break 110 | } 111 | 112 | switch method { 113 | case .GET, .HEAD, .DELETE: 114 | return true 115 | default: 116 | return false 117 | } 118 | } 119 | 120 | if let method = Method(rawValue: mutableURLRequest.HTTPMethod) where encodesParametersInURL(method) { 121 | if let URLComponents = NSURLComponents(URL: mutableURLRequest.URL!, resolvingAgainstBaseURL: false) { 122 | let percentEncodedQuery = (URLComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) 123 | URLComponents.percentEncodedQuery = percentEncodedQuery 124 | mutableURLRequest.URL = URLComponents.URL 125 | } 126 | } else { 127 | if mutableURLRequest.valueForHTTPHeaderField("Content-Type") == nil { 128 | mutableURLRequest.setValue( 129 | "application/x-www-form-urlencoded; charset=utf-8", 130 | forHTTPHeaderField: "Content-Type" 131 | ) 132 | } 133 | 134 | mutableURLRequest.HTTPBody = query(parameters).dataUsingEncoding( 135 | NSUTF8StringEncoding, 136 | allowLossyConversion: false 137 | ) 138 | } 139 | case .JSON: 140 | do { 141 | let options = NSJSONWritingOptions() 142 | let data = try NSJSONSerialization.dataWithJSONObject(parameters, options: options) 143 | 144 | mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 145 | mutableURLRequest.HTTPBody = data 146 | } catch { 147 | encodingError = error as NSError 148 | } 149 | case .PropertyList(let format, let options): 150 | do { 151 | let data = try NSPropertyListSerialization.dataWithPropertyList( 152 | parameters, 153 | format: format, 154 | options: options 155 | ) 156 | mutableURLRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") 157 | mutableURLRequest.HTTPBody = data 158 | } catch { 159 | encodingError = error as NSError 160 | } 161 | case .Custom(let closure): 162 | (mutableURLRequest, encodingError) = closure(mutableURLRequest, parameters) 163 | } 164 | 165 | return (mutableURLRequest, encodingError) 166 | } 167 | 168 | /** 169 | Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. 170 | 171 | - parameter key: The key of the query component. 172 | - parameter value: The value of the query component. 173 | 174 | - returns: The percent-escaped, URL encoded query string components. 175 | */ 176 | public func queryComponents(key: String, _ value: AnyObject) -> [(String, String)] { 177 | var components: [(String, String)] = [] 178 | 179 | if let dictionary = value as? [String: AnyObject] { 180 | for (nestedKey, value) in dictionary { 181 | components += queryComponents("\(key)[\(nestedKey)]", value) 182 | } 183 | } else if let array = value as? [AnyObject] { 184 | for value in array { 185 | components += queryComponents("\(key)[]", value) 186 | } 187 | } else { 188 | components.append((escape(key), escape("\(value)"))) 189 | } 190 | 191 | return components 192 | } 193 | 194 | /** 195 | Returns a percent-escaped string following RFC 3986 for a query string key or value. 196 | 197 | RFC 3986 states that the following characters are "reserved" characters. 198 | 199 | - General Delimiters: ":", "#", "[", "]", "@", "?", "/" 200 | - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" 201 | 202 | In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow 203 | query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" 204 | should be percent-escaped in the query string. 205 | 206 | - parameter string: The string to be percent-escaped. 207 | 208 | - returns: The percent-escaped string. 209 | */ 210 | public func escape(string: String) -> String { 211 | let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 212 | let subDelimitersToEncode = "!$&'()*+,;=" 213 | 214 | let allowedCharacterSet = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet 215 | allowedCharacterSet.removeCharactersInString(generalDelimitersToEncode + subDelimitersToEncode) 216 | 217 | var escaped = "" 218 | 219 | //========================================================================================================== 220 | // 221 | // Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few 222 | // hundred Chinense characters causes various malloc error crashes. To avoid this issue until iOS 8 is no 223 | // longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more 224 | // info, please refer to: 225 | // 226 | // - https://github.com/Alamofire/Alamofire/issues/206 227 | // 228 | //========================================================================================================== 229 | 230 | if #available(iOS 8.3, OSX 10.10, *) { 231 | escaped = string.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) ?? string 232 | } else { 233 | let batchSize = 50 234 | var index = string.startIndex 235 | 236 | while index != string.endIndex { 237 | let startIndex = index 238 | let endIndex = index.advancedBy(batchSize, limit: string.endIndex) 239 | let range = Range(start: startIndex, end: endIndex) 240 | 241 | let substring = string.substringWithRange(range) 242 | 243 | escaped += substring.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) ?? substring 244 | 245 | index = endIndex 246 | } 247 | } 248 | 249 | return escaped 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Request.swift: -------------------------------------------------------------------------------- 1 | // Request.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | Responsible for sending a request and receiving the response and associated data from the server, as well as 27 | managing its underlying `NSURLSessionTask`. 28 | */ 29 | public class Request { 30 | 31 | // MARK: - Properties 32 | 33 | /// The delegate for the underlying task. 34 | public let delegate: TaskDelegate 35 | 36 | /// The underlying task. 37 | public var task: NSURLSessionTask { return delegate.task } 38 | 39 | /// The session belonging to the underlying task. 40 | public let session: NSURLSession 41 | 42 | /// The request sent or to be sent to the server. 43 | public var request: NSURLRequest? { return task.originalRequest } 44 | 45 | /// The response received from the server, if any. 46 | public var response: NSHTTPURLResponse? { return task.response as? NSHTTPURLResponse } 47 | 48 | /// The progress of the request lifecycle. 49 | public var progress: NSProgress { return delegate.progress } 50 | 51 | // MARK: - Lifecycle 52 | 53 | init(session: NSURLSession, task: NSURLSessionTask) { 54 | self.session = session 55 | 56 | switch task { 57 | case is NSURLSessionUploadTask: 58 | self.delegate = UploadTaskDelegate(task: task) 59 | case is NSURLSessionDataTask: 60 | self.delegate = DataTaskDelegate(task: task) 61 | case is NSURLSessionDownloadTask: 62 | self.delegate = DownloadTaskDelegate(task: task) 63 | default: 64 | self.delegate = TaskDelegate(task: task) 65 | } 66 | } 67 | 68 | // MARK: - Authentication 69 | 70 | /** 71 | Associates an HTTP Basic credential with the request. 72 | 73 | - parameter user: The user. 74 | - parameter password: The password. 75 | - parameter persistence: The URL credential persistence. `.ForSession` by default. 76 | 77 | - returns: The request. 78 | */ 79 | public func authenticate( 80 | user user: String, 81 | password: String, 82 | persistence: NSURLCredentialPersistence = .ForSession) 83 | -> Self 84 | { 85 | let credential = NSURLCredential(user: user, password: password, persistence: persistence) 86 | 87 | return authenticate(usingCredential: credential) 88 | } 89 | 90 | /** 91 | Associates a specified credential with the request. 92 | 93 | - parameter credential: The credential. 94 | 95 | - returns: The request. 96 | */ 97 | public func authenticate(usingCredential credential: NSURLCredential) -> Self { 98 | delegate.credential = credential 99 | 100 | return self 101 | } 102 | 103 | // MARK: - Progress 104 | 105 | /** 106 | Sets a closure to be called periodically during the lifecycle of the request as data is written to or read 107 | from the server. 108 | 109 | - For uploads, the progress closure returns the bytes written, total bytes written, and total bytes expected 110 | to write. 111 | - For downloads and data tasks, the progress closure returns the bytes read, total bytes read, and total bytes 112 | expected to read. 113 | 114 | - parameter closure: The code to be executed periodically during the lifecycle of the request. 115 | 116 | - returns: The request. 117 | */ 118 | public func progress(closure: ((Int64, Int64, Int64) -> Void)? = nil) -> Self { 119 | if let uploadDelegate = delegate as? UploadTaskDelegate { 120 | uploadDelegate.uploadProgress = closure 121 | } else if let dataDelegate = delegate as? DataTaskDelegate { 122 | dataDelegate.dataProgress = closure 123 | } else if let downloadDelegate = delegate as? DownloadTaskDelegate { 124 | downloadDelegate.downloadProgress = closure 125 | } 126 | 127 | return self 128 | } 129 | 130 | /** 131 | Sets a closure to be called periodically during the lifecycle of the request as data is read from the server. 132 | 133 | This closure returns the bytes most recently received from the server, not including data from previous calls. 134 | If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is 135 | also important to note that the `response` closure will be called with nil `responseData`. 136 | 137 | - parameter closure: The code to be executed periodically during the lifecycle of the request. 138 | 139 | - returns: The request. 140 | */ 141 | public func stream(closure: (NSData -> Void)? = nil) -> Self { 142 | if let dataDelegate = delegate as? DataTaskDelegate { 143 | dataDelegate.dataStream = closure 144 | } 145 | 146 | return self 147 | } 148 | 149 | // MARK: - State 150 | 151 | /** 152 | Suspends the request. 153 | */ 154 | public func suspend() { 155 | task.suspend() 156 | } 157 | 158 | /** 159 | Resumes the request. 160 | */ 161 | public func resume() { 162 | task.resume() 163 | } 164 | 165 | /** 166 | Cancels the request. 167 | */ 168 | public func cancel() { 169 | if let 170 | downloadDelegate = delegate as? DownloadTaskDelegate, 171 | downloadTask = downloadDelegate.downloadTask 172 | { 173 | downloadTask.cancelByProducingResumeData { data in 174 | downloadDelegate.resumeData = data 175 | } 176 | } else { 177 | task.cancel() 178 | } 179 | } 180 | 181 | // MARK: - TaskDelegate 182 | 183 | /** 184 | The task delegate is responsible for handling all delegate callbacks for the underlying task as well as 185 | executing all operations attached to the serial operation queue upon task completion. 186 | */ 187 | public class TaskDelegate: NSObject { 188 | 189 | /// The serial operation queue used to execute all operations after the task completes. 190 | public let queue: NSOperationQueue 191 | 192 | let task: NSURLSessionTask 193 | let progress: NSProgress 194 | 195 | var data: NSData? { return nil } 196 | var error: NSError? 197 | 198 | var credential: NSURLCredential? 199 | 200 | init(task: NSURLSessionTask) { 201 | self.task = task 202 | self.progress = NSProgress(totalUnitCount: 0) 203 | self.queue = { 204 | let operationQueue = NSOperationQueue() 205 | operationQueue.maxConcurrentOperationCount = 1 206 | operationQueue.suspended = true 207 | 208 | if #available(OSX 10.10, *) { 209 | operationQueue.qualityOfService = NSQualityOfService.Utility 210 | } 211 | 212 | return operationQueue 213 | }() 214 | } 215 | 216 | deinit { 217 | queue.cancelAllOperations() 218 | queue.suspended = false 219 | } 220 | 221 | // MARK: - NSURLSessionTaskDelegate 222 | 223 | // MARK: Override Closures 224 | 225 | var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest) -> NSURLRequest?)? 226 | var taskDidReceiveChallenge: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))? 227 | var taskNeedNewBodyStream: ((NSURLSession, NSURLSessionTask) -> NSInputStream?)? 228 | var taskDidCompleteWithError: ((NSURLSession, NSURLSessionTask, NSError?) -> Void)? 229 | 230 | // MARK: Delegate Methods 231 | 232 | func URLSession( 233 | session: NSURLSession, 234 | task: NSURLSessionTask, 235 | willPerformHTTPRedirection response: NSHTTPURLResponse, 236 | newRequest request: NSURLRequest, 237 | completionHandler: ((NSURLRequest?) -> Void)) 238 | { 239 | var redirectRequest: NSURLRequest? = request 240 | 241 | if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { 242 | redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) 243 | } 244 | 245 | completionHandler(redirectRequest) 246 | } 247 | 248 | func URLSession( 249 | session: NSURLSession, 250 | task: NSURLSessionTask, 251 | didReceiveChallenge challenge: NSURLAuthenticationChallenge, 252 | completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)) 253 | { 254 | var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling 255 | var credential: NSURLCredential? 256 | 257 | if let taskDidReceiveChallenge = taskDidReceiveChallenge { 258 | (disposition, credential) = taskDidReceiveChallenge(session, task, challenge) 259 | } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 260 | let host = challenge.protectionSpace.host 261 | 262 | if let 263 | serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicyForHost(host), 264 | serverTrust = challenge.protectionSpace.serverTrust 265 | { 266 | if serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) { 267 | disposition = .UseCredential 268 | credential = NSURLCredential(forTrust: serverTrust) 269 | } else { 270 | disposition = .CancelAuthenticationChallenge 271 | } 272 | } 273 | } else { 274 | if challenge.previousFailureCount > 0 { 275 | disposition = .CancelAuthenticationChallenge 276 | } else { 277 | credential = self.credential ?? session.configuration.URLCredentialStorage?.defaultCredentialForProtectionSpace(challenge.protectionSpace) 278 | 279 | if credential != nil { 280 | disposition = .UseCredential 281 | } 282 | } 283 | } 284 | 285 | completionHandler(disposition, credential) 286 | } 287 | 288 | func URLSession( 289 | session: NSURLSession, 290 | task: NSURLSessionTask, 291 | needNewBodyStream completionHandler: ((NSInputStream?) -> Void)) 292 | { 293 | var bodyStream: NSInputStream? 294 | 295 | if let taskNeedNewBodyStream = taskNeedNewBodyStream { 296 | bodyStream = taskNeedNewBodyStream(session, task) 297 | } 298 | 299 | completionHandler(bodyStream) 300 | } 301 | 302 | func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) { 303 | if let taskDidCompleteWithError = taskDidCompleteWithError { 304 | taskDidCompleteWithError(session, task, error) 305 | } else { 306 | if let error = error { 307 | self.error = error 308 | 309 | if let 310 | downloadDelegate = self as? DownloadTaskDelegate, 311 | userInfo = error.userInfo as? [String: AnyObject], 312 | resumeData = userInfo[NSURLSessionDownloadTaskResumeData] as? NSData 313 | { 314 | downloadDelegate.resumeData = resumeData 315 | } 316 | } 317 | 318 | queue.suspended = false 319 | } 320 | } 321 | } 322 | 323 | // MARK: - DataTaskDelegate 324 | 325 | class DataTaskDelegate: TaskDelegate, NSURLSessionDataDelegate { 326 | var dataTask: NSURLSessionDataTask? { return task as? NSURLSessionDataTask } 327 | 328 | private var totalBytesReceived: Int64 = 0 329 | private var mutableData: NSMutableData 330 | override var data: NSData? { 331 | if dataStream != nil { 332 | return nil 333 | } else { 334 | return mutableData 335 | } 336 | } 337 | 338 | private var expectedContentLength: Int64? 339 | private var dataProgress: ((bytesReceived: Int64, totalBytesReceived: Int64, totalBytesExpectedToReceive: Int64) -> Void)? 340 | private var dataStream: ((data: NSData) -> Void)? 341 | 342 | override init(task: NSURLSessionTask) { 343 | mutableData = NSMutableData() 344 | super.init(task: task) 345 | } 346 | 347 | // MARK: - NSURLSessionDataDelegate 348 | 349 | // MARK: Override Closures 350 | 351 | var dataTaskDidReceiveResponse: ((NSURLSession, NSURLSessionDataTask, NSURLResponse) -> NSURLSessionResponseDisposition)? 352 | var dataTaskDidBecomeDownloadTask: ((NSURLSession, NSURLSessionDataTask, NSURLSessionDownloadTask) -> Void)? 353 | var dataTaskDidReceiveData: ((NSURLSession, NSURLSessionDataTask, NSData) -> Void)? 354 | var dataTaskWillCacheResponse: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse) -> NSCachedURLResponse?)? 355 | 356 | // MARK: Delegate Methods 357 | 358 | func URLSession( 359 | session: NSURLSession, 360 | dataTask: NSURLSessionDataTask, 361 | didReceiveResponse response: NSURLResponse, 362 | completionHandler: (NSURLSessionResponseDisposition -> Void)) 363 | { 364 | var disposition: NSURLSessionResponseDisposition = .Allow 365 | 366 | expectedContentLength = response.expectedContentLength 367 | 368 | if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { 369 | disposition = dataTaskDidReceiveResponse(session, dataTask, response) 370 | } 371 | 372 | completionHandler(disposition) 373 | } 374 | 375 | func URLSession( 376 | session: NSURLSession, 377 | dataTask: NSURLSessionDataTask, 378 | didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask) 379 | { 380 | dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask) 381 | } 382 | 383 | func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) { 384 | if let dataTaskDidReceiveData = dataTaskDidReceiveData { 385 | dataTaskDidReceiveData(session, dataTask, data) 386 | } else { 387 | if let dataStream = dataStream { 388 | dataStream(data: data) 389 | } else { 390 | mutableData.appendData(data) 391 | } 392 | 393 | totalBytesReceived += data.length 394 | let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown 395 | 396 | progress.totalUnitCount = totalBytesExpected 397 | progress.completedUnitCount = totalBytesReceived 398 | 399 | dataProgress?( 400 | bytesReceived: Int64(data.length), 401 | totalBytesReceived: totalBytesReceived, 402 | totalBytesExpectedToReceive: totalBytesExpected 403 | ) 404 | } 405 | } 406 | 407 | func URLSession( 408 | session: NSURLSession, 409 | dataTask: NSURLSessionDataTask, 410 | willCacheResponse proposedResponse: NSCachedURLResponse, 411 | completionHandler: ((NSCachedURLResponse?) -> Void)) 412 | { 413 | var cachedResponse: NSCachedURLResponse? = proposedResponse 414 | 415 | if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { 416 | cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse) 417 | } 418 | 419 | completionHandler(cachedResponse) 420 | } 421 | } 422 | } 423 | 424 | // MARK: - CustomStringConvertible 425 | 426 | extension Request: CustomStringConvertible { 427 | 428 | /** 429 | The textual representation used when written to an output stream, which includes the HTTP method and URL, as 430 | well as the response status code if a response has been received. 431 | */ 432 | public var description: String { 433 | var components: [String] = [] 434 | 435 | if let HTTPMethod = request?.HTTPMethod { 436 | components.append(HTTPMethod) 437 | } 438 | 439 | if let URLString = request?.URL?.absoluteString { 440 | components.append(URLString) 441 | } 442 | 443 | if let response = response { 444 | components.append("(\(response.statusCode))") 445 | } 446 | 447 | return components.joinWithSeparator(" ") 448 | } 449 | } 450 | 451 | // MARK: - CustomDebugStringConvertible 452 | 453 | extension Request: CustomDebugStringConvertible { 454 | func cURLRepresentation() -> String { 455 | var components = ["$ curl -i"] 456 | 457 | guard let 458 | request = self.request, 459 | URL = request.URL, 460 | host = URL.host 461 | else { 462 | return "$ curl command could not be created" 463 | } 464 | 465 | if let HTTPMethod = request.HTTPMethod where HTTPMethod != "GET" { 466 | components.append("-X \(HTTPMethod)") 467 | } 468 | 469 | if let credentialStorage = self.session.configuration.URLCredentialStorage { 470 | let protectionSpace = NSURLProtectionSpace( 471 | host: host, 472 | port: URL.port?.integerValue ?? 0, 473 | `protocol`: URL.scheme, 474 | realm: host, 475 | authenticationMethod: NSURLAuthenticationMethodHTTPBasic 476 | ) 477 | 478 | if let credentials = credentialStorage.credentialsForProtectionSpace(protectionSpace)?.values { 479 | for credential in credentials { 480 | components.append("-u \(credential.user!):\(credential.password!)") 481 | } 482 | } else { 483 | if let credential = delegate.credential { 484 | components.append("-u \(credential.user!):\(credential.password!)") 485 | } 486 | } 487 | } 488 | 489 | if session.configuration.HTTPShouldSetCookies { 490 | if let 491 | cookieStorage = session.configuration.HTTPCookieStorage, 492 | cookies = cookieStorage.cookiesForURL(URL) where !cookies.isEmpty 493 | { 494 | let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value ?? String());" } 495 | components.append("-b \"\(string.substringToIndex(string.endIndex.predecessor()))\"") 496 | } 497 | } 498 | 499 | if let headerFields = request.allHTTPHeaderFields { 500 | for (field, value) in headerFields { 501 | switch field { 502 | case "Cookie": 503 | continue 504 | default: 505 | components.append("-H \"\(field): \(value)\"") 506 | } 507 | } 508 | } 509 | 510 | if let additionalHeaders = session.configuration.HTTPAdditionalHeaders { 511 | for (field, value) in additionalHeaders { 512 | switch field { 513 | case "Cookie": 514 | continue 515 | default: 516 | components.append("-H \"\(field): \(value)\"") 517 | } 518 | } 519 | } 520 | 521 | if let 522 | HTTPBodyData = request.HTTPBody, 523 | HTTPBody = String(data: HTTPBodyData, encoding: NSUTF8StringEncoding) 524 | { 525 | let escapedBody = HTTPBody.stringByReplacingOccurrencesOfString("\"", withString: "\\\"") 526 | components.append("-d \"\(escapedBody)\"") 527 | } 528 | 529 | components.append("\"\(URL.absoluteString)\"") 530 | 531 | return components.joinWithSeparator(" \\\n\t") 532 | } 533 | 534 | /// The textual representation used when written to an output stream, in the form of a cURL command. 535 | public var debugDescription: String { 536 | return cURLRepresentation() 537 | } 538 | } 539 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Response.swift: -------------------------------------------------------------------------------- 1 | // Response.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /// Used to store all response data returned from a completed `Request`. 26 | public struct Response { 27 | /// The URL request sent to the server. 28 | public let request: NSURLRequest? 29 | 30 | /// The server's response to the URL request. 31 | public let response: NSHTTPURLResponse? 32 | 33 | /// The data returned by the server. 34 | public let data: NSData? 35 | 36 | /// The result of response serialization. 37 | public let result: Result 38 | 39 | /** 40 | Initializes the `Response` instance with the specified URL request, URL response, server data and response 41 | serialization result. 42 | 43 | - parameter request: The URL request sent to the server. 44 | - parameter response: The server's response to the URL request. 45 | - parameter data: The data returned by the server. 46 | - parameter result: The result of response serialization. 47 | 48 | - returns: the new `Response` instance. 49 | */ 50 | public init(request: NSURLRequest?, response: NSHTTPURLResponse?, data: NSData?, result: Result) { 51 | self.request = request 52 | self.response = response 53 | self.data = data 54 | self.result = result 55 | } 56 | } 57 | 58 | // MARK: - CustomStringConvertible 59 | 60 | extension Response: CustomStringConvertible { 61 | /// The textual representation used when written to an output stream, which includes whether the result was a 62 | /// success or failure. 63 | public var description: String { 64 | return result.debugDescription 65 | } 66 | } 67 | 68 | // MARK: - CustomDebugStringConvertible 69 | 70 | extension Response: CustomDebugStringConvertible { 71 | /// The debug textual representation used when written to an output stream, which includes the URL request, the URL 72 | /// response, the server data and the response serialization result. 73 | public var debugDescription: String { 74 | var output: [String] = [] 75 | 76 | output.append(request != nil ? "[Request]: \(request!)" : "[Request]: nil") 77 | output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") 78 | output.append("[Data]: \(data?.length ?? 0) bytes") 79 | output.append("[Result]: \(result.debugDescription)") 80 | 81 | return output.joinWithSeparator("\n") 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ResponseSerialization.swift: -------------------------------------------------------------------------------- 1 | // ResponseSerialization.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | // MARK: ResponseSerializer 26 | 27 | /** 28 | The type in which all response serializers must conform to in order to serialize a response. 29 | */ 30 | public protocol ResponseSerializerType { 31 | /// The type of serialized object to be created by this `ResponseSerializerType`. 32 | typealias SerializedObject 33 | 34 | /// The type of error to be created by this `ResponseSerializer` if serialization fails. 35 | typealias ErrorObject: ErrorType 36 | 37 | /** 38 | A closure used by response handlers that takes a request, response, data and error and returns a result. 39 | */ 40 | var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result { get } 41 | } 42 | 43 | // MARK: - 44 | 45 | /** 46 | A generic `ResponseSerializerType` used to serialize a request, response, and data into a serialized object. 47 | */ 48 | public struct ResponseSerializer: ResponseSerializerType { 49 | /// The type of serialized object to be created by this `ResponseSerializer`. 50 | public typealias SerializedObject = Value 51 | 52 | /// The type of error to be created by this `ResponseSerializer` if serialization fails. 53 | public typealias ErrorObject = Error 54 | 55 | /** 56 | A closure used by response handlers that takes a request, response, data and error and returns a result. 57 | */ 58 | public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result 59 | 60 | /** 61 | Initializes the `ResponseSerializer` instance with the given serialize response closure. 62 | 63 | - parameter serializeResponse: The closure used to serialize the response. 64 | 65 | - returns: The new generic response serializer instance. 66 | */ 67 | public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result) { 68 | self.serializeResponse = serializeResponse 69 | } 70 | } 71 | 72 | // MARK: - Default 73 | 74 | extension Request { 75 | 76 | /** 77 | Adds a handler to be called once the request has finished. 78 | 79 | - parameter queue: The queue on which the completion handler is dispatched. 80 | - parameter completionHandler: The code to be executed once the request has finished. 81 | 82 | - returns: The request. 83 | */ 84 | public func response( 85 | queue queue: dispatch_queue_t? = nil, 86 | completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Void) 87 | -> Self 88 | { 89 | delegate.queue.addOperationWithBlock { 90 | dispatch_async(queue ?? dispatch_get_main_queue()) { 91 | completionHandler(self.request, self.response, self.delegate.data, self.delegate.error) 92 | } 93 | } 94 | 95 | return self 96 | } 97 | 98 | /** 99 | Adds a handler to be called once the request has finished. 100 | 101 | - parameter queue: The queue on which the completion handler is dispatched. 102 | - parameter responseSerializer: The response serializer responsible for serializing the request, response, 103 | and data. 104 | - parameter completionHandler: The code to be executed once the request has finished. 105 | 106 | - returns: The request. 107 | */ 108 | public func response( 109 | queue queue: dispatch_queue_t? = nil, 110 | responseSerializer: T, 111 | completionHandler: Response -> Void) 112 | -> Self 113 | { 114 | delegate.queue.addOperationWithBlock { 115 | let result = responseSerializer.serializeResponse( 116 | self.request, 117 | self.response, 118 | self.delegate.data, 119 | self.delegate.error 120 | ) 121 | 122 | dispatch_async(queue ?? dispatch_get_main_queue()) { 123 | let response = Response( 124 | request: self.request, 125 | response: self.response, 126 | data: self.delegate.data, 127 | result: result 128 | ) 129 | 130 | completionHandler(response) 131 | } 132 | } 133 | 134 | return self 135 | } 136 | } 137 | 138 | // MARK: - Data 139 | 140 | extension Request { 141 | 142 | /** 143 | Creates a response serializer that returns the associated data as-is. 144 | 145 | - returns: A data response serializer. 146 | */ 147 | public static func dataResponseSerializer() -> ResponseSerializer { 148 | return ResponseSerializer { _, response, data, error in 149 | guard error == nil else { return .Failure(error!) } 150 | 151 | if let response = response where response.statusCode == 204 { return .Success(NSData()) } 152 | 153 | guard let validData = data else { 154 | let failureReason = "Data could not be serialized. Input data was nil." 155 | let error = Error.errorWithCode(.DataSerializationFailed, failureReason: failureReason) 156 | return .Failure(error) 157 | } 158 | 159 | return .Success(validData) 160 | } 161 | } 162 | 163 | /** 164 | Adds a handler to be called once the request has finished. 165 | 166 | - parameter completionHandler: The code to be executed once the request has finished. 167 | 168 | - returns: The request. 169 | */ 170 | public func responseData(completionHandler: Response -> Void) -> Self { 171 | return response(responseSerializer: Request.dataResponseSerializer(), completionHandler: completionHandler) 172 | } 173 | } 174 | 175 | // MARK: - String 176 | 177 | extension Request { 178 | 179 | /** 180 | Creates a response serializer that returns a string initialized from the response data with the specified 181 | string encoding. 182 | 183 | - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server 184 | response, falling back to the default HTTP default character set, ISO-8859-1. 185 | 186 | - returns: A string response serializer. 187 | */ 188 | public static func stringResponseSerializer( 189 | var encoding encoding: NSStringEncoding? = nil) 190 | -> ResponseSerializer 191 | { 192 | return ResponseSerializer { _, response, data, error in 193 | guard error == nil else { return .Failure(error!) } 194 | 195 | if let response = response where response.statusCode == 204 { return .Success("") } 196 | 197 | guard let validData = data else { 198 | let failureReason = "String could not be serialized. Input data was nil." 199 | let error = Error.errorWithCode(.StringSerializationFailed, failureReason: failureReason) 200 | return .Failure(error) 201 | } 202 | 203 | if let encodingName = response?.textEncodingName where encoding == nil { 204 | encoding = CFStringConvertEncodingToNSStringEncoding( 205 | CFStringConvertIANACharSetNameToEncoding(encodingName) 206 | ) 207 | } 208 | 209 | let actualEncoding = encoding ?? NSISOLatin1StringEncoding 210 | 211 | if let string = String(data: validData, encoding: actualEncoding) { 212 | return .Success(string) 213 | } else { 214 | let failureReason = "String could not be serialized with encoding: \(actualEncoding)" 215 | let error = Error.errorWithCode(.StringSerializationFailed, failureReason: failureReason) 216 | return .Failure(error) 217 | } 218 | } 219 | } 220 | 221 | /** 222 | Adds a handler to be called once the request has finished. 223 | 224 | - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the 225 | server response, falling back to the default HTTP default character set, 226 | ISO-8859-1. 227 | - parameter completionHandler: A closure to be executed once the request has finished. 228 | 229 | - returns: The request. 230 | */ 231 | public func responseString( 232 | encoding encoding: NSStringEncoding? = nil, 233 | completionHandler: Response -> Void) 234 | -> Self 235 | { 236 | return response( 237 | responseSerializer: Request.stringResponseSerializer(encoding: encoding), 238 | completionHandler: completionHandler 239 | ) 240 | } 241 | } 242 | 243 | // MARK: - JSON 244 | 245 | extension Request { 246 | 247 | /** 248 | Creates a response serializer that returns a JSON object constructed from the response data using 249 | `NSJSONSerialization` with the specified reading options. 250 | 251 | - parameter options: The JSON serialization reading options. `.AllowFragments` by default. 252 | 253 | - returns: A JSON object response serializer. 254 | */ 255 | public static func JSONResponseSerializer( 256 | options options: NSJSONReadingOptions = .AllowFragments) 257 | -> ResponseSerializer 258 | { 259 | return ResponseSerializer { _, response, data, error in 260 | guard error == nil else { return .Failure(error!) } 261 | 262 | if let response = response where response.statusCode == 204 { return .Success(NSNull()) } 263 | 264 | guard let validData = data where validData.length > 0 else { 265 | let failureReason = "JSON could not be serialized. Input data was nil or zero length." 266 | let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason) 267 | return .Failure(error) 268 | } 269 | 270 | do { 271 | let JSON = try NSJSONSerialization.JSONObjectWithData(validData, options: options) 272 | return .Success(JSON) 273 | } catch { 274 | return .Failure(error as NSError) 275 | } 276 | } 277 | } 278 | 279 | /** 280 | Adds a handler to be called once the request has finished. 281 | 282 | - parameter options: The JSON serialization reading options. `.AllowFragments` by default. 283 | - parameter completionHandler: A closure to be executed once the request has finished. 284 | 285 | - returns: The request. 286 | */ 287 | public func responseJSON( 288 | options options: NSJSONReadingOptions = .AllowFragments, 289 | completionHandler: Response -> Void) 290 | -> Self 291 | { 292 | return response( 293 | responseSerializer: Request.JSONResponseSerializer(options: options), 294 | completionHandler: completionHandler 295 | ) 296 | } 297 | } 298 | 299 | // MARK: - Property List 300 | 301 | extension Request { 302 | 303 | /** 304 | Creates a response serializer that returns an object constructed from the response data using 305 | `NSPropertyListSerialization` with the specified reading options. 306 | 307 | - parameter options: The property list reading options. `NSPropertyListReadOptions()` by default. 308 | 309 | - returns: A property list object response serializer. 310 | */ 311 | public static func propertyListResponseSerializer( 312 | options options: NSPropertyListReadOptions = NSPropertyListReadOptions()) 313 | -> ResponseSerializer 314 | { 315 | return ResponseSerializer { _, response, data, error in 316 | guard error == nil else { return .Failure(error!) } 317 | 318 | if let response = response where response.statusCode == 204 { return .Success(NSNull()) } 319 | 320 | guard let validData = data where validData.length > 0 else { 321 | let failureReason = "Property list could not be serialized. Input data was nil or zero length." 322 | let error = Error.errorWithCode(.PropertyListSerializationFailed, failureReason: failureReason) 323 | return .Failure(error) 324 | } 325 | 326 | do { 327 | let plist = try NSPropertyListSerialization.propertyListWithData(validData, options: options, format: nil) 328 | return .Success(plist) 329 | } catch { 330 | return .Failure(error as NSError) 331 | } 332 | } 333 | } 334 | 335 | /** 336 | Adds a handler to be called once the request has finished. 337 | 338 | - parameter options: The property list reading options. `0` by default. 339 | - parameter completionHandler: A closure to be executed once the request has finished. The closure takes 3 340 | arguments: the URL request, the URL response, the server data and the result 341 | produced while creating the property list. 342 | 343 | - returns: The request. 344 | */ 345 | public func responsePropertyList( 346 | options options: NSPropertyListReadOptions = NSPropertyListReadOptions(), 347 | completionHandler: Response -> Void) 348 | -> Self 349 | { 350 | return response( 351 | responseSerializer: Request.propertyListResponseSerializer(options: options), 352 | completionHandler: completionHandler 353 | ) 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Result.swift: -------------------------------------------------------------------------------- 1 | // Result.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | Used to represent whether a request was successful or encountered an error. 27 | 28 | - Success: The request and all post processing operations were successful resulting in the serialization of the 29 | provided associated value. 30 | - Failure: The request encountered an error resulting in a failure. The associated values are the original data 31 | provided by the server as well as the error that caused the failure. 32 | */ 33 | public enum Result { 34 | case Success(Value) 35 | case Failure(Error) 36 | 37 | /// Returns `true` if the result is a success, `false` otherwise. 38 | public var isSuccess: Bool { 39 | switch self { 40 | case .Success: 41 | return true 42 | case .Failure: 43 | return false 44 | } 45 | } 46 | 47 | /// Returns `true` if the result is a failure, `false` otherwise. 48 | public var isFailure: Bool { 49 | return !isSuccess 50 | } 51 | 52 | /// Returns the associated value if the result is a success, `nil` otherwise. 53 | public var value: Value? { 54 | switch self { 55 | case .Success(let value): 56 | return value 57 | case .Failure: 58 | return nil 59 | } 60 | } 61 | 62 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 63 | public var error: Error? { 64 | switch self { 65 | case .Success: 66 | return nil 67 | case .Failure(let error): 68 | return error 69 | } 70 | } 71 | } 72 | 73 | // MARK: - CustomStringConvertible 74 | 75 | extension Result: CustomStringConvertible { 76 | /// The textual representation used when written to an output stream, which includes whether the result was a 77 | /// success or failure. 78 | public var description: String { 79 | switch self { 80 | case .Success: 81 | return "SUCCESS" 82 | case .Failure: 83 | return "FAILURE" 84 | } 85 | } 86 | } 87 | 88 | // MARK: - CustomDebugStringConvertible 89 | 90 | extension Result: CustomDebugStringConvertible { 91 | /// The debug textual representation used when written to an output stream, which includes whether the result was a 92 | /// success or failure in addition to the value or error. 93 | public var debugDescription: String { 94 | switch self { 95 | case .Success(let value): 96 | return "SUCCESS: \(value)" 97 | case .Failure(let error): 98 | return "FAILURE: \(error)" 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ServerTrustPolicy.swift: -------------------------------------------------------------------------------- 1 | // ServerTrustPolicy.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host. 26 | public class ServerTrustPolicyManager { 27 | /// The dictionary of policies mapped to a particular host. 28 | public let policies: [String: ServerTrustPolicy] 29 | 30 | /** 31 | Initializes the `ServerTrustPolicyManager` instance with the given policies. 32 | 33 | Since different servers and web services can have different leaf certificates, intermediate and even root 34 | certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This 35 | allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key 36 | pinning for host3 and disabling evaluation for host4. 37 | 38 | - parameter policies: A dictionary of all policies mapped to a particular host. 39 | 40 | - returns: The new `ServerTrustPolicyManager` instance. 41 | */ 42 | public init(policies: [String: ServerTrustPolicy]) { 43 | self.policies = policies 44 | } 45 | 46 | /** 47 | Returns the `ServerTrustPolicy` for the given host if applicable. 48 | 49 | By default, this method will return the policy that perfectly matches the given host. Subclasses could override 50 | this method and implement more complex mapping implementations such as wildcards. 51 | 52 | - parameter host: The host to use when searching for a matching policy. 53 | 54 | - returns: The server trust policy for the given host if found. 55 | */ 56 | public func serverTrustPolicyForHost(host: String) -> ServerTrustPolicy? { 57 | return policies[host] 58 | } 59 | } 60 | 61 | // MARK: - 62 | 63 | extension NSURLSession { 64 | private struct AssociatedKeys { 65 | static var ManagerKey = "NSURLSession.ServerTrustPolicyManager" 66 | } 67 | 68 | var serverTrustPolicyManager: ServerTrustPolicyManager? { 69 | get { 70 | return objc_getAssociatedObject(self, &AssociatedKeys.ManagerKey) as? ServerTrustPolicyManager 71 | } 72 | set (manager) { 73 | objc_setAssociatedObject(self, &AssociatedKeys.ManagerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 74 | } 75 | } 76 | } 77 | 78 | // MARK: - ServerTrustPolicy 79 | 80 | /** 81 | The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when 82 | connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust 83 | with a given set of criteria to determine whether the server trust is valid and the connection should be made. 84 | 85 | Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) attacks and other 86 | vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged 87 | to route all communication over an HTTPS connection with pinning enabled. 88 | 89 | - PerformDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to 90 | validate the host provided by the challenge. Applications are encouraged to always 91 | validate the host in production environments to guarantee the validity of the server's 92 | certificate chain. 93 | 94 | - PinCertificates: Uses the pinned certificates to validate the server trust. The server trust is 95 | considered valid if one of the pinned certificates match one of the server certificates. 96 | By validating both the certificate chain and host, certificate pinning provides a very 97 | secure form of server trust validation mitigating most, if not all, MITM attacks. 98 | Applications are encouraged to always validate the host and require a valid certificate 99 | chain in production environments. 100 | 101 | - PinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered 102 | valid if one of the pinned public keys match one of the server certificate public keys. 103 | By validating both the certificate chain and host, public key pinning provides a very 104 | secure form of server trust validation mitigating most, if not all, MITM attacks. 105 | Applications are encouraged to always validate the host and require a valid certificate 106 | chain in production environments. 107 | 108 | - DisableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid. 109 | 110 | - CustomEvaluation: Uses the associated closure to evaluate the validity of the server trust. 111 | */ 112 | public enum ServerTrustPolicy { 113 | case PerformDefaultEvaluation(validateHost: Bool) 114 | case PinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) 115 | case PinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) 116 | case DisableEvaluation 117 | case CustomEvaluation((serverTrust: SecTrust, host: String) -> Bool) 118 | 119 | // MARK: - Bundle Location 120 | 121 | /** 122 | Returns all certificates within the given bundle with a `.cer` file extension. 123 | 124 | - parameter bundle: The bundle to search for all `.cer` files. 125 | 126 | - returns: All certificates within the given bundle. 127 | */ 128 | public static func certificatesInBundle(bundle: NSBundle = NSBundle.mainBundle()) -> [SecCertificate] { 129 | var certificates: [SecCertificate] = [] 130 | 131 | let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in 132 | bundle.pathsForResourcesOfType(fileExtension, inDirectory: nil) 133 | }.flatten()) 134 | 135 | for path in paths { 136 | if let 137 | certificateData = NSData(contentsOfFile: path), 138 | certificate = SecCertificateCreateWithData(nil, certificateData) 139 | { 140 | certificates.append(certificate) 141 | } 142 | } 143 | 144 | return certificates 145 | } 146 | 147 | /** 148 | Returns all public keys within the given bundle with a `.cer` file extension. 149 | 150 | - parameter bundle: The bundle to search for all `*.cer` files. 151 | 152 | - returns: All public keys within the given bundle. 153 | */ 154 | public static func publicKeysInBundle(bundle: NSBundle = NSBundle.mainBundle()) -> [SecKey] { 155 | var publicKeys: [SecKey] = [] 156 | 157 | for certificate in certificatesInBundle(bundle) { 158 | if let publicKey = publicKeyForCertificate(certificate) { 159 | publicKeys.append(publicKey) 160 | } 161 | } 162 | 163 | return publicKeys 164 | } 165 | 166 | // MARK: - Evaluation 167 | 168 | /** 169 | Evaluates whether the server trust is valid for the given host. 170 | 171 | - parameter serverTrust: The server trust to evaluate. 172 | - parameter host: The host of the challenge protection space. 173 | 174 | - returns: Whether the server trust is valid. 175 | */ 176 | public func evaluateServerTrust(serverTrust: SecTrust, isValidForHost host: String) -> Bool { 177 | var serverTrustIsValid = false 178 | 179 | switch self { 180 | case let .PerformDefaultEvaluation(validateHost): 181 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 182 | SecTrustSetPolicies(serverTrust, [policy]) 183 | 184 | serverTrustIsValid = trustIsValid(serverTrust) 185 | case let .PinCertificates(pinnedCertificates, validateCertificateChain, validateHost): 186 | if validateCertificateChain { 187 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 188 | SecTrustSetPolicies(serverTrust, [policy]) 189 | 190 | SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates) 191 | SecTrustSetAnchorCertificatesOnly(serverTrust, true) 192 | 193 | serverTrustIsValid = trustIsValid(serverTrust) 194 | } else { 195 | let serverCertificatesDataArray = certificateDataForTrust(serverTrust) 196 | let pinnedCertificatesDataArray = certificateDataForCertificates(pinnedCertificates) 197 | 198 | outerLoop: for serverCertificateData in serverCertificatesDataArray { 199 | for pinnedCertificateData in pinnedCertificatesDataArray { 200 | if serverCertificateData.isEqualToData(pinnedCertificateData) { 201 | serverTrustIsValid = true 202 | break outerLoop 203 | } 204 | } 205 | } 206 | } 207 | case let .PinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost): 208 | var certificateChainEvaluationPassed = true 209 | 210 | if validateCertificateChain { 211 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 212 | SecTrustSetPolicies(serverTrust, [policy]) 213 | 214 | certificateChainEvaluationPassed = trustIsValid(serverTrust) 215 | } 216 | 217 | if certificateChainEvaluationPassed { 218 | outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeysForTrust(serverTrust) as [AnyObject] { 219 | for pinnedPublicKey in pinnedPublicKeys as [AnyObject] { 220 | if serverPublicKey.isEqual(pinnedPublicKey) { 221 | serverTrustIsValid = true 222 | break outerLoop 223 | } 224 | } 225 | } 226 | } 227 | case .DisableEvaluation: 228 | serverTrustIsValid = true 229 | case let .CustomEvaluation(closure): 230 | serverTrustIsValid = closure(serverTrust: serverTrust, host: host) 231 | } 232 | 233 | return serverTrustIsValid 234 | } 235 | 236 | // MARK: - Private - Trust Validation 237 | 238 | private func trustIsValid(trust: SecTrust) -> Bool { 239 | var isValid = false 240 | 241 | var result = SecTrustResultType(kSecTrustResultInvalid) 242 | let status = SecTrustEvaluate(trust, &result) 243 | 244 | if status == errSecSuccess { 245 | let unspecified = SecTrustResultType(kSecTrustResultUnspecified) 246 | let proceed = SecTrustResultType(kSecTrustResultProceed) 247 | 248 | isValid = result == unspecified || result == proceed 249 | } 250 | 251 | return isValid 252 | } 253 | 254 | // MARK: - Private - Certificate Data 255 | 256 | private func certificateDataForTrust(trust: SecTrust) -> [NSData] { 257 | var certificates: [SecCertificate] = [] 258 | 259 | for index in 0.. [NSData] { 269 | return certificates.map { SecCertificateCopyData($0) as NSData } 270 | } 271 | 272 | // MARK: - Private - Public Key Extraction 273 | 274 | private static func publicKeysForTrust(trust: SecTrust) -> [SecKey] { 275 | var publicKeys: [SecKey] = [] 276 | 277 | for index in 0.. SecKey? { 290 | var publicKey: SecKey? 291 | 292 | let policy = SecPolicyCreateBasicX509() 293 | var trust: SecTrust? 294 | let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust) 295 | 296 | if let trust = trust where trustCreationStatus == errSecSuccess { 297 | publicKey = SecTrustCopyPublicKey(trust) 298 | } 299 | 300 | return publicKey 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Stream.swift: -------------------------------------------------------------------------------- 1 | // Stream.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | #if !os(watchOS) 26 | 27 | @available(iOS 9.0, OSX 10.11, *) 28 | extension Manager { 29 | private enum Streamable { 30 | case Stream(String, Int) 31 | case NetService(NSNetService) 32 | } 33 | 34 | private func stream(streamable: Streamable) -> Request { 35 | var streamTask: NSURLSessionStreamTask! 36 | 37 | switch streamable { 38 | case .Stream(let hostName, let port): 39 | dispatch_sync(queue) { 40 | streamTask = self.session.streamTaskWithHostName(hostName, port: port) 41 | } 42 | case .NetService(let netService): 43 | dispatch_sync(queue) { 44 | streamTask = self.session.streamTaskWithNetService(netService) 45 | } 46 | } 47 | 48 | let request = Request(session: session, task: streamTask) 49 | 50 | delegate[request.delegate.task] = request.delegate 51 | 52 | if startRequestsImmediately { 53 | request.resume() 54 | } 55 | 56 | return request 57 | } 58 | 59 | /** 60 | Creates a request for bidirectional streaming with the given hostname and port. 61 | 62 | - parameter hostName: The hostname of the server to connect to. 63 | - parameter port: The port of the server to connect to. 64 | 65 | :returns: The created stream request. 66 | */ 67 | public func stream(hostName hostName: String, port: Int) -> Request { 68 | return stream(.Stream(hostName, port)) 69 | } 70 | 71 | /** 72 | Creates a request for bidirectional streaming with the given `NSNetService`. 73 | 74 | - parameter netService: The net service used to identify the endpoint. 75 | 76 | - returns: The created stream request. 77 | */ 78 | public func stream(netService netService: NSNetService) -> Request { 79 | return stream(.NetService(netService)) 80 | } 81 | } 82 | 83 | // MARK: - 84 | 85 | @available(iOS 9.0, OSX 10.11, *) 86 | extension Manager.SessionDelegate: NSURLSessionStreamDelegate { 87 | 88 | // MARK: Override Closures 89 | 90 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:readClosedForStreamTask:`. 91 | public var streamTaskReadClosed: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 92 | get { 93 | return _streamTaskReadClosed as? (NSURLSession, NSURLSessionStreamTask) -> Void 94 | } 95 | set { 96 | _streamTaskReadClosed = newValue 97 | } 98 | } 99 | 100 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:writeClosedForStreamTask:`. 101 | public var streamTaskWriteClosed: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 102 | get { 103 | return _streamTaskWriteClosed as? (NSURLSession, NSURLSessionStreamTask) -> Void 104 | } 105 | set { 106 | _streamTaskWriteClosed = newValue 107 | } 108 | } 109 | 110 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:betterRouteDiscoveredForStreamTask:`. 111 | public var streamTaskBetterRouteDiscovered: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 112 | get { 113 | return _streamTaskBetterRouteDiscovered as? (NSURLSession, NSURLSessionStreamTask) -> Void 114 | } 115 | set { 116 | _streamTaskBetterRouteDiscovered = newValue 117 | } 118 | } 119 | 120 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:streamTask:didBecomeInputStream:outputStream:`. 121 | public var streamTaskDidBecomeInputStream: ((NSURLSession, NSURLSessionStreamTask, NSInputStream, NSOutputStream) -> Void)? { 122 | get { 123 | return _streamTaskDidBecomeInputStream as? (NSURLSession, NSURLSessionStreamTask, NSInputStream, NSOutputStream) -> Void 124 | } 125 | set { 126 | _streamTaskDidBecomeInputStream = newValue 127 | } 128 | } 129 | 130 | // MARK: Delegate Methods 131 | 132 | /** 133 | Tells the delegate that the read side of the connection has been closed. 134 | 135 | - parameter session: The session. 136 | - parameter streamTask: The stream task. 137 | */ 138 | public func URLSession(session: NSURLSession, readClosedForStreamTask streamTask: NSURLSessionStreamTask) { 139 | streamTaskReadClosed?(session, streamTask) 140 | } 141 | 142 | /** 143 | Tells the delegate that the write side of the connection has been closed. 144 | 145 | - parameter session: The session. 146 | - parameter streamTask: The stream task. 147 | */ 148 | public func URLSession(session: NSURLSession, writeClosedForStreamTask streamTask: NSURLSessionStreamTask) { 149 | streamTaskWriteClosed?(session, streamTask) 150 | } 151 | 152 | /** 153 | Tells the delegate that the system has determined that a better route to the host is available. 154 | 155 | - parameter session: The session. 156 | - parameter streamTask: The stream task. 157 | */ 158 | public func URLSession(session: NSURLSession, betterRouteDiscoveredForStreamTask streamTask: NSURLSessionStreamTask) { 159 | streamTaskBetterRouteDiscovered?(session, streamTask) 160 | } 161 | 162 | /** 163 | Tells the delegate that the stream task has been completed and provides the unopened stream objects. 164 | 165 | - parameter session: The session. 166 | - parameter streamTask: The stream task. 167 | - parameter inputStream: The new input stream. 168 | - parameter outputStream: The new output stream. 169 | */ 170 | public func URLSession( 171 | session: NSURLSession, 172 | streamTask: NSURLSessionStreamTask, 173 | didBecomeInputStream inputStream: NSInputStream, 174 | outputStream: NSOutputStream) 175 | { 176 | streamTaskDidBecomeInputStream?(session, streamTask, inputStream, outputStream) 177 | } 178 | } 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Upload.swift: -------------------------------------------------------------------------------- 1 | // Upload.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | extension Manager { 26 | private enum Uploadable { 27 | case Data(NSURLRequest, NSData) 28 | case File(NSURLRequest, NSURL) 29 | case Stream(NSURLRequest, NSInputStream) 30 | } 31 | 32 | private func upload(uploadable: Uploadable) -> Request { 33 | var uploadTask: NSURLSessionUploadTask! 34 | var HTTPBodyStream: NSInputStream? 35 | 36 | switch uploadable { 37 | case .Data(let request, let data): 38 | dispatch_sync(queue) { 39 | uploadTask = self.session.uploadTaskWithRequest(request, fromData: data) 40 | } 41 | case .File(let request, let fileURL): 42 | dispatch_sync(queue) { 43 | uploadTask = self.session.uploadTaskWithRequest(request, fromFile: fileURL) 44 | } 45 | case .Stream(let request, let stream): 46 | dispatch_sync(queue) { 47 | uploadTask = self.session.uploadTaskWithStreamedRequest(request) 48 | } 49 | 50 | HTTPBodyStream = stream 51 | } 52 | 53 | let request = Request(session: session, task: uploadTask) 54 | 55 | if HTTPBodyStream != nil { 56 | request.delegate.taskNeedNewBodyStream = { _, _ in 57 | return HTTPBodyStream 58 | } 59 | } 60 | 61 | delegate[request.delegate.task] = request.delegate 62 | 63 | if startRequestsImmediately { 64 | request.resume() 65 | } 66 | 67 | return request 68 | } 69 | 70 | // MARK: File 71 | 72 | /** 73 | Creates a request for uploading a file to the specified URL request. 74 | 75 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 76 | 77 | - parameter URLRequest: The URL request 78 | - parameter file: The file to upload 79 | 80 | - returns: The created upload request. 81 | */ 82 | public func upload(URLRequest: URLRequestConvertible, file: NSURL) -> Request { 83 | return upload(.File(URLRequest.URLRequest, file)) 84 | } 85 | 86 | /** 87 | Creates a request for uploading a file to the specified URL request. 88 | 89 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 90 | 91 | - parameter method: The HTTP method. 92 | - parameter URLString: The URL string. 93 | - parameter headers: The HTTP headers. `nil` by default. 94 | - parameter file: The file to upload 95 | 96 | - returns: The created upload request. 97 | */ 98 | public func upload( 99 | method: Method, 100 | _ URLString: URLStringConvertible, 101 | headers: [String: String]? = nil, 102 | file: NSURL) 103 | -> Request 104 | { 105 | let mutableURLRequest = URLRequest(method, URLString, headers: headers) 106 | return upload(mutableURLRequest, file: file) 107 | } 108 | 109 | // MARK: Data 110 | 111 | /** 112 | Creates a request for uploading data to the specified URL request. 113 | 114 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 115 | 116 | - parameter URLRequest: The URL request. 117 | - parameter data: The data to upload. 118 | 119 | - returns: The created upload request. 120 | */ 121 | public func upload(URLRequest: URLRequestConvertible, data: NSData) -> Request { 122 | return upload(.Data(URLRequest.URLRequest, data)) 123 | } 124 | 125 | /** 126 | Creates a request for uploading data to the specified URL request. 127 | 128 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 129 | 130 | - parameter method: The HTTP method. 131 | - parameter URLString: The URL string. 132 | - parameter headers: The HTTP headers. `nil` by default. 133 | - parameter data: The data to upload 134 | 135 | - returns: The created upload request. 136 | */ 137 | public func upload( 138 | method: Method, 139 | _ URLString: URLStringConvertible, 140 | headers: [String: String]? = nil, 141 | data: NSData) 142 | -> Request 143 | { 144 | let mutableURLRequest = URLRequest(method, URLString, headers: headers) 145 | 146 | return upload(mutableURLRequest, data: data) 147 | } 148 | 149 | // MARK: Stream 150 | 151 | /** 152 | Creates a request for uploading a stream to the specified URL request. 153 | 154 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 155 | 156 | - parameter URLRequest: The URL request. 157 | - parameter stream: The stream to upload. 158 | 159 | - returns: The created upload request. 160 | */ 161 | public func upload(URLRequest: URLRequestConvertible, stream: NSInputStream) -> Request { 162 | return upload(.Stream(URLRequest.URLRequest, stream)) 163 | } 164 | 165 | /** 166 | Creates a request for uploading a stream to the specified URL request. 167 | 168 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 169 | 170 | - parameter method: The HTTP method. 171 | - parameter URLString: The URL string. 172 | - parameter headers: The HTTP headers. `nil` by default. 173 | - parameter stream: The stream to upload. 174 | 175 | - returns: The created upload request. 176 | */ 177 | public func upload( 178 | method: Method, 179 | _ URLString: URLStringConvertible, 180 | headers: [String: String]? = nil, 181 | stream: NSInputStream) 182 | -> Request 183 | { 184 | let mutableURLRequest = URLRequest(method, URLString, headers: headers) 185 | 186 | return upload(mutableURLRequest, stream: stream) 187 | } 188 | 189 | // MARK: MultipartFormData 190 | 191 | /// Default memory threshold used when encoding `MultipartFormData`. 192 | public static let MultipartFormDataEncodingMemoryThreshold: UInt64 = 10 * 1024 * 1024 193 | 194 | /** 195 | Defines whether the `MultipartFormData` encoding was successful and contains result of the encoding as 196 | associated values. 197 | 198 | - Success: Represents a successful `MultipartFormData` encoding and contains the new `Request` along with 199 | streaming information. 200 | - Failure: Used to represent a failure in the `MultipartFormData` encoding and also contains the encoding 201 | error. 202 | */ 203 | public enum MultipartFormDataEncodingResult { 204 | case Success(request: Request, streamingFromDisk: Bool, streamFileURL: NSURL?) 205 | case Failure(ErrorType) 206 | } 207 | 208 | /** 209 | Encodes the `MultipartFormData` and creates a request to upload the result to the specified URL request. 210 | 211 | It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative 212 | payload is small, encoding the data in-memory and directly uploading to a server is the by far the most 213 | efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to 214 | be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory 215 | footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be 216 | used for larger payloads such as video content. 217 | 218 | The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory 219 | or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, 220 | encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk 221 | during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding 222 | technique was used. 223 | 224 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 225 | 226 | - parameter method: The HTTP method. 227 | - parameter URLString: The URL string. 228 | - parameter headers: The HTTP headers. `nil` by default. 229 | - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. 230 | - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. 231 | `MultipartFormDataEncodingMemoryThreshold` by default. 232 | - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. 233 | */ 234 | public func upload( 235 | method: Method, 236 | _ URLString: URLStringConvertible, 237 | headers: [String: String]? = nil, 238 | multipartFormData: MultipartFormData -> Void, 239 | encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold, 240 | encodingCompletion: (MultipartFormDataEncodingResult -> Void)?) 241 | { 242 | let mutableURLRequest = URLRequest(method, URLString, headers: headers) 243 | 244 | return upload( 245 | mutableURLRequest, 246 | multipartFormData: multipartFormData, 247 | encodingMemoryThreshold: encodingMemoryThreshold, 248 | encodingCompletion: encodingCompletion 249 | ) 250 | } 251 | 252 | /** 253 | Encodes the `MultipartFormData` and creates a request to upload the result to the specified URL request. 254 | 255 | It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative 256 | payload is small, encoding the data in-memory and directly uploading to a server is the by far the most 257 | efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to 258 | be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory 259 | footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be 260 | used for larger payloads such as video content. 261 | 262 | The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory 263 | or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, 264 | encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk 265 | during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding 266 | technique was used. 267 | 268 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 269 | 270 | - parameter URLRequest: The URL request. 271 | - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. 272 | - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. 273 | `MultipartFormDataEncodingMemoryThreshold` by default. 274 | - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. 275 | */ 276 | public func upload( 277 | URLRequest: URLRequestConvertible, 278 | multipartFormData: MultipartFormData -> Void, 279 | encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold, 280 | encodingCompletion: (MultipartFormDataEncodingResult -> Void)?) 281 | { 282 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 283 | let formData = MultipartFormData() 284 | multipartFormData(formData) 285 | 286 | let URLRequestWithContentType = URLRequest.URLRequest 287 | URLRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type") 288 | 289 | let isBackgroundSession = self.session.configuration.identifier != nil 290 | 291 | if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession { 292 | do { 293 | let data = try formData.encode() 294 | let encodingResult = MultipartFormDataEncodingResult.Success( 295 | request: self.upload(URLRequestWithContentType, data: data), 296 | streamingFromDisk: false, 297 | streamFileURL: nil 298 | ) 299 | 300 | dispatch_async(dispatch_get_main_queue()) { 301 | encodingCompletion?(encodingResult) 302 | } 303 | } catch { 304 | dispatch_async(dispatch_get_main_queue()) { 305 | encodingCompletion?(.Failure(error as NSError)) 306 | } 307 | } 308 | } else { 309 | let fileManager = NSFileManager.defaultManager() 310 | let tempDirectoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory()) 311 | let directoryURL = tempDirectoryURL.URLByAppendingPathComponent("com.alamofire.manager/multipart.form.data") 312 | let fileName = NSUUID().UUIDString 313 | let fileURL = directoryURL.URLByAppendingPathComponent(fileName) 314 | 315 | do { 316 | try fileManager.createDirectoryAtURL(directoryURL, withIntermediateDirectories: true, attributes: nil) 317 | try formData.writeEncodedDataToDisk(fileURL) 318 | 319 | dispatch_async(dispatch_get_main_queue()) { 320 | let encodingResult = MultipartFormDataEncodingResult.Success( 321 | request: self.upload(URLRequestWithContentType, file: fileURL), 322 | streamingFromDisk: true, 323 | streamFileURL: fileURL 324 | ) 325 | encodingCompletion?(encodingResult) 326 | } 327 | } catch { 328 | dispatch_async(dispatch_get_main_queue()) { 329 | encodingCompletion?(.Failure(error as NSError)) 330 | } 331 | } 332 | } 333 | } 334 | } 335 | } 336 | 337 | // MARK: - 338 | 339 | extension Request { 340 | 341 | // MARK: - UploadTaskDelegate 342 | 343 | class UploadTaskDelegate: DataTaskDelegate { 344 | var uploadTask: NSURLSessionUploadTask? { return task as? NSURLSessionUploadTask } 345 | var uploadProgress: ((Int64, Int64, Int64) -> Void)! 346 | 347 | // MARK: - NSURLSessionTaskDelegate 348 | 349 | // MARK: Override Closures 350 | 351 | var taskDidSendBodyData: ((NSURLSession, NSURLSessionTask, Int64, Int64, Int64) -> Void)? 352 | 353 | // MARK: Delegate Methods 354 | 355 | func URLSession( 356 | session: NSURLSession, 357 | task: NSURLSessionTask, 358 | didSendBodyData bytesSent: Int64, 359 | totalBytesSent: Int64, 360 | totalBytesExpectedToSend: Int64) 361 | { 362 | if let taskDidSendBodyData = taskDidSendBodyData { 363 | taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) 364 | } else { 365 | progress.totalUnitCount = totalBytesExpectedToSend 366 | progress.completedUnitCount = totalBytesSent 367 | 368 | uploadProgress?(bytesSent, totalBytesSent, totalBytesExpectedToSend) 369 | } 370 | } 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Validation.swift: -------------------------------------------------------------------------------- 1 | // Validation.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | extension Request { 26 | 27 | /** 28 | Used to represent whether validation was successful or encountered an error resulting in a failure. 29 | 30 | - Success: The validation was successful. 31 | - Failure: The validation failed encountering the provided error. 32 | */ 33 | public enum ValidationResult { 34 | case Success 35 | case Failure(NSError) 36 | } 37 | 38 | /** 39 | A closure used to validate a request that takes a URL request and URL response, and returns whether the 40 | request was valid. 41 | */ 42 | public typealias Validation = (NSURLRequest?, NSHTTPURLResponse) -> ValidationResult 43 | 44 | /** 45 | Validates the request, using the specified closure. 46 | 47 | If validation fails, subsequent calls to response handlers will have an associated error. 48 | 49 | - parameter validation: A closure to validate the request. 50 | 51 | - returns: The request. 52 | */ 53 | public func validate(validation: Validation) -> Self { 54 | delegate.queue.addOperationWithBlock { 55 | if let 56 | response = self.response where self.delegate.error == nil, 57 | case let .Failure(error) = validation(self.request, response) 58 | { 59 | self.delegate.error = error 60 | } 61 | } 62 | 63 | return self 64 | } 65 | 66 | // MARK: - Status Code 67 | 68 | /** 69 | Validates that the response has a status code in the specified range. 70 | 71 | If validation fails, subsequent calls to response handlers will have an associated error. 72 | 73 | - parameter range: The range of acceptable status codes. 74 | 75 | - returns: The request. 76 | */ 77 | public func validate(statusCode acceptableStatusCode: S) -> Self { 78 | return validate { _, response in 79 | if acceptableStatusCode.contains(response.statusCode) { 80 | return .Success 81 | } else { 82 | let failureReason = "Response status code was unacceptable: \(response.statusCode)" 83 | return .Failure(Error.errorWithCode(.StatusCodeValidationFailed, failureReason: failureReason)) 84 | } 85 | } 86 | } 87 | 88 | // MARK: - Content-Type 89 | 90 | private struct MIMEType { 91 | let type: String 92 | let subtype: String 93 | 94 | init?(_ string: String) { 95 | let components: [String] = { 96 | let stripped = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) 97 | let split = stripped.substringToIndex(stripped.rangeOfString(";")?.startIndex ?? stripped.endIndex) 98 | return split.componentsSeparatedByString("/") 99 | }() 100 | 101 | if let 102 | type = components.first, 103 | subtype = components.last 104 | { 105 | self.type = type 106 | self.subtype = subtype 107 | } else { 108 | return nil 109 | } 110 | } 111 | 112 | func matches(MIME: MIMEType) -> Bool { 113 | switch (type, subtype) { 114 | case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"): 115 | return true 116 | default: 117 | return false 118 | } 119 | } 120 | } 121 | 122 | /** 123 | Validates that the response has a content type in the specified array. 124 | 125 | If validation fails, subsequent calls to response handlers will have an associated error. 126 | 127 | - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 128 | 129 | - returns: The request. 130 | */ 131 | public func validate(contentType acceptableContentTypes: S) -> Self { 132 | return validate { _, response in 133 | guard let validData = self.delegate.data where validData.length > 0 else { return .Success } 134 | 135 | if let 136 | responseContentType = response.MIMEType, 137 | responseMIMEType = MIMEType(responseContentType) 138 | { 139 | for contentType in acceptableContentTypes { 140 | if let acceptableMIMEType = MIMEType(contentType) where acceptableMIMEType.matches(responseMIMEType) { 141 | return .Success 142 | } 143 | } 144 | } else { 145 | for contentType in acceptableContentTypes { 146 | if let MIMEType = MIMEType(contentType) where MIMEType.type == "*" && MIMEType.subtype == "*" { 147 | return .Success 148 | } 149 | } 150 | } 151 | 152 | let failureReason: String 153 | 154 | if let responseContentType = response.MIMEType { 155 | failureReason = ( 156 | "Response content type \"\(responseContentType)\" does not match any acceptable " + 157 | "content types: \(acceptableContentTypes)" 158 | ) 159 | } else { 160 | failureReason = "Response content type was missing and acceptable content type does not match \"*/*\"" 161 | } 162 | 163 | return .Failure(Error.errorWithCode(.ContentTypeValidationFailed, failureReason: failureReason)) 164 | } 165 | } 166 | 167 | // MARK: - Automatic 168 | 169 | /** 170 | Validates that the response has a status code in the default acceptable range of 200...299, and that the content 171 | type matches any specified in the Accept HTTP header field. 172 | 173 | If validation fails, subsequent calls to response handlers will have an associated error. 174 | 175 | - returns: The request. 176 | */ 177 | public func validate() -> Self { 178 | let acceptableStatusCodes: Range = 200..<300 179 | let acceptableContentTypes: [String] = { 180 | if let accept = request?.valueForHTTPHeaderField("Accept") { 181 | return accept.componentsSeparatedByString(",") 182 | } 183 | 184 | return ["*/*"] 185 | }() 186 | 187 | return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (3.1.5) 3 | 4 | DEPENDENCIES: 5 | - Alamofire (~> 3.0) 6 | 7 | SPEC CHECKSUMS: 8 | Alamofire: 5f730ba29fd113b7ddd71c1e65d0c630acf5d7b0 9 | 10 | COCOAPODS: 0.39.0 11 | -------------------------------------------------------------------------------- /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 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double AlamofireVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /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 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Alamofire" "${PODS_ROOT}/Headers/Public" 3 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 4 | PODS_ROOT = ${SRCROOT} 5 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.1.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 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-SSL-Pinning/Pods-SSL-Pinning-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 - http://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning-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 | Title 38 | Alamofire 39 | Type 40 | PSGroupSpecifier 41 | 42 | 43 | FooterText 44 | Generated by CocoaPods - http://cocoapods.org 45 | Title 46 | 47 | Type 48 | PSGroupSpecifier 49 | 50 | 51 | StringsTable 52 | Acknowledgements 53 | Title 54 | Acknowledgements 55 | 56 | 57 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_SSL_Pinning : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_SSL_Pinning 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning-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="${CONFIGURATION_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 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | 86 | if [[ "$CONFIGURATION" == "Debug" ]]; then 87 | install_framework "Pods-SSL-Pinning/Alamofire.framework" 88 | fi 89 | if [[ "$CONFIGURATION" == "Release" ]]; then 90 | install_framework "Pods-SSL-Pinning/Alamofire.framework" 91 | fi 92 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_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 | realpath() { 12 | DIRECTORY="$(cd "${1%/*}" && pwd)" 13 | FILENAME="${1##*/}" 14 | echo "$DIRECTORY/$FILENAME" 15 | } 16 | 17 | install_resource() 18 | { 19 | case $1 in 20 | *.storyboard) 21 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 22 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 23 | ;; 24 | *.xib) 25 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 26 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 27 | ;; 28 | *.framework) 29 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 30 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 31 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 32 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 33 | ;; 34 | *.xcdatamodel) 35 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 36 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 37 | ;; 38 | *.xcdatamodeld) 39 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 40 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 41 | ;; 42 | *.xcmappingmodel) 43 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 44 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 45 | ;; 46 | *.xcassets) 47 | ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") 48 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 49 | ;; 50 | /*) 51 | echo "$1" 52 | echo "$1" >> "$RESOURCES_TO_COPY" 53 | ;; 54 | *) 55 | echo "${PODS_ROOT}/$1" 56 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 57 | ;; 58 | esac 59 | } 60 | 61 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 62 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 63 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 64 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 65 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 66 | fi 67 | rm -f "$RESOURCES_TO_COPY" 68 | 69 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 70 | then 71 | case "${TARGETED_DEVICE_FAMILY}" in 72 | 1,2) 73 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 74 | ;; 75 | 1) 76 | TARGET_DEVICE_ARGS="--target-device iphone" 77 | ;; 78 | 2) 79 | TARGET_DEVICE_ARGS="--target-device ipad" 80 | ;; 81 | *) 82 | TARGET_DEVICE_ARGS="--target-device mac" 83 | ;; 84 | esac 85 | 86 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 87 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 88 | while read line; do 89 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 90 | XCASSET_FILES+=("$line") 91 | fi 92 | done <<<"$OTHER_XCASSETS" 93 | 94 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 95 | fi 96 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double Pods_SSL_PinningVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char Pods_SSL_PinningVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning.debug.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Alamofire.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-SSL-Pinning 8 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_SSL_Pinning { 2 | umbrella header "Pods-SSL-Pinning-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning.release.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Alamofire.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-SSL-Pinning 8 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Resources/corrupted.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antekarin/ssl-pinning-swift/4301d8451ef9a1a2b5342f6df24d5d36aea2759e/Resources/corrupted.cer -------------------------------------------------------------------------------- /Resources/github.com.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antekarin/ssl-pinning-swift/4301d8451ef9a1a2b5342f6df24d5d36aea2759e/Resources/github.com.cer -------------------------------------------------------------------------------- /SSL-Pinning.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4638051E1C6213B4003484E8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4638051D1C6213B4003484E8 /* AppDelegate.swift */; }; 11 | 463805201C6213B4003484E8 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4638051F1C6213B4003484E8 /* ViewController.swift */; }; 12 | 463805231C6213B4003484E8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 463805211C6213B4003484E8 /* Main.storyboard */; }; 13 | 463805251C6213B4003484E8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 463805241C6213B4003484E8 /* Assets.xcassets */; }; 14 | 463805281C6213B4003484E8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 463805261C6213B4003484E8 /* LaunchScreen.storyboard */; }; 15 | 463805311C621B16003484E8 /* github.com.cer in Resources */ = {isa = PBXBuildFile; fileRef = 463805301C621B16003484E8 /* github.com.cer */; }; 16 | 46F7D2611C68B33800DDBC98 /* corrupted.cer in Resources */ = {isa = PBXBuildFile; fileRef = 46F7D2601C68B33800DDBC98 /* corrupted.cer */; }; 17 | CF113521FA8FFF30A279D518 /* Pods_SSL_Pinning.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5DD18964A5BFDEA797B1C5E /* Pods_SSL_Pinning.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 03AA085EC0044A1826ECFE14 /* Pods-SSL-Pinning.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SSL-Pinning.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning.debug.xcconfig"; sourceTree = ""; }; 22 | 4638051A1C6213B4003484E8 /* SSL-Pinning.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SSL-Pinning.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 4638051D1C6213B4003484E8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | 4638051F1C6213B4003484E8 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | 463805221C6213B4003484E8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 463805241C6213B4003484E8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | 463805271C6213B4003484E8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | 463805291C6213B4003484E8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 463805301C621B16003484E8 /* github.com.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = github.com.cer; sourceTree = ""; }; 30 | 46F7D2601C68B33800DDBC98 /* corrupted.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = corrupted.cer; sourceTree = ""; }; 31 | B5DD18964A5BFDEA797B1C5E /* Pods_SSL_Pinning.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SSL_Pinning.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | C808BD1F09052AE4F5C5862A /* Pods-SSL-Pinning.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SSL-Pinning.release.xcconfig"; path = "Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning.release.xcconfig"; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 463805171C6213B4003484E8 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | CF113521FA8FFF30A279D518 /* Pods_SSL_Pinning.framework in Frameworks */, 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | 2D4AB41F5B126C80A80110B8 /* Pods */ = { 48 | isa = PBXGroup; 49 | children = ( 50 | 03AA085EC0044A1826ECFE14 /* Pods-SSL-Pinning.debug.xcconfig */, 51 | C808BD1F09052AE4F5C5862A /* Pods-SSL-Pinning.release.xcconfig */, 52 | ); 53 | name = Pods; 54 | sourceTree = ""; 55 | }; 56 | 463805111C6213B4003484E8 = { 57 | isa = PBXGroup; 58 | children = ( 59 | 4638052F1C621B16003484E8 /* Resouces */, 60 | 4638051C1C6213B4003484E8 /* SSL-Pinning */, 61 | 4638051B1C6213B4003484E8 /* Products */, 62 | 2D4AB41F5B126C80A80110B8 /* Pods */, 63 | 90034B516BD280E335389B13 /* Frameworks */, 64 | ); 65 | sourceTree = ""; 66 | }; 67 | 4638051B1C6213B4003484E8 /* Products */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 4638051A1C6213B4003484E8 /* SSL-Pinning.app */, 71 | ); 72 | name = Products; 73 | sourceTree = ""; 74 | }; 75 | 4638051C1C6213B4003484E8 /* SSL-Pinning */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 4638051D1C6213B4003484E8 /* AppDelegate.swift */, 79 | 4638051F1C6213B4003484E8 /* ViewController.swift */, 80 | 463805211C6213B4003484E8 /* Main.storyboard */, 81 | 463805241C6213B4003484E8 /* Assets.xcassets */, 82 | 463805261C6213B4003484E8 /* LaunchScreen.storyboard */, 83 | 463805291C6213B4003484E8 /* Info.plist */, 84 | ); 85 | path = "SSL-Pinning"; 86 | sourceTree = ""; 87 | }; 88 | 4638052F1C621B16003484E8 /* Resouces */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 463805301C621B16003484E8 /* github.com.cer */, 92 | 46F7D2601C68B33800DDBC98 /* corrupted.cer */, 93 | ); 94 | name = Resouces; 95 | path = Resources; 96 | sourceTree = ""; 97 | }; 98 | 90034B516BD280E335389B13 /* Frameworks */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | B5DD18964A5BFDEA797B1C5E /* Pods_SSL_Pinning.framework */, 102 | ); 103 | name = Frameworks; 104 | sourceTree = ""; 105 | }; 106 | /* End PBXGroup section */ 107 | 108 | /* Begin PBXNativeTarget section */ 109 | 463805191C6213B4003484E8 /* SSL-Pinning */ = { 110 | isa = PBXNativeTarget; 111 | buildConfigurationList = 4638052C1C6213B4003484E8 /* Build configuration list for PBXNativeTarget "SSL-Pinning" */; 112 | buildPhases = ( 113 | 82DA7833CC55F2EF28175C10 /* Check Pods Manifest.lock */, 114 | 463805161C6213B4003484E8 /* Sources */, 115 | 463805171C6213B4003484E8 /* Frameworks */, 116 | 463805181C6213B4003484E8 /* Resources */, 117 | 83A08047E026B24800169626 /* Embed Pods Frameworks */, 118 | 97E188DB104DE1C65904835A /* Copy Pods Resources */, 119 | ); 120 | buildRules = ( 121 | ); 122 | dependencies = ( 123 | ); 124 | name = "SSL-Pinning"; 125 | productName = "SSL-Pinning"; 126 | productReference = 4638051A1C6213B4003484E8 /* SSL-Pinning.app */; 127 | productType = "com.apple.product-type.application"; 128 | }; 129 | /* End PBXNativeTarget section */ 130 | 131 | /* Begin PBXProject section */ 132 | 463805121C6213B4003484E8 /* Project object */ = { 133 | isa = PBXProject; 134 | attributes = { 135 | LastSwiftUpdateCheck = 0720; 136 | LastUpgradeCheck = 0720; 137 | ORGANIZATIONNAME = "Ante Karin"; 138 | TargetAttributes = { 139 | 463805191C6213B4003484E8 = { 140 | CreatedOnToolsVersion = 7.2; 141 | }; 142 | }; 143 | }; 144 | buildConfigurationList = 463805151C6213B4003484E8 /* Build configuration list for PBXProject "SSL-Pinning" */; 145 | compatibilityVersion = "Xcode 3.2"; 146 | developmentRegion = English; 147 | hasScannedForEncodings = 0; 148 | knownRegions = ( 149 | en, 150 | Base, 151 | ); 152 | mainGroup = 463805111C6213B4003484E8; 153 | productRefGroup = 4638051B1C6213B4003484E8 /* Products */; 154 | projectDirPath = ""; 155 | projectRoot = ""; 156 | targets = ( 157 | 463805191C6213B4003484E8 /* SSL-Pinning */, 158 | ); 159 | }; 160 | /* End PBXProject section */ 161 | 162 | /* Begin PBXResourcesBuildPhase section */ 163 | 463805181C6213B4003484E8 /* Resources */ = { 164 | isa = PBXResourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | 46F7D2611C68B33800DDBC98 /* corrupted.cer in Resources */, 168 | 463805281C6213B4003484E8 /* LaunchScreen.storyboard in Resources */, 169 | 463805251C6213B4003484E8 /* Assets.xcassets in Resources */, 170 | 463805311C621B16003484E8 /* github.com.cer in Resources */, 171 | 463805231C6213B4003484E8 /* Main.storyboard in Resources */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXResourcesBuildPhase section */ 176 | 177 | /* Begin PBXShellScriptBuildPhase section */ 178 | 82DA7833CC55F2EF28175C10 /* Check Pods Manifest.lock */ = { 179 | isa = PBXShellScriptBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | ); 183 | inputPaths = ( 184 | ); 185 | name = "Check Pods Manifest.lock"; 186 | outputPaths = ( 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | shellPath = /bin/sh; 190 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 191 | showEnvVarsInLog = 0; 192 | }; 193 | 83A08047E026B24800169626 /* Embed Pods Frameworks */ = { 194 | isa = PBXShellScriptBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | ); 198 | inputPaths = ( 199 | ); 200 | name = "Embed Pods Frameworks"; 201 | outputPaths = ( 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | shellPath = /bin/sh; 205 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning-frameworks.sh\"\n"; 206 | showEnvVarsInLog = 0; 207 | }; 208 | 97E188DB104DE1C65904835A /* Copy Pods Resources */ = { 209 | isa = PBXShellScriptBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | inputPaths = ( 214 | ); 215 | name = "Copy Pods Resources"; 216 | outputPaths = ( 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | shellPath = /bin/sh; 220 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SSL-Pinning/Pods-SSL-Pinning-resources.sh\"\n"; 221 | showEnvVarsInLog = 0; 222 | }; 223 | /* End PBXShellScriptBuildPhase section */ 224 | 225 | /* Begin PBXSourcesBuildPhase section */ 226 | 463805161C6213B4003484E8 /* Sources */ = { 227 | isa = PBXSourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | 463805201C6213B4003484E8 /* ViewController.swift in Sources */, 231 | 4638051E1C6213B4003484E8 /* AppDelegate.swift in Sources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | /* End PBXSourcesBuildPhase section */ 236 | 237 | /* Begin PBXVariantGroup section */ 238 | 463805211C6213B4003484E8 /* Main.storyboard */ = { 239 | isa = PBXVariantGroup; 240 | children = ( 241 | 463805221C6213B4003484E8 /* Base */, 242 | ); 243 | name = Main.storyboard; 244 | sourceTree = ""; 245 | }; 246 | 463805261C6213B4003484E8 /* LaunchScreen.storyboard */ = { 247 | isa = PBXVariantGroup; 248 | children = ( 249 | 463805271C6213B4003484E8 /* Base */, 250 | ); 251 | name = LaunchScreen.storyboard; 252 | sourceTree = ""; 253 | }; 254 | /* End PBXVariantGroup section */ 255 | 256 | /* Begin XCBuildConfiguration section */ 257 | 4638052A1C6213B4003484E8 /* Debug */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 262 | CLANG_CXX_LIBRARY = "libc++"; 263 | CLANG_ENABLE_MODULES = YES; 264 | CLANG_ENABLE_OBJC_ARC = YES; 265 | CLANG_WARN_BOOL_CONVERSION = YES; 266 | CLANG_WARN_CONSTANT_CONVERSION = YES; 267 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 268 | CLANG_WARN_EMPTY_BODY = YES; 269 | CLANG_WARN_ENUM_CONVERSION = YES; 270 | CLANG_WARN_INT_CONVERSION = YES; 271 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 272 | CLANG_WARN_UNREACHABLE_CODE = YES; 273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 274 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 275 | COPY_PHASE_STRIP = NO; 276 | DEBUG_INFORMATION_FORMAT = dwarf; 277 | ENABLE_STRICT_OBJC_MSGSEND = YES; 278 | ENABLE_TESTABILITY = YES; 279 | GCC_C_LANGUAGE_STANDARD = gnu99; 280 | GCC_DYNAMIC_NO_PIC = NO; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_OPTIMIZATION_LEVEL = 0; 283 | GCC_PREPROCESSOR_DEFINITIONS = ( 284 | "DEBUG=1", 285 | "$(inherited)", 286 | ); 287 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 289 | GCC_WARN_UNDECLARED_SELECTOR = YES; 290 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 291 | GCC_WARN_UNUSED_FUNCTION = YES; 292 | GCC_WARN_UNUSED_VARIABLE = YES; 293 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 294 | MTL_ENABLE_DEBUG_INFO = YES; 295 | ONLY_ACTIVE_ARCH = YES; 296 | SDKROOT = iphoneos; 297 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 298 | }; 299 | name = Debug; 300 | }; 301 | 4638052B1C6213B4003484E8 /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 306 | CLANG_CXX_LIBRARY = "libc++"; 307 | CLANG_ENABLE_MODULES = YES; 308 | CLANG_ENABLE_OBJC_ARC = YES; 309 | CLANG_WARN_BOOL_CONVERSION = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 312 | CLANG_WARN_EMPTY_BODY = YES; 313 | CLANG_WARN_ENUM_CONVERSION = YES; 314 | CLANG_WARN_INT_CONVERSION = YES; 315 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 319 | COPY_PHASE_STRIP = NO; 320 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 321 | ENABLE_NS_ASSERTIONS = NO; 322 | ENABLE_STRICT_OBJC_MSGSEND = YES; 323 | GCC_C_LANGUAGE_STANDARD = gnu99; 324 | GCC_NO_COMMON_BLOCKS = YES; 325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 327 | GCC_WARN_UNDECLARED_SELECTOR = YES; 328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 329 | GCC_WARN_UNUSED_FUNCTION = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 332 | MTL_ENABLE_DEBUG_INFO = NO; 333 | SDKROOT = iphoneos; 334 | VALIDATE_PRODUCT = YES; 335 | }; 336 | name = Release; 337 | }; 338 | 4638052D1C6213B4003484E8 /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | baseConfigurationReference = 03AA085EC0044A1826ECFE14 /* Pods-SSL-Pinning.debug.xcconfig */; 341 | buildSettings = { 342 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 343 | INFOPLIST_FILE = "SSL-Pinning/Info.plist"; 344 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 345 | PRODUCT_BUNDLE_IDENTIFIER = "hr.ante.SSL-Pinning"; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | }; 348 | name = Debug; 349 | }; 350 | 4638052E1C6213B4003484E8 /* Release */ = { 351 | isa = XCBuildConfiguration; 352 | baseConfigurationReference = C808BD1F09052AE4F5C5862A /* Pods-SSL-Pinning.release.xcconfig */; 353 | buildSettings = { 354 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 355 | INFOPLIST_FILE = "SSL-Pinning/Info.plist"; 356 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 357 | PRODUCT_BUNDLE_IDENTIFIER = "hr.ante.SSL-Pinning"; 358 | PRODUCT_NAME = "$(TARGET_NAME)"; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | 463805151C6213B4003484E8 /* Build configuration list for PBXProject "SSL-Pinning" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | 4638052A1C6213B4003484E8 /* Debug */, 369 | 4638052B1C6213B4003484E8 /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | 4638052C1C6213B4003484E8 /* Build configuration list for PBXNativeTarget "SSL-Pinning" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | 4638052D1C6213B4003484E8 /* Debug */, 378 | 4638052E1C6213B4003484E8 /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | }; 385 | rootObject = 463805121C6213B4003484E8 /* Project object */; 386 | } 387 | -------------------------------------------------------------------------------- /SSL-Pinning.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SSL-Pinning.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SSL-Pinning/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SSL-Pinning 4 | // 5 | // Created by Ante Karin on 03/02/16. 6 | // Copyright © 2016 Ante Karin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SSL-Pinning/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /SSL-Pinning/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SSL-Pinning/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 36 | 37 | 38 | 39 | 46 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /SSL-Pinning/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SSL-Pinning/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SSL-Pinning 4 | // 5 | // Created by Ante Karin on 03/02/16. 6 | // Copyright © 2016 Ante Karin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Alamofire 11 | 12 | class ViewController: UIViewController, NSURLSessionDelegate, NSURLSessionTaskDelegate { 13 | 14 | @IBOutlet weak var urlTextField: UITextField! 15 | @IBOutlet weak var responseTextView: UITextView! 16 | @IBOutlet weak var certificateCorruptionButton: UIButton! 17 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 18 | 19 | let githubCert = "github.com" 20 | let corruptedCert = "corrupted" 21 | 22 | var urlSession: NSURLSession! 23 | var serverTrustPolicy: ServerTrustPolicy! 24 | var serverTrustPolicies: [String: ServerTrustPolicy]! 25 | var afManager: Manager! 26 | 27 | var isSimulatingCertificateCorruption = false 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | let pathToCert = NSBundle.mainBundle().pathForResource(githubCert, ofType: "cer") 33 | let localCertificate:NSData = NSData(contentsOfFile: pathToCert!)! 34 | self.configureAlamoFireSSLPinningWithCertificateData(localCertificate) 35 | self.configureURLSession() 36 | 37 | self.activityIndicator.hidesWhenStopped = true 38 | } 39 | 40 | // MARK: Button actions 41 | 42 | @IBAction func alamoFireRequestHandler(sender: UIButton) { 43 | self.activityIndicator.startAnimating() 44 | if let urlText = self.urlTextField.text { 45 | self.afManager.request(.GET, urlText) 46 | .response { request, response, data, error in 47 | self.activityIndicator.stopAnimating() 48 | 49 | guard let data = data where error == nil else { 50 | self.responseTextView.text = error!.description 51 | self.responseTextView.textColor = UIColor.redColor() 52 | return 53 | } 54 | 55 | self.responseTextView.text = String(data: data, encoding: NSUTF8StringEncoding)! 56 | self.responseTextView.textColor = UIColor.blackColor() 57 | } 58 | 59 | } 60 | } 61 | 62 | @IBAction func nsurlSessionRequestHandler(sender: UIButton) { 63 | self.activityIndicator.startAnimating() 64 | self.urlSession?.dataTaskWithURL(NSURL(string:self.urlTextField.text!)!, completionHandler: { ( data, response, error) -> Void in 65 | dispatch_async(dispatch_get_main_queue(), { () -> Void in 66 | self.activityIndicator.stopAnimating() 67 | }) 68 | 69 | guard let data = data where error == nil else { 70 | dispatch_async(dispatch_get_main_queue(), { () -> Void in 71 | self.responseTextView.text = error!.description 72 | self.responseTextView.textColor = UIColor.redColor() 73 | }) 74 | return 75 | } 76 | 77 | dispatch_async(dispatch_get_main_queue(), { () -> Void in 78 | self.responseTextView.text = String(data: data, encoding: NSUTF8StringEncoding) 79 | self.responseTextView.textColor = UIColor.blackColor() 80 | }) 81 | }).resume() 82 | } 83 | 84 | @IBAction func toggleCertificateSimulation(sender: AnyObject) { 85 | if self.isSimulatingCertificateCorruption == true { 86 | self.isSimulatingCertificateCorruption = false; 87 | let pathToCert = NSBundle.mainBundle().pathForResource(githubCert, ofType: "cer") 88 | let localCertificate:NSData = NSData(contentsOfFile: pathToCert!)! 89 | self.configureAlamoFireSSLPinningWithCertificateData(localCertificate) 90 | self.certificateCorruptionButton.setTitleColor(self.certificateCorruptionButton.tintColor, forState: UIControlState.Normal) 91 | self.certificateCorruptionButton.setTitle("Simulate certificate corruption", forState: UIControlState.Normal) 92 | } else { 93 | self.isSimulatingCertificateCorruption = true 94 | let pathToCert = NSBundle.mainBundle().pathForResource(corruptedCert, ofType: "cer") 95 | let localCertificate:NSData = NSData(contentsOfFile: pathToCert!)! 96 | self.configureAlamoFireSSLPinningWithCertificateData(localCertificate) 97 | self.certificateCorruptionButton.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal) 98 | self.certificateCorruptionButton.setTitle("Simulating certificate corruption", forState: UIControlState.Normal) 99 | } 100 | } 101 | 102 | // MARK: SSL Config 103 | 104 | func configureAlamoFireSSLPinningWithCertificateData(certificateData: NSData) { 105 | self.serverTrustPolicy = ServerTrustPolicy.PinCertificates( 106 | // Getting the certificate from the certificate data 107 | certificates: [SecCertificateCreateWithData(nil, certificateData)!], 108 | // Choose to validate the complete certificate chain, not only the certificate itself 109 | validateCertificateChain: true, 110 | // Check that the certificate mathes the host who provided it 111 | validateHost: true 112 | ) 113 | 114 | self.serverTrustPolicies = [ 115 | "github.com": self.serverTrustPolicy! 116 | ] 117 | 118 | self.afManager = Manager( 119 | configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), 120 | serverTrustPolicyManager: ServerTrustPolicyManager(policies: self.serverTrustPolicies) 121 | ) 122 | } 123 | 124 | func configureURLSession() { 125 | self.urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: self, delegateQueue: nil) 126 | } 127 | 128 | // MARK: URL session delegate 129 | 130 | func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { 131 | let serverTrust = challenge.protectionSpace.serverTrust 132 | let certificate = SecTrustGetCertificateAtIndex(serverTrust!, 0) 133 | 134 | // Set SSL policies for domain name check 135 | let policies = NSMutableArray(); 136 | policies.addObject(SecPolicyCreateSSL(true, (challenge.protectionSpace.host))) 137 | SecTrustSetPolicies(serverTrust!, policies); 138 | 139 | // Evaluate server certificate 140 | var result: SecTrustResultType = 0 141 | SecTrustEvaluate(serverTrust!, &result) 142 | let isServerTrusted:Bool = (Int(result) == kSecTrustResultUnspecified || Int(result) == kSecTrustResultProceed) 143 | 144 | var certName = "" 145 | if self.isSimulatingCertificateCorruption { 146 | certName = corruptedCert 147 | } else { 148 | certName = githubCert 149 | } 150 | 151 | // Get local and remote cert data 152 | let remoteCertificateData:NSData = SecCertificateCopyData(certificate!) 153 | let pathToCert = NSBundle.mainBundle().pathForResource(certName, ofType: "cer") 154 | let localCertificate:NSData = NSData(contentsOfFile: pathToCert!)! 155 | 156 | if (isServerTrusted && remoteCertificateData.isEqualToData(localCertificate)) { 157 | let credential:NSURLCredential = NSURLCredential(forTrust: serverTrust!) 158 | completionHandler(.UseCredential, credential) 159 | } else { 160 | completionHandler(.CancelAuthenticationChallenge, nil) 161 | } 162 | } 163 | 164 | } 165 | 166 | --------------------------------------------------------------------------------