├── .gitignore ├── README.md ├── keys_template.plist └── swift-mvvm ├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── MultipartFormData.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── SessionDelegate.swift │ │ ├── SessionManager.swift │ │ ├── TaskDelegate.swift │ │ ├── Timeline.swift │ │ └── Validation.swift ├── Manifest.lock ├── Pods.xcodeproj │ └── project.pbxproj ├── SwiftyJSON │ ├── LICENSE │ ├── README.md │ └── Source │ │ └── SwiftyJSON.swift └── Target Support Files │ ├── Alamofire │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.modulemap │ ├── Alamofire.xcconfig │ └── Info.plist │ ├── Pods-swift-mvvm │ ├── Info.plist │ ├── Pods-swift-mvvm-acknowledgements.markdown │ ├── Pods-swift-mvvm-acknowledgements.plist │ ├── Pods-swift-mvvm-dummy.m │ ├── Pods-swift-mvvm-frameworks.sh │ ├── Pods-swift-mvvm-resources.sh │ ├── Pods-swift-mvvm-umbrella.h │ ├── Pods-swift-mvvm.debug.xcconfig │ ├── Pods-swift-mvvm.modulemap │ └── Pods-swift-mvvm.release.xcconfig │ └── SwiftyJSON │ ├── Info.plist │ ├── SwiftyJSON-dummy.m │ ├── SwiftyJSON-prefix.pch │ ├── SwiftyJSON-umbrella.h │ ├── SwiftyJSON.modulemap │ └── SwiftyJSON.xcconfig ├── swift-mvvm.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── swift-mvvm.xcworkspace └── contents.xcworkspacedata └── swift-mvvm ├── AppDelegate.swift ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── NetworkManager.swift ├── Utilities.swift ├── ViewController.swift ├── WeatherData.swift ├── WeatherTableViewCell.swift └── WeatherViewModel.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User Added 6 | keys.plist 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xcuserstate 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift MVVM 2 | A Swift Example of ISL's MVVM Technique 3 | 4 | 5 | ### 💰 Installation 6 | 7 | 1. Create an account or grab a new API key from [Dark Sky API](https://darksky.net/dev/) 8 | 2. Make sure CocoaPods is installed and up to date. Run `pod install` in the project directory. 9 | 3. At the root of the repo find the `keys_template.plst` file. Copy or rename to `keys.plist`. 10 | 4. Open `swift-mvvm.workspace` and drag the `keys.plist` into the project and add to target. Open the file an put your API Key in the plist under `apiKey`. 11 | 5. Build and run! 12 | 13 | 14 | ### ⛽️ Dependecies 15 | 16 | To minimize code and highlight the MVVM structure, two dependencies were used to make network calls easier. 17 | 18 | * Alamofire 19 | * SwiftyJSON 20 | 21 | ### 📱 Project Overview 22 | 23 | * `NetworkManager.swift` - Handles the GET Request to Dark Sky's API. 24 | * `WeatherData.swift` - Data model with the information we want to show in the app. 25 | * `WeatherViewModel.swift` - Takes in `WeatherData` and formats the data to be displayed in the views. 26 | * `WeatherTableViewCell.swift` - Updates the view from the view model. 27 | * `Utilities.swift` - Converts the payload into an array of `WeatherData`. 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /keys_template.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | apiKey 6 | PUTKEYHERE 7 | 8 | 9 | -------------------------------------------------------------------------------- /swift-mvvm/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '10.0' 2 | use_frameworks! 3 | 4 | target 'swift-mvvm' do 5 | pod 'SwiftyJSON' 6 | pod 'Alamofire', '~> 4.0' 7 | end 8 | 9 | post_install do |installer| 10 | installer.pods_project.targets.each do |target| 11 | target.build_configurations.each do |config| 12 | config.build_settings['SWIFT_VERSION'] = '3.0' 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /swift-mvvm/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.0.1) 3 | - SwiftyJSON (3.1.1) 4 | 5 | DEPENDENCIES: 6 | - Alamofire (~> 4.0) 7 | - SwiftyJSON 8 | 9 | SPEC CHECKSUMS: 10 | Alamofire: 7682d43245de14874acd142ec137b144aa1dd335 11 | SwiftyJSON: f0be2e604f83e8405a624e9f891898bf6ed4e019 12 | 13 | PODFILE CHECKSUM: 53075ccc16f565c7c7673fe7e66201fd5dc8a2cd 14 | 15 | COCOAPODS: 1.1.1 16 | -------------------------------------------------------------------------------- /swift-mvvm/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 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct 28 | /// URL requests. 29 | public protocol URLConvertible { 30 | /// Returns a URL that conforms to RFC 2396 or throws an `Error`. 31 | /// 32 | /// - throws: An `Error` if the type cannot be converted to a `URL`. 33 | /// 34 | /// - returns: A URL or throws an `Error`. 35 | func asURL() throws -> URL 36 | } 37 | 38 | extension String: URLConvertible { 39 | /// Returns a URL if `self` represents a valid URL string that conforms to RFC 2396 or throws an `AFError`. 40 | /// 41 | /// - throws: An `AFError.invalidURL` if `self` is not a valid URL string. 42 | /// 43 | /// - returns: A URL or throws an `AFError`. 44 | public func asURL() throws -> URL { 45 | guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) } 46 | return url 47 | } 48 | } 49 | 50 | extension URL: URLConvertible { 51 | /// Returns self. 52 | public func asURL() throws -> URL { return self } 53 | } 54 | 55 | extension URLComponents: URLConvertible { 56 | /// Returns a URL if `url` is not nil, otherise throws an `Error`. 57 | /// 58 | /// - throws: An `AFError.invalidURL` if `url` is `nil`. 59 | /// 60 | /// - returns: A URL or throws an `AFError`. 61 | public func asURL() throws -> URL { 62 | guard let url = url else { throw AFError.invalidURL(url: self) } 63 | return url 64 | } 65 | } 66 | 67 | // MARK: - 68 | 69 | /// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. 70 | public protocol URLRequestConvertible { 71 | /// Returns a URL request or throws if an `Error` was encountered. 72 | /// 73 | /// - throws: An `Error` if the underlying `URLRequest` is `nil`. 74 | /// 75 | /// - returns: A URL request. 76 | func asURLRequest() throws -> URLRequest 77 | } 78 | 79 | extension URLRequestConvertible { 80 | /// The URL request. 81 | public var urlRequest: URLRequest? { return try? asURLRequest() } 82 | } 83 | 84 | extension URLRequest: URLRequestConvertible { 85 | /// Returns a URL request or throws if an `Error` was encountered. 86 | public func asURLRequest() throws -> URLRequest { return self } 87 | } 88 | 89 | // MARK: - 90 | 91 | extension URLRequest { 92 | /// Creates an instance with the specified `method`, `urlString` and `headers`. 93 | /// 94 | /// - parameter url: The URL. 95 | /// - parameter method: The HTTP method. 96 | /// - parameter headers: The HTTP headers. `nil` by default. 97 | /// 98 | /// - returns: The new `URLRequest` instance. 99 | public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { 100 | let url = try url.asURL() 101 | 102 | self.init(url: url) 103 | 104 | httpMethod = method.rawValue 105 | 106 | if let headers = headers { 107 | for (headerField, headerValue) in headers { 108 | setValue(headerValue, forHTTPHeaderField: headerField) 109 | } 110 | } 111 | } 112 | 113 | func adapt(using adapter: RequestAdapter?) throws -> URLRequest { 114 | guard let adapter = adapter else { return self } 115 | return try adapter.adapt(self) 116 | } 117 | } 118 | 119 | // MARK: - Data Request 120 | 121 | /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`, 122 | /// `method`, `parameters`, `encoding` and `headers`. 123 | /// 124 | /// - parameter url: The URL. 125 | /// - parameter method: The HTTP method. `.get` by default. 126 | /// - parameter parameters: The parameters. `nil` by default. 127 | /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. 128 | /// - parameter headers: The HTTP headers. `nil` by default. 129 | /// 130 | /// - returns: The created `DataRequest`. 131 | @discardableResult 132 | public func request( 133 | _ url: URLConvertible, 134 | method: HTTPMethod = .get, 135 | parameters: Parameters? = nil, 136 | encoding: ParameterEncoding = URLEncoding.default, 137 | headers: HTTPHeaders? = nil) 138 | -> DataRequest 139 | { 140 | return SessionManager.default.request( 141 | url, 142 | method: method, 143 | parameters: parameters, 144 | encoding: encoding, 145 | headers: headers 146 | ) 147 | } 148 | 149 | /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the 150 | /// specified `urlRequest`. 151 | /// 152 | /// - parameter urlRequest: The URL request 153 | /// 154 | /// - returns: The created `DataRequest`. 155 | @discardableResult 156 | public func request(_ urlRequest: URLRequestConvertible) -> DataRequest { 157 | return SessionManager.default.request(urlRequest) 158 | } 159 | 160 | // MARK: - Download Request 161 | 162 | // MARK: URL Request 163 | 164 | /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of the specified `url`, 165 | /// `method`, `parameters`, `encoding`, `headers` and save them to the `destination`. 166 | /// 167 | /// If `destination` is not specified, the contents will remain in the temporary location determined by the 168 | /// underlying URL session. 169 | /// 170 | /// - parameter url: The URL. 171 | /// - parameter method: The HTTP method. `.get` by default. 172 | /// - parameter parameters: The parameters. `nil` by default. 173 | /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. 174 | /// - parameter headers: The HTTP headers. `nil` by default. 175 | /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. 176 | /// 177 | /// - returns: The created `DownloadRequest`. 178 | @discardableResult 179 | public func download( 180 | _ url: URLConvertible, 181 | method: HTTPMethod = .get, 182 | parameters: Parameters? = nil, 183 | encoding: ParameterEncoding = URLEncoding.default, 184 | headers: HTTPHeaders? = nil, 185 | to destination: DownloadRequest.DownloadFileDestination? = nil) 186 | -> DownloadRequest 187 | { 188 | return SessionManager.default.download( 189 | url, 190 | method: method, 191 | parameters: parameters, 192 | encoding: encoding, 193 | headers: headers, 194 | to: destination 195 | ) 196 | } 197 | 198 | /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the 199 | /// specified `urlRequest` and save them to the `destination`. 200 | /// 201 | /// If `destination` is not specified, the contents will remain in the temporary location determined by the 202 | /// underlying URL session. 203 | /// 204 | /// - parameter urlRequest: The URL request. 205 | /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. 206 | /// 207 | /// - returns: The created `DownloadRequest`. 208 | @discardableResult 209 | public func download( 210 | _ urlRequest: URLRequestConvertible, 211 | to destination: DownloadRequest.DownloadFileDestination? = nil) 212 | -> DownloadRequest 213 | { 214 | return SessionManager.default.download(urlRequest, to: destination) 215 | } 216 | 217 | // MARK: Resume Data 218 | 219 | /// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a 220 | /// previous request cancellation to retrieve the contents of the original request and save them to the `destination`. 221 | /// 222 | /// If `destination` is not specified, the contents will remain in the temporary location determined by the 223 | /// underlying URL session. 224 | /// 225 | /// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask` 226 | /// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional 227 | /// information. 228 | /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. 229 | /// 230 | /// - returns: The created `DownloadRequest`. 231 | @discardableResult 232 | public func download( 233 | resumingWith resumeData: Data, 234 | to destination: DownloadRequest.DownloadFileDestination? = nil) 235 | -> DownloadRequest 236 | { 237 | return SessionManager.default.download(resumingWith: resumeData, to: destination) 238 | } 239 | 240 | // MARK: - Upload Request 241 | 242 | // MARK: File 243 | 244 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` 245 | /// for uploading the `file`. 246 | /// 247 | /// - parameter file: The file to upload. 248 | /// - parameter url: The URL. 249 | /// - parameter method: The HTTP method. `.post` by default. 250 | /// - parameter headers: The HTTP headers. `nil` by default. 251 | /// 252 | /// - returns: The created `UploadRequest`. 253 | @discardableResult 254 | public func upload( 255 | _ fileURL: URL, 256 | to url: URLConvertible, 257 | method: HTTPMethod = .post, 258 | headers: HTTPHeaders? = nil) 259 | -> UploadRequest 260 | { 261 | return SessionManager.default.upload(fileURL, to: url, method: method, headers: headers) 262 | } 263 | 264 | /// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for 265 | /// uploading the `file`. 266 | /// 267 | /// - parameter file: The file to upload. 268 | /// - parameter urlRequest: The URL request. 269 | /// 270 | /// - returns: The created `UploadRequest`. 271 | @discardableResult 272 | public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest { 273 | return SessionManager.default.upload(fileURL, with: urlRequest) 274 | } 275 | 276 | // MARK: Data 277 | 278 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` 279 | /// for uploading the `data`. 280 | /// 281 | /// - parameter data: The data to upload. 282 | /// - parameter url: The URL. 283 | /// - parameter method: The HTTP method. `.post` by default. 284 | /// - parameter headers: The HTTP headers. `nil` by default. 285 | /// 286 | /// - returns: The created `UploadRequest`. 287 | @discardableResult 288 | public func upload( 289 | _ data: Data, 290 | to url: URLConvertible, 291 | method: HTTPMethod = .post, 292 | headers: HTTPHeaders? = nil) 293 | -> UploadRequest 294 | { 295 | return SessionManager.default.upload(data, to: url, method: method, headers: headers) 296 | } 297 | 298 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for 299 | /// uploading the `data`. 300 | /// 301 | /// - parameter data: The data to upload. 302 | /// - parameter urlRequest: The URL request. 303 | /// 304 | /// - returns: The created `UploadRequest`. 305 | @discardableResult 306 | public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest { 307 | return SessionManager.default.upload(data, with: urlRequest) 308 | } 309 | 310 | // MARK: InputStream 311 | 312 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` 313 | /// for uploading the `stream`. 314 | /// 315 | /// - parameter stream: The stream to upload. 316 | /// - parameter url: The URL. 317 | /// - parameter method: The HTTP method. `.post` by default. 318 | /// - parameter headers: The HTTP headers. `nil` by default. 319 | /// 320 | /// - returns: The created `UploadRequest`. 321 | @discardableResult 322 | public func upload( 323 | _ stream: InputStream, 324 | to url: URLConvertible, 325 | method: HTTPMethod = .post, 326 | headers: HTTPHeaders? = nil) 327 | -> UploadRequest 328 | { 329 | return SessionManager.default.upload(stream, to: url, method: method, headers: headers) 330 | } 331 | 332 | /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for 333 | /// uploading the `stream`. 334 | /// 335 | /// - parameter urlRequest: The URL request. 336 | /// - parameter stream: The stream to upload. 337 | /// 338 | /// - returns: The created `UploadRequest`. 339 | @discardableResult 340 | public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest { 341 | return SessionManager.default.upload(stream, with: urlRequest) 342 | } 343 | 344 | // MARK: MultipartFormData 345 | 346 | /// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls 347 | /// `encodingCompletion` with new `UploadRequest` using the `url`, `method` and `headers`. 348 | /// 349 | /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative 350 | /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most 351 | /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to 352 | /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory 353 | /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be 354 | /// used for larger payloads such as video content. 355 | /// 356 | /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory 357 | /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, 358 | /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk 359 | /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding 360 | /// technique was used. 361 | /// 362 | /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. 363 | /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. 364 | /// `multipartFormDataEncodingMemoryThreshold` by default. 365 | /// - parameter url: The URL. 366 | /// - parameter method: The HTTP method. `.post` by default. 367 | /// - parameter headers: The HTTP headers. `nil` by default. 368 | /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. 369 | public func upload( 370 | multipartFormData: @escaping (MultipartFormData) -> Void, 371 | usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, 372 | to url: URLConvertible, 373 | method: HTTPMethod = .post, 374 | headers: HTTPHeaders? = nil, 375 | encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) 376 | { 377 | return SessionManager.default.upload( 378 | multipartFormData: multipartFormData, 379 | usingThreshold: encodingMemoryThreshold, 380 | to: url, 381 | method: method, 382 | headers: headers, 383 | encodingCompletion: encodingCompletion 384 | ) 385 | } 386 | 387 | /// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and 388 | /// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`. 389 | /// 390 | /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative 391 | /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most 392 | /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to 393 | /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory 394 | /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be 395 | /// used for larger payloads such as video content. 396 | /// 397 | /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory 398 | /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, 399 | /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk 400 | /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding 401 | /// technique was used. 402 | /// 403 | /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. 404 | /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. 405 | /// `multipartFormDataEncodingMemoryThreshold` by default. 406 | /// - parameter urlRequest: The URL request. 407 | /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. 408 | public func upload( 409 | multipartFormData: @escaping (MultipartFormData) -> Void, 410 | usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, 411 | with urlRequest: URLRequestConvertible, 412 | encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) 413 | { 414 | return SessionManager.default.upload( 415 | multipartFormData: multipartFormData, 416 | usingThreshold: encodingMemoryThreshold, 417 | with: urlRequest, 418 | encodingCompletion: encodingCompletion 419 | ) 420 | } 421 | 422 | #if !os(watchOS) 423 | 424 | // MARK: - Stream Request 425 | 426 | // MARK: Hostname and Port 427 | 428 | /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname` 429 | /// and `port`. 430 | /// 431 | /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 432 | /// 433 | /// - parameter hostName: The hostname of the server to connect to. 434 | /// - parameter port: The port of the server to connect to. 435 | /// 436 | /// - returns: The created `StreamRequest`. 437 | @discardableResult 438 | public func stream(withHostName hostName: String, port: Int) -> StreamRequest { 439 | return SessionManager.default.stream(withHostName: hostName, port: port) 440 | } 441 | 442 | // MARK: NetService 443 | 444 | /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`. 445 | /// 446 | /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 447 | /// 448 | /// - parameter netService: The net service used to identify the endpoint. 449 | /// 450 | /// - returns: The created `StreamRequest`. 451 | @discardableResult 452 | public func stream(with netService: NetService) -> StreamRequest { 453 | return SessionManager.default.stream(with: netService) 454 | } 455 | 456 | #endif 457 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) } 30 | static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) } 31 | static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) } 32 | static var background: DispatchQueue { return DispatchQueue.global(qos: .background) } 33 | 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | 38 | func syncResult(_ closure: () -> T) -> T { 39 | var result: T! 40 | sync { result = closure() } 41 | return result 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/NetworkReachabilityManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManager.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #if !os(watchOS) 26 | 27 | import Foundation 28 | import SystemConfiguration 29 | 30 | /// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN and 31 | /// WiFi network interfaces. 32 | /// 33 | /// Reachability can be used to determine background information about why a network operation failed, or to retry 34 | /// network requests when a connection is established. It should not be used to prevent a user from initiating a network 35 | /// request, as it's possible that an initial request may be required to establish reachability. 36 | open class NetworkReachabilityManager { 37 | /** 38 | Defines the various states of network reachability. 39 | 40 | - Unknown: It is unknown whether the network is reachable. 41 | - NotReachable: The network is not reachable. 42 | - ReachableOnWWAN: The network is reachable over the WWAN connection. 43 | - ReachableOnWiFi: The network is reachable over the WiFi connection. 44 | */ 45 | 46 | 47 | /// Defines the various states of network reachability. 48 | /// 49 | /// - unknown: It is unknown whether the network is reachable. 50 | /// - notReachable: The network is not reachable. 51 | /// - reachable: The network is reachable. 52 | public enum NetworkReachabilityStatus { 53 | case unknown 54 | case notReachable 55 | case reachable(ConnectionType) 56 | } 57 | 58 | /// Defines the various connection types detected by reachability flags. 59 | /// 60 | /// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi. 61 | /// - wwan: The connection type is a WWAN connection. 62 | public enum ConnectionType { 63 | case ethernetOrWiFi 64 | case wwan 65 | } 66 | 67 | /// A closure executed when the network reachability status changes. The closure takes a single argument: the 68 | /// network reachability status. 69 | public typealias Listener = (NetworkReachabilityStatus) -> Void 70 | 71 | // MARK: - Properties 72 | 73 | /// Whether the network is currently reachable. 74 | open var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } 75 | 76 | /// Whether the network is currently reachable over the WWAN interface. 77 | open var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) } 78 | 79 | /// Whether the network is currently reachable over Ethernet or WiFi interface. 80 | open var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) } 81 | 82 | /// The current network reachability status. 83 | open var networkReachabilityStatus: NetworkReachabilityStatus { 84 | guard let flags = self.flags else { return .unknown } 85 | return networkReachabilityStatusForFlags(flags) 86 | } 87 | 88 | /// The dispatch queue to execute the `listener` closure on. 89 | open var listenerQueue: DispatchQueue = DispatchQueue.main 90 | 91 | /// A closure executed when the network reachability status changes. 92 | open var listener: Listener? 93 | 94 | private var flags: SCNetworkReachabilityFlags? { 95 | var flags = SCNetworkReachabilityFlags() 96 | 97 | if SCNetworkReachabilityGetFlags(reachability, &flags) { 98 | return flags 99 | } 100 | 101 | return nil 102 | } 103 | 104 | private let reachability: SCNetworkReachability 105 | private var previousFlags: SCNetworkReachabilityFlags 106 | 107 | // MARK: - Initialization 108 | 109 | /// Creates a `NetworkReachabilityManager` instance with the specified host. 110 | /// 111 | /// - parameter host: The host used to evaluate network reachability. 112 | /// 113 | /// - returns: The new `NetworkReachabilityManager` instance. 114 | public convenience init?(host: String) { 115 | guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } 116 | self.init(reachability: reachability) 117 | } 118 | 119 | /// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0. 120 | /// 121 | /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing 122 | /// status of the device, both IPv4 and IPv6. 123 | /// 124 | /// - returns: The new `NetworkReachabilityManager` instance. 125 | public convenience init?() { 126 | var address = sockaddr_in() 127 | address.sin_len = UInt8(MemoryLayout.size) 128 | address.sin_family = sa_family_t(AF_INET) 129 | 130 | guard let reachability = withUnsafePointer(to: &address, { pointer in 131 | return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout.size) { 132 | return SCNetworkReachabilityCreateWithAddress(nil, $0) 133 | } 134 | }) else { return nil } 135 | 136 | self.init(reachability: reachability) 137 | } 138 | 139 | private init(reachability: SCNetworkReachability) { 140 | self.reachability = reachability 141 | self.previousFlags = SCNetworkReachabilityFlags() 142 | } 143 | 144 | deinit { 145 | stopListening() 146 | } 147 | 148 | // MARK: - Listening 149 | 150 | /// Starts listening for changes in network reachability status. 151 | /// 152 | /// - returns: `true` if listening was started successfully, `false` otherwise. 153 | @discardableResult 154 | open func startListening() -> Bool { 155 | var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) 156 | context.info = Unmanaged.passUnretained(self).toOpaque() 157 | 158 | let callbackEnabled = SCNetworkReachabilitySetCallback( 159 | reachability, 160 | { (_, flags, info) in 161 | let reachability = Unmanaged.fromOpaque(info!).takeUnretainedValue() 162 | reachability.notifyListener(flags) 163 | }, 164 | &context 165 | ) 166 | 167 | let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue) 168 | 169 | listenerQueue.async { 170 | self.previousFlags = SCNetworkReachabilityFlags() 171 | self.notifyListener(self.flags ?? SCNetworkReachabilityFlags()) 172 | } 173 | 174 | return callbackEnabled && queueEnabled 175 | } 176 | 177 | /// Stops listening for changes in network reachability status. 178 | open func stopListening() { 179 | SCNetworkReachabilitySetCallback(reachability, nil, nil) 180 | SCNetworkReachabilitySetDispatchQueue(reachability, nil) 181 | } 182 | 183 | // MARK: - Internal - Listener Notification 184 | 185 | func notifyListener(_ flags: SCNetworkReachabilityFlags) { 186 | guard previousFlags != flags else { return } 187 | previousFlags = flags 188 | 189 | listener?(networkReachabilityStatusForFlags(flags)) 190 | } 191 | 192 | // MARK: - Internal - Network Reachability Status 193 | 194 | func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus { 195 | guard flags.contains(.reachable) else { return .notReachable } 196 | 197 | var networkStatus: NetworkReachabilityStatus = .notReachable 198 | 199 | if !flags.contains(.connectionRequired) { networkStatus = .reachable(.ethernetOrWiFi) } 200 | 201 | if flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) { 202 | if !flags.contains(.interventionRequired) { networkStatus = .reachable(.ethernetOrWiFi) } 203 | } 204 | 205 | #if os(iOS) 206 | if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } 207 | #endif 208 | 209 | return networkStatus 210 | } 211 | } 212 | 213 | // MARK: - 214 | 215 | extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} 216 | 217 | /// Returns whether the two network reachability status values are equal. 218 | /// 219 | /// - parameter lhs: The left-hand side value to compare. 220 | /// - parameter rhs: The right-hand side value to compare. 221 | /// 222 | /// - returns: `true` if the two values are equal, `false` otherwise. 223 | public func ==( 224 | lhs: NetworkReachabilityManager.NetworkReachabilityStatus, 225 | rhs: NetworkReachabilityManager.NetworkReachabilityStatus) 226 | -> Bool 227 | { 228 | switch (lhs, rhs) { 229 | case (.unknown, .unknown): 230 | return true 231 | case (.notReachable, .notReachable): 232 | return true 233 | case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)): 234 | return lhsConnectionType == rhsConnectionType 235 | default: 236 | return false 237 | } 238 | } 239 | 240 | #endif 241 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Notification.Name { 28 | /// Used as a namespace for all `URLSessionTask` related notifications. 29 | public struct Task { 30 | /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`. 31 | public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume") 32 | 33 | /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`. 34 | public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend") 35 | 36 | /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`. 37 | public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel") 38 | 39 | /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`. 40 | public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete") 41 | } 42 | } 43 | 44 | // MARK: - 45 | 46 | extension Notification { 47 | /// Used as a namespace for all `Notification` user info dictionary keys. 48 | public struct Key { 49 | /// User info dictionary key representing the `URLSessionTask` associated with the notification. 50 | public static let Task = "org.alamofire.notification.key.task" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/ParameterEncoding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParameterEncoding.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// HTTP method definitions. 28 | /// 29 | /// See https://tools.ietf.org/html/rfc7231#section-4.3 30 | public enum HTTPMethod: String { 31 | case options = "OPTIONS" 32 | case get = "GET" 33 | case head = "HEAD" 34 | case post = "POST" 35 | case put = "PUT" 36 | case patch = "PATCH" 37 | case delete = "DELETE" 38 | case trace = "TRACE" 39 | case connect = "CONNECT" 40 | } 41 | 42 | // MARK: - 43 | 44 | /// A dictionary of parameters to apply to a `URLRequest`. 45 | public typealias Parameters = [String: Any] 46 | 47 | /// A type used to define how a set of parameters are applied to a `URLRequest`. 48 | public protocol ParameterEncoding { 49 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 50 | /// 51 | /// - parameter urlRequest: The request to have parameters applied. 52 | /// - parameter parameters: The parameters to apply. 53 | /// 54 | /// - throws: An `AFError.parameterEncodingFailed` error if encoding fails. 55 | /// 56 | /// - returns: The encoded request. 57 | func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest 58 | } 59 | 60 | // MARK: - 61 | 62 | /// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP 63 | /// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as 64 | /// the HTTP body depends on the destination of the encoding. 65 | /// 66 | /// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to 67 | /// `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification for how to encode 68 | /// collection types, the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending 69 | /// the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`). 70 | public struct URLEncoding: ParameterEncoding { 71 | 72 | // MARK: Helper Types 73 | 74 | /// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the 75 | /// resulting URL request. 76 | /// 77 | /// - methodDependent: Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` 78 | /// requests and sets as the HTTP body for requests with any other HTTP method. 79 | /// - queryString: Sets or appends encoded query string result to existing query string. 80 | /// - httpBody: Sets encoded query string result as the HTTP body of the URL request. 81 | public enum Destination { 82 | case methodDependent, queryString, httpBody 83 | } 84 | 85 | // MARK: Properties 86 | 87 | /// Returns a default `URLEncoding` instance. 88 | public static var `default`: URLEncoding { return URLEncoding() } 89 | 90 | /// Returns a `URLEncoding` instance with a `.methodDependent` destination. 91 | public static var methodDependent: URLEncoding { return URLEncoding() } 92 | 93 | /// Returns a `URLEncoding` instance with a `.queryString` destination. 94 | public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) } 95 | 96 | /// Returns a `URLEncoding` instance with an `.httpBody` destination. 97 | public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) } 98 | 99 | /// The destination defining where the encoded query string is to be applied to the URL request. 100 | public let destination: Destination 101 | 102 | // MARK: Initialization 103 | 104 | /// Creates a `URLEncoding` instance using the specified destination. 105 | /// 106 | /// - parameter destination: The destination defining where the encoded query string is to be applied. 107 | /// 108 | /// - returns: The new `URLEncoding` instance. 109 | public init(destination: Destination = .methodDependent) { 110 | self.destination = destination 111 | } 112 | 113 | // MARK: Encoding 114 | 115 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 116 | /// 117 | /// - parameter urlRequest: The request to have parameters applied. 118 | /// - parameter parameters: The parameters to apply. 119 | /// 120 | /// - throws: An `Error` if the encoding process encounters an error. 121 | /// 122 | /// - returns: The encoded request. 123 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { 124 | var urlRequest = try urlRequest.asURLRequest() 125 | 126 | guard let parameters = parameters else { return urlRequest } 127 | 128 | if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) { 129 | guard let url = urlRequest.url else { 130 | throw AFError.parameterEncodingFailed(reason: .missingURL) 131 | } 132 | 133 | if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty { 134 | let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) 135 | urlComponents.percentEncodedQuery = percentEncodedQuery 136 | urlRequest.url = urlComponents.url 137 | } 138 | } else { 139 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 140 | urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type") 141 | } 142 | 143 | urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false) 144 | } 145 | 146 | return urlRequest 147 | } 148 | 149 | /// Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. 150 | /// 151 | /// - parameter key: The key of the query component. 152 | /// - parameter value: The value of the query component. 153 | /// 154 | /// - returns: The percent-escaped, URL encoded query string components. 155 | public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] { 156 | var components: [(String, String)] = [] 157 | 158 | if let dictionary = value as? [String: Any] { 159 | for (nestedKey, value) in dictionary { 160 | components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) 161 | } 162 | } else if let array = value as? [Any] { 163 | for value in array { 164 | components += queryComponents(fromKey: "\(key)[]", value: value) 165 | } 166 | } else if let value = value as? NSNumber { 167 | if value.isBool { 168 | components.append((escape(key), escape((value.boolValue ? "1" : "0")))) 169 | } else { 170 | components.append((escape(key), escape("\(value)"))) 171 | } 172 | } else if let bool = value as? Bool { 173 | components.append((escape(key), escape((bool ? "1" : "0")))) 174 | } else { 175 | components.append((escape(key), escape("\(value)"))) 176 | } 177 | 178 | return components 179 | } 180 | 181 | /// Returns a percent-escaped string following RFC 3986 for a query string key or value. 182 | /// 183 | /// RFC 3986 states that the following characters are "reserved" characters. 184 | /// 185 | /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/" 186 | /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" 187 | /// 188 | /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow 189 | /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" 190 | /// should be percent-escaped in the query string. 191 | /// 192 | /// - parameter string: The string to be percent-escaped. 193 | /// 194 | /// - returns: The percent-escaped string. 195 | public func escape(_ string: String) -> String { 196 | let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 197 | let subDelimitersToEncode = "!$&'()*+,;=" 198 | 199 | var allowedCharacterSet = CharacterSet.urlQueryAllowed 200 | allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") 201 | 202 | return string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string 203 | } 204 | 205 | private func query(_ parameters: [String: Any]) -> String { 206 | var components: [(String, String)] = [] 207 | 208 | for key in parameters.keys.sorted(by: <) { 209 | let value = parameters[key]! 210 | components += queryComponents(fromKey: key, value: value) 211 | } 212 | 213 | return components.map { "\($0)=\($1)" }.joined(separator: "&") 214 | } 215 | 216 | private func encodesParametersInURL(with method: HTTPMethod) -> Bool { 217 | switch destination { 218 | case .queryString: 219 | return true 220 | case .httpBody: 221 | return false 222 | default: 223 | break 224 | } 225 | 226 | switch method { 227 | case .get, .head, .delete: 228 | return true 229 | default: 230 | return false 231 | } 232 | } 233 | } 234 | 235 | // MARK: - 236 | 237 | /// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the 238 | /// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. 239 | public struct JSONEncoding: ParameterEncoding { 240 | 241 | // MARK: Properties 242 | 243 | /// Returns a `JSONEncoding` instance with default writing options. 244 | public static var `default`: JSONEncoding { return JSONEncoding() } 245 | 246 | /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options. 247 | public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) } 248 | 249 | /// The options for writing the parameters as JSON data. 250 | public let options: JSONSerialization.WritingOptions 251 | 252 | // MARK: Initialization 253 | 254 | /// Creates a `JSONEncoding` instance using the specified options. 255 | /// 256 | /// - parameter options: The options for writing the parameters as JSON data. 257 | /// 258 | /// - returns: The new `JSONEncoding` instance. 259 | public init(options: JSONSerialization.WritingOptions = []) { 260 | self.options = options 261 | } 262 | 263 | // MARK: Encoding 264 | 265 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 266 | /// 267 | /// - parameter urlRequest: The request to have parameters applied. 268 | /// - parameter parameters: The parameters to apply. 269 | /// 270 | /// - throws: An `Error` if the encoding process encounters an error. 271 | /// 272 | /// - returns: The encoded request. 273 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { 274 | var urlRequest = try urlRequest.asURLRequest() 275 | 276 | guard let parameters = parameters else { return urlRequest } 277 | 278 | do { 279 | let data = try JSONSerialization.data(withJSONObject: parameters, options: options) 280 | 281 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 282 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 283 | } 284 | 285 | urlRequest.httpBody = data 286 | } catch { 287 | throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) 288 | } 289 | 290 | return urlRequest 291 | } 292 | } 293 | 294 | // MARK: - 295 | 296 | /// Uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the 297 | /// associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header 298 | /// field of an encoded request is set to `application/x-plist`. 299 | public struct PropertyListEncoding: ParameterEncoding { 300 | 301 | // MARK: Properties 302 | 303 | /// Returns a default `PropertyListEncoding` instance. 304 | public static var `default`: PropertyListEncoding { return PropertyListEncoding() } 305 | 306 | /// Returns a `PropertyListEncoding` instance with xml formatting and default writing options. 307 | public static var xml: PropertyListEncoding { return PropertyListEncoding(format: .xml) } 308 | 309 | /// Returns a `PropertyListEncoding` instance with binary formatting and default writing options. 310 | public static var binary: PropertyListEncoding { return PropertyListEncoding(format: .binary) } 311 | 312 | /// The property list serialization format. 313 | public let format: PropertyListSerialization.PropertyListFormat 314 | 315 | /// The options for writing the parameters as plist data. 316 | public let options: PropertyListSerialization.WriteOptions 317 | 318 | // MARK: Initialization 319 | 320 | /// Creates a `PropertyListEncoding` instance using the specified format and options. 321 | /// 322 | /// - parameter format: The property list serialization format. 323 | /// - parameter options: The options for writing the parameters as plist data. 324 | /// 325 | /// - returns: The new `PropertyListEncoding` instance. 326 | public init( 327 | format: PropertyListSerialization.PropertyListFormat = .xml, 328 | options: PropertyListSerialization.WriteOptions = 0) 329 | { 330 | self.format = format 331 | self.options = options 332 | } 333 | 334 | // MARK: Encoding 335 | 336 | /// Creates a URL request by encoding parameters and applying them onto an existing request. 337 | /// 338 | /// - parameter urlRequest: The request to have parameters applied. 339 | /// - parameter parameters: The parameters to apply. 340 | /// 341 | /// - throws: An `Error` if the encoding process encounters an error. 342 | /// 343 | /// - returns: The encoded request. 344 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { 345 | var urlRequest = try urlRequest.asURLRequest() 346 | 347 | guard let parameters = parameters else { return urlRequest } 348 | 349 | do { 350 | let data = try PropertyListSerialization.data( 351 | fromPropertyList: parameters, 352 | format: format, 353 | options: options 354 | ) 355 | 356 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { 357 | urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") 358 | } 359 | 360 | urlRequest.httpBody = data 361 | } catch { 362 | throw AFError.parameterEncodingFailed(reason: .propertyListEncodingFailed(error: error)) 363 | } 364 | 365 | return urlRequest 366 | } 367 | } 368 | 369 | // MARK: - 370 | 371 | extension NSNumber { 372 | fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) } 373 | } 374 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Used to store all data associated with an non-serialized response of a data or upload request. 28 | public struct DefaultDataResponse { 29 | /// The URL request sent to the server. 30 | public let request: URLRequest? 31 | 32 | /// The server's response to the URL request. 33 | public let response: HTTPURLResponse? 34 | 35 | /// The data returned by the server. 36 | public let data: Data? 37 | 38 | /// The error encountered while executing or validating the request. 39 | public let error: Error? 40 | 41 | var _metrics: AnyObject? 42 | 43 | init(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) { 44 | self.request = request 45 | self.response = response 46 | self.data = data 47 | self.error = error 48 | } 49 | } 50 | 51 | // MARK: - 52 | 53 | /// Used to store all data associated with a serialized response of a data or upload request. 54 | public struct DataResponse { 55 | /// The URL request sent to the server. 56 | public let request: URLRequest? 57 | 58 | /// The server's response to the URL request. 59 | public let response: HTTPURLResponse? 60 | 61 | /// The data returned by the server. 62 | public let data: Data? 63 | 64 | /// The result of response serialization. 65 | public let result: Result 66 | 67 | /// The timeline of the complete lifecycle of the `Request`. 68 | public let timeline: Timeline 69 | 70 | var _metrics: AnyObject? 71 | 72 | /// Creates a `DataResponse` instance with the specified parameters derived from response serialization. 73 | /// 74 | /// - parameter request: The URL request sent to the server. 75 | /// - parameter response: The server's response to the URL request. 76 | /// - parameter data: The data returned by the server. 77 | /// - parameter result: The result of response serialization. 78 | /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. 79 | /// 80 | /// - returns: The new `DataResponse` instance. 81 | public init( 82 | request: URLRequest?, 83 | response: HTTPURLResponse?, 84 | data: Data?, 85 | result: Result, 86 | timeline: Timeline = Timeline()) 87 | { 88 | self.request = request 89 | self.response = response 90 | self.data = data 91 | self.result = result 92 | self.timeline = timeline 93 | } 94 | } 95 | 96 | // MARK: - 97 | 98 | extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible { 99 | /// The textual representation used when written to an output stream, which includes whether the result was a 100 | /// success or failure. 101 | public var description: String { 102 | return result.debugDescription 103 | } 104 | 105 | /// The debug textual representation used when written to an output stream, which includes the URL request, the URL 106 | /// response, the server data, the response serialization result and the timeline. 107 | public var debugDescription: String { 108 | var output: [String] = [] 109 | 110 | output.append(request != nil ? "[Request]: \(request!)" : "[Request]: nil") 111 | output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") 112 | output.append("[Data]: \(data?.count ?? 0) bytes") 113 | output.append("[Result]: \(result.debugDescription)") 114 | output.append("[Timeline]: \(timeline.debugDescription)") 115 | 116 | return output.joined(separator: "\n") 117 | } 118 | } 119 | 120 | // MARK: - 121 | 122 | /// Used to store all data associated with an non-serialized response of a download request. 123 | public struct DefaultDownloadResponse { 124 | /// The URL request sent to the server. 125 | public let request: URLRequest? 126 | 127 | /// The server's response to the URL request. 128 | public let response: HTTPURLResponse? 129 | 130 | /// The temporary destination URL of the data returned from the server. 131 | public let temporaryURL: URL? 132 | 133 | /// The final destination URL of the data returned from the server if it was moved. 134 | public let destinationURL: URL? 135 | 136 | /// The resume data generated if the request was cancelled. 137 | public let resumeData: Data? 138 | 139 | /// The error encountered while executing or validating the request. 140 | public let error: Error? 141 | 142 | var _metrics: AnyObject? 143 | 144 | init( 145 | request: URLRequest?, 146 | response: HTTPURLResponse?, 147 | temporaryURL: URL?, 148 | destinationURL: URL?, 149 | resumeData: Data?, 150 | error: Error?) 151 | { 152 | self.request = request 153 | self.response = response 154 | self.temporaryURL = temporaryURL 155 | self.destinationURL = destinationURL 156 | self.resumeData = resumeData 157 | self.error = error 158 | } 159 | } 160 | 161 | // MARK: - 162 | 163 | /// Used to store all data associated with a serialized response of a download request. 164 | public struct DownloadResponse { 165 | /// The URL request sent to the server. 166 | public let request: URLRequest? 167 | 168 | /// The server's response to the URL request. 169 | public let response: HTTPURLResponse? 170 | 171 | /// The temporary destination URL of the data returned from the server. 172 | public let temporaryURL: URL? 173 | 174 | /// The final destination URL of the data returned from the server if it was moved. 175 | public let destinationURL: URL? 176 | 177 | /// The resume data generated if the request was cancelled. 178 | public let resumeData: Data? 179 | 180 | /// The result of response serialization. 181 | public let result: Result 182 | 183 | /// The timeline of the complete lifecycle of the request. 184 | public let timeline: Timeline 185 | 186 | var _metrics: AnyObject? 187 | 188 | /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization. 189 | /// 190 | /// - parameter request: The URL request sent to the server. 191 | /// - parameter response: The server's response to the URL request. 192 | /// - parameter temporaryURL: The temporary destination URL of the data returned from the server. 193 | /// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved. 194 | /// - parameter resumeData: The resume data generated if the request was cancelled. 195 | /// - parameter result: The result of response serialization. 196 | /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. 197 | /// 198 | /// - returns: The new `DownloadResponse` instance. 199 | public init( 200 | request: URLRequest?, 201 | response: HTTPURLResponse?, 202 | temporaryURL: URL?, 203 | destinationURL: URL?, 204 | resumeData: Data?, 205 | result: Result, 206 | timeline: Timeline = Timeline()) 207 | { 208 | self.request = request 209 | self.response = response 210 | self.temporaryURL = temporaryURL 211 | self.destinationURL = destinationURL 212 | self.resumeData = resumeData 213 | self.result = result 214 | self.timeline = timeline 215 | } 216 | } 217 | 218 | // MARK: - 219 | 220 | extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible { 221 | /// The textual representation used when written to an output stream, which includes whether the result was a 222 | /// success or failure. 223 | public var description: String { 224 | return result.debugDescription 225 | } 226 | 227 | /// The debug textual representation used when written to an output stream, which includes the URL request, the URL 228 | /// response, the temporary and destination URLs, the resume data, the response serialization result and the 229 | /// timeline. 230 | public var debugDescription: String { 231 | var output: [String] = [] 232 | 233 | output.append(request != nil ? "[Request]: \(request!)" : "[Request]: nil") 234 | output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") 235 | output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")") 236 | output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")") 237 | output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes") 238 | output.append("[Result]: \(result.debugDescription)") 239 | output.append("[Timeline]: \(timeline.debugDescription)") 240 | 241 | return output.joined(separator: "\n") 242 | } 243 | } 244 | 245 | // MARK: - 246 | 247 | protocol Response { 248 | /// The task metrics containing the request / response statistics. 249 | var _metrics: AnyObject? { get set } 250 | mutating func add(_ metrics: AnyObject?) 251 | } 252 | 253 | extension Response { 254 | mutating func add(_ metrics: AnyObject?) { 255 | #if !os(watchOS) 256 | guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return } 257 | guard let metrics = metrics as? URLSessionTaskMetrics else { return } 258 | 259 | _metrics = metrics 260 | #endif 261 | } 262 | } 263 | 264 | // MARK: - 265 | 266 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 267 | extension DefaultDataResponse: Response { 268 | #if !os(watchOS) 269 | /// The task metrics containing the request / response statistics. 270 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 271 | #endif 272 | } 273 | 274 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 275 | extension DataResponse: Response { 276 | #if !os(watchOS) 277 | /// The task metrics containing the request / response statistics. 278 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 279 | #endif 280 | } 281 | 282 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 283 | extension DefaultDownloadResponse: Response { 284 | #if !os(watchOS) 285 | /// The task metrics containing the request / response statistics. 286 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 287 | #endif 288 | } 289 | 290 | @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) 291 | extension DownloadResponse: Response { 292 | #if !os(watchOS) 293 | /// The task metrics containing the request / response statistics. 294 | public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } 295 | #endif 296 | } 297 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/Result.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Used to represent whether a request was successful or encountered an error. 28 | /// 29 | /// - success: The request and all post processing operations were successful resulting in the serialization of the 30 | /// provided associated value. 31 | /// 32 | /// - failure: The request encountered an error resulting in a failure. The associated values are the original data 33 | /// provided by the server as well as the error that caused the failure. 34 | public enum Result { 35 | case success(Value) 36 | case failure(Error) 37 | 38 | /// Returns `true` if the result is a success, `false` otherwise. 39 | public var isSuccess: Bool { 40 | switch self { 41 | case .success: 42 | return true 43 | case .failure: 44 | return false 45 | } 46 | } 47 | 48 | /// Returns `true` if the result is a failure, `false` otherwise. 49 | public var isFailure: Bool { 50 | return !isSuccess 51 | } 52 | 53 | /// Returns the associated value if the result is a success, `nil` otherwise. 54 | public var value: Value? { 55 | switch self { 56 | case .success(let value): 57 | return value 58 | case .failure: 59 | return nil 60 | } 61 | } 62 | 63 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 64 | public var error: Error? { 65 | switch self { 66 | case .success: 67 | return nil 68 | case .failure(let error): 69 | return error 70 | } 71 | } 72 | } 73 | 74 | // MARK: - CustomStringConvertible 75 | 76 | extension Result: CustomStringConvertible { 77 | /// The textual representation used when written to an output stream, which includes whether the result was a 78 | /// success or failure. 79 | public var description: String { 80 | switch self { 81 | case .success: 82 | return "SUCCESS" 83 | case .failure: 84 | return "FAILURE" 85 | } 86 | } 87 | } 88 | 89 | // MARK: - CustomDebugStringConvertible 90 | 91 | extension Result: CustomDebugStringConvertible { 92 | /// The debug textual representation used when written to an output stream, which includes whether the result was a 93 | /// success or failure in addition to the value or error. 94 | public var debugDescription: String { 95 | switch self { 96 | case .success(let value): 97 | return "SUCCESS: \(value)" 98 | case .failure(let error): 99 | return "FAILURE: \(error)" 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/ServerTrustPolicy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerTrustPolicy.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host. 28 | open class ServerTrustPolicyManager { 29 | /// The dictionary of policies mapped to a particular host. 30 | open let policies: [String: ServerTrustPolicy] 31 | 32 | /// Initializes the `ServerTrustPolicyManager` instance with the given policies. 33 | /// 34 | /// Since different servers and web services can have different leaf certificates, intermediate and even root 35 | /// certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This 36 | /// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key 37 | /// pinning for host3 and disabling evaluation for host4. 38 | /// 39 | /// - parameter policies: A dictionary of all policies mapped to a particular host. 40 | /// 41 | /// - returns: The new `ServerTrustPolicyManager` instance. 42 | public init(policies: [String: ServerTrustPolicy]) { 43 | self.policies = policies 44 | } 45 | 46 | /// Returns the `ServerTrustPolicy` for the given host if applicable. 47 | /// 48 | /// By default, this method will return the policy that perfectly matches the given host. Subclasses could override 49 | /// this method and implement more complex mapping implementations such as wildcards. 50 | /// 51 | /// - parameter host: The host to use when searching for a matching policy. 52 | /// 53 | /// - returns: The server trust policy for the given host if found. 54 | open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? { 55 | return policies[host] 56 | } 57 | } 58 | 59 | // MARK: - 60 | 61 | extension URLSession { 62 | private struct AssociatedKeys { 63 | static var managerKey = "URLSession.ServerTrustPolicyManager" 64 | } 65 | 66 | var serverTrustPolicyManager: ServerTrustPolicyManager? { 67 | get { 68 | return objc_getAssociatedObject(self, &AssociatedKeys.managerKey) as? ServerTrustPolicyManager 69 | } 70 | set (manager) { 71 | objc_setAssociatedObject(self, &AssociatedKeys.managerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 72 | } 73 | } 74 | } 75 | 76 | // MARK: - ServerTrustPolicy 77 | 78 | /// The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when 79 | /// connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust 80 | /// with a given set of criteria to determine whether the server trust is valid and the connection should be made. 81 | /// 82 | /// Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) attacks and other 83 | /// vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged 84 | /// to route all communication over an HTTPS connection with pinning enabled. 85 | /// 86 | /// - performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to 87 | /// validate the host provided by the challenge. Applications are encouraged to always 88 | /// validate the host in production environments to guarantee the validity of the server's 89 | /// certificate chain. 90 | /// 91 | /// - pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is 92 | /// considered valid if one of the pinned certificates match one of the server certificates. 93 | /// By validating both the certificate chain and host, certificate pinning provides a very 94 | /// secure form of server trust validation mitigating most, if not all, MITM attacks. 95 | /// Applications are encouraged to always validate the host and require a valid certificate 96 | /// chain in production environments. 97 | /// 98 | /// - pinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered 99 | /// valid if one of the pinned public keys match one of the server certificate public keys. 100 | /// By validating both the certificate chain and host, public key pinning provides a very 101 | /// secure form of server trust validation mitigating most, if not all, MITM attacks. 102 | /// Applications are encouraged to always validate the host and require a valid certificate 103 | /// chain in production environments. 104 | /// 105 | /// - disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid. 106 | /// 107 | /// - customEvaluation: Uses the associated closure to evaluate the validity of the server trust. 108 | public enum ServerTrustPolicy { 109 | case performDefaultEvaluation(validateHost: Bool) 110 | case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) 111 | case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) 112 | case disableEvaluation 113 | case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool) 114 | 115 | // MARK: - Bundle Location 116 | 117 | /// Returns all certificates within the given bundle with a `.cer` file extension. 118 | /// 119 | /// - parameter bundle: The bundle to search for all `.cer` files. 120 | /// 121 | /// - returns: All certificates within the given bundle. 122 | public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] { 123 | var certificates: [SecCertificate] = [] 124 | 125 | let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in 126 | bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil) 127 | }.joined()) 128 | 129 | for path in paths { 130 | if 131 | let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData, 132 | let certificate = SecCertificateCreateWithData(nil, certificateData) 133 | { 134 | certificates.append(certificate) 135 | } 136 | } 137 | 138 | return certificates 139 | } 140 | 141 | /// Returns all public keys within the given bundle with a `.cer` file extension. 142 | /// 143 | /// - parameter bundle: The bundle to search for all `*.cer` files. 144 | /// 145 | /// - returns: All public keys within the given bundle. 146 | public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] { 147 | var publicKeys: [SecKey] = [] 148 | 149 | for certificate in certificates(in: bundle) { 150 | if let publicKey = publicKey(for: certificate) { 151 | publicKeys.append(publicKey) 152 | } 153 | } 154 | 155 | return publicKeys 156 | } 157 | 158 | // MARK: - Evaluation 159 | 160 | /// Evaluates whether the server trust is valid for the given host. 161 | /// 162 | /// - parameter serverTrust: The server trust to evaluate. 163 | /// - parameter host: The host of the challenge protection space. 164 | /// 165 | /// - returns: Whether the server trust is valid. 166 | public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool { 167 | var serverTrustIsValid = false 168 | 169 | switch self { 170 | case let .performDefaultEvaluation(validateHost): 171 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 172 | SecTrustSetPolicies(serverTrust, policy) 173 | 174 | serverTrustIsValid = trustIsValid(serverTrust) 175 | case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost): 176 | if validateCertificateChain { 177 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 178 | SecTrustSetPolicies(serverTrust, policy) 179 | 180 | SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray) 181 | SecTrustSetAnchorCertificatesOnly(serverTrust, true) 182 | 183 | serverTrustIsValid = trustIsValid(serverTrust) 184 | } else { 185 | let serverCertificatesDataArray = certificateData(for: serverTrust) 186 | let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates) 187 | 188 | outerLoop: for serverCertificateData in serverCertificatesDataArray { 189 | for pinnedCertificateData in pinnedCertificatesDataArray { 190 | if serverCertificateData == pinnedCertificateData { 191 | serverTrustIsValid = true 192 | break outerLoop 193 | } 194 | } 195 | } 196 | } 197 | case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost): 198 | var certificateChainEvaluationPassed = true 199 | 200 | if validateCertificateChain { 201 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 202 | SecTrustSetPolicies(serverTrust, policy) 203 | 204 | certificateChainEvaluationPassed = trustIsValid(serverTrust) 205 | } 206 | 207 | if certificateChainEvaluationPassed { 208 | outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] { 209 | for pinnedPublicKey in pinnedPublicKeys as [AnyObject] { 210 | if serverPublicKey.isEqual(pinnedPublicKey) { 211 | serverTrustIsValid = true 212 | break outerLoop 213 | } 214 | } 215 | } 216 | } 217 | case .disableEvaluation: 218 | serverTrustIsValid = true 219 | case let .customEvaluation(closure): 220 | serverTrustIsValid = closure(serverTrust, host) 221 | } 222 | 223 | return serverTrustIsValid 224 | } 225 | 226 | // MARK: - Private - Trust Validation 227 | 228 | private func trustIsValid(_ trust: SecTrust) -> Bool { 229 | var isValid = false 230 | 231 | var result = SecTrustResultType.invalid 232 | let status = SecTrustEvaluate(trust, &result) 233 | 234 | if status == errSecSuccess { 235 | let unspecified = SecTrustResultType.unspecified 236 | let proceed = SecTrustResultType.proceed 237 | 238 | 239 | isValid = result == unspecified || result == proceed 240 | } 241 | 242 | return isValid 243 | } 244 | 245 | // MARK: - Private - Certificate Data 246 | 247 | private func certificateData(for trust: SecTrust) -> [Data] { 248 | var certificates: [SecCertificate] = [] 249 | 250 | for index in 0.. [Data] { 260 | return certificates.map { SecCertificateCopyData($0) as Data } 261 | } 262 | 263 | // MARK: - Private - Public Key Extraction 264 | 265 | private static func publicKeys(for trust: SecTrust) -> [SecKey] { 266 | var publicKeys: [SecKey] = [] 267 | 268 | for index in 0.. SecKey? { 281 | var publicKey: SecKey? 282 | 283 | let policy = SecPolicyCreateBasicX509() 284 | var trust: SecTrust? 285 | let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust) 286 | 287 | if let trust = trust, trustCreationStatus == errSecSuccess { 288 | publicKey = SecTrustCopyPublicKey(trust) 289 | } 290 | 291 | return publicKey 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/TaskDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TaskDelegate.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as 28 | /// executing all operations attached to the serial operation queue upon task completion. 29 | open class TaskDelegate: NSObject { 30 | 31 | // MARK: Properties 32 | 33 | /// The serial operation queue used to execute all operations after the task completes. 34 | open let queue: OperationQueue 35 | 36 | var task: URLSessionTask? { 37 | didSet { reset() } 38 | } 39 | 40 | var data: Data? { return nil } 41 | var error: Error? 42 | 43 | var initialResponseTime: CFAbsoluteTime? 44 | var credential: URLCredential? 45 | var metrics: AnyObject? // URLSessionTaskMetrics 46 | 47 | // MARK: Lifecycle 48 | 49 | init(task: URLSessionTask?) { 50 | self.task = task 51 | 52 | self.queue = { 53 | let operationQueue = OperationQueue() 54 | 55 | operationQueue.maxConcurrentOperationCount = 1 56 | operationQueue.isSuspended = true 57 | operationQueue.qualityOfService = .utility 58 | 59 | return operationQueue 60 | }() 61 | } 62 | 63 | func reset() { 64 | error = nil 65 | initialResponseTime = nil 66 | } 67 | 68 | // MARK: URLSessionTaskDelegate 69 | 70 | var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? 71 | var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? 72 | var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? 73 | var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)? 74 | 75 | @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:) 76 | func urlSession( 77 | _ session: URLSession, 78 | task: URLSessionTask, 79 | willPerformHTTPRedirection response: HTTPURLResponse, 80 | newRequest request: URLRequest, 81 | completionHandler: @escaping (URLRequest?) -> Void) 82 | { 83 | var redirectRequest: URLRequest? = request 84 | 85 | if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { 86 | redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) 87 | } 88 | 89 | completionHandler(redirectRequest) 90 | } 91 | 92 | @objc(URLSession:task:didReceiveChallenge:completionHandler:) 93 | func urlSession( 94 | _ session: URLSession, 95 | task: URLSessionTask, 96 | didReceive challenge: URLAuthenticationChallenge, 97 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 98 | { 99 | var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling 100 | var credential: URLCredential? 101 | 102 | if let taskDidReceiveChallenge = taskDidReceiveChallenge { 103 | (disposition, credential) = taskDidReceiveChallenge(session, task, challenge) 104 | } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 105 | let host = challenge.protectionSpace.host 106 | 107 | if 108 | let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host), 109 | let serverTrust = challenge.protectionSpace.serverTrust 110 | { 111 | if serverTrustPolicy.evaluate(serverTrust, forHost: host) { 112 | disposition = .useCredential 113 | credential = URLCredential(trust: serverTrust) 114 | } else { 115 | disposition = .cancelAuthenticationChallenge 116 | } 117 | } 118 | } else { 119 | if challenge.previousFailureCount > 0 { 120 | disposition = .rejectProtectionSpace 121 | } else { 122 | credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace) 123 | 124 | if credential != nil { 125 | disposition = .useCredential 126 | } 127 | } 128 | } 129 | 130 | completionHandler(disposition, credential) 131 | } 132 | 133 | @objc(URLSession:task:needNewBodyStream:) 134 | func urlSession( 135 | _ session: URLSession, 136 | task: URLSessionTask, 137 | needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) 138 | { 139 | var bodyStream: InputStream? 140 | 141 | if let taskNeedNewBodyStream = taskNeedNewBodyStream { 142 | bodyStream = taskNeedNewBodyStream(session, task) 143 | } 144 | 145 | completionHandler(bodyStream) 146 | } 147 | 148 | @objc(URLSession:task:didCompleteWithError:) 149 | func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 150 | if let taskDidCompleteWithError = taskDidCompleteWithError { 151 | taskDidCompleteWithError(session, task, error) 152 | } else { 153 | if let error = error { 154 | if self.error == nil { self.error = error } 155 | 156 | if 157 | let downloadDelegate = self as? DownloadTaskDelegate, 158 | let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data 159 | { 160 | downloadDelegate.resumeData = resumeData 161 | } 162 | } 163 | 164 | queue.isSuspended = false 165 | } 166 | } 167 | } 168 | 169 | // MARK: - 170 | 171 | class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate { 172 | 173 | // MARK: Properties 174 | 175 | var dataTask: URLSessionDataTask { return task as! URLSessionDataTask } 176 | 177 | override var data: Data? { 178 | if dataStream != nil { 179 | return nil 180 | } else { 181 | return mutableData 182 | } 183 | } 184 | 185 | var progress: Progress 186 | var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 187 | 188 | var dataStream: ((_ data: Data) -> Void)? 189 | 190 | private var totalBytesReceived: Int64 = 0 191 | private var mutableData: Data 192 | 193 | private var expectedContentLength: Int64? 194 | 195 | // MARK: Lifecycle 196 | 197 | override init(task: URLSessionTask?) { 198 | mutableData = Data() 199 | progress = Progress(totalUnitCount: 0) 200 | 201 | super.init(task: task) 202 | } 203 | 204 | override func reset() { 205 | super.reset() 206 | 207 | progress = Progress(totalUnitCount: 0) 208 | totalBytesReceived = 0 209 | mutableData = Data() 210 | expectedContentLength = nil 211 | } 212 | 213 | // MARK: URLSessionDataDelegate 214 | 215 | var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)? 216 | var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? 217 | var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? 218 | var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? 219 | 220 | func urlSession( 221 | _ session: URLSession, 222 | dataTask: URLSessionDataTask, 223 | didReceive response: URLResponse, 224 | completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) 225 | { 226 | var disposition: URLSession.ResponseDisposition = .allow 227 | 228 | expectedContentLength = response.expectedContentLength 229 | 230 | if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { 231 | disposition = dataTaskDidReceiveResponse(session, dataTask, response) 232 | } 233 | 234 | completionHandler(disposition) 235 | } 236 | 237 | func urlSession( 238 | _ session: URLSession, 239 | dataTask: URLSessionDataTask, 240 | didBecome downloadTask: URLSessionDownloadTask) 241 | { 242 | dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask) 243 | } 244 | 245 | func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 246 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 247 | 248 | if let dataTaskDidReceiveData = dataTaskDidReceiveData { 249 | dataTaskDidReceiveData(session, dataTask, data) 250 | } else { 251 | if let dataStream = dataStream { 252 | dataStream(data) 253 | } else { 254 | mutableData.append(data) 255 | } 256 | 257 | let bytesReceived = Int64(data.count) 258 | totalBytesReceived += bytesReceived 259 | let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown 260 | 261 | progress.totalUnitCount = totalBytesExpected 262 | progress.completedUnitCount = totalBytesReceived 263 | 264 | if let progressHandler = progressHandler { 265 | progressHandler.queue.async { progressHandler.closure(self.progress) } 266 | } 267 | } 268 | } 269 | 270 | func urlSession( 271 | _ session: URLSession, 272 | dataTask: URLSessionDataTask, 273 | willCacheResponse proposedResponse: CachedURLResponse, 274 | completionHandler: @escaping (CachedURLResponse?) -> Void) 275 | { 276 | var cachedResponse: CachedURLResponse? = proposedResponse 277 | 278 | if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { 279 | cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse) 280 | } 281 | 282 | completionHandler(cachedResponse) 283 | } 284 | } 285 | 286 | // MARK: - 287 | 288 | class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate { 289 | 290 | // MARK: Properties 291 | 292 | var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask } 293 | 294 | var progress: Progress 295 | var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 296 | 297 | var resumeData: Data? 298 | override var data: Data? { return resumeData } 299 | 300 | var destination: DownloadRequest.DownloadFileDestination? 301 | 302 | var temporaryURL: URL? 303 | var destinationURL: URL? 304 | 305 | var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL } 306 | 307 | // MARK: Lifecycle 308 | 309 | override init(task: URLSessionTask?) { 310 | progress = Progress(totalUnitCount: 0) 311 | super.init(task: task) 312 | } 313 | 314 | override func reset() { 315 | super.reset() 316 | 317 | progress = Progress(totalUnitCount: 0) 318 | resumeData = nil 319 | } 320 | 321 | // MARK: URLSessionDownloadDelegate 322 | 323 | var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)? 324 | var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? 325 | var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? 326 | 327 | func urlSession( 328 | _ session: URLSession, 329 | downloadTask: URLSessionDownloadTask, 330 | didFinishDownloadingTo location: URL) 331 | { 332 | temporaryURL = location 333 | 334 | if let destination = destination { 335 | let result = destination(location, downloadTask.response as! HTTPURLResponse) 336 | let destination = result.destinationURL 337 | let options = result.options 338 | 339 | do { 340 | destinationURL = destination 341 | 342 | if options.contains(.removePreviousFile) { 343 | if FileManager.default.fileExists(atPath: destination.path) { 344 | try FileManager.default.removeItem(at: destination) 345 | } 346 | } 347 | 348 | if options.contains(.createIntermediateDirectories) { 349 | let directory = destination.deletingLastPathComponent() 350 | try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil) 351 | } 352 | 353 | try FileManager.default.moveItem(at: location, to: destination) 354 | } catch { 355 | self.error = error 356 | } 357 | } 358 | } 359 | 360 | func urlSession( 361 | _ session: URLSession, 362 | downloadTask: URLSessionDownloadTask, 363 | didWriteData bytesWritten: Int64, 364 | totalBytesWritten: Int64, 365 | totalBytesExpectedToWrite: Int64) 366 | { 367 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 368 | 369 | if let downloadTaskDidWriteData = downloadTaskDidWriteData { 370 | downloadTaskDidWriteData( 371 | session, 372 | downloadTask, 373 | bytesWritten, 374 | totalBytesWritten, 375 | totalBytesExpectedToWrite 376 | ) 377 | } else { 378 | progress.totalUnitCount = totalBytesExpectedToWrite 379 | progress.completedUnitCount = totalBytesWritten 380 | 381 | if let progressHandler = progressHandler { 382 | progressHandler.queue.async { progressHandler.closure(self.progress) } 383 | } 384 | } 385 | } 386 | 387 | func urlSession( 388 | _ session: URLSession, 389 | downloadTask: URLSessionDownloadTask, 390 | didResumeAtOffset fileOffset: Int64, 391 | expectedTotalBytes: Int64) 392 | { 393 | if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { 394 | downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) 395 | } else { 396 | progress.totalUnitCount = expectedTotalBytes 397 | progress.completedUnitCount = fileOffset 398 | } 399 | } 400 | } 401 | 402 | // MARK: - 403 | 404 | class UploadTaskDelegate: DataTaskDelegate { 405 | 406 | // MARK: Properties 407 | 408 | var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask } 409 | 410 | var uploadProgress: Progress 411 | var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 412 | 413 | // MARK: Lifecycle 414 | 415 | override init(task: URLSessionTask?) { 416 | uploadProgress = Progress(totalUnitCount: 0) 417 | super.init(task: task) 418 | } 419 | 420 | override func reset() { 421 | super.reset() 422 | uploadProgress = Progress(totalUnitCount: 0) 423 | } 424 | 425 | // MARK: URLSessionTaskDelegate 426 | 427 | var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? 428 | 429 | func URLSession( 430 | _ session: URLSession, 431 | task: URLSessionTask, 432 | didSendBodyData bytesSent: Int64, 433 | totalBytesSent: Int64, 434 | totalBytesExpectedToSend: Int64) 435 | { 436 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 437 | 438 | if let taskDidSendBodyData = taskDidSendBodyData { 439 | taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) 440 | } else { 441 | uploadProgress.totalUnitCount = totalBytesExpectedToSend 442 | uploadProgress.completedUnitCount = totalBytesSent 443 | 444 | if let uploadProgressHandler = uploadProgressHandler { 445 | uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) } 446 | } 447 | } 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/Timeline.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timeline.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Responsible for computing the timing metrics for the complete lifecycle of a `Request`. 28 | public struct Timeline { 29 | /// The time the request was initialized. 30 | public let requestStartTime: CFAbsoluteTime 31 | 32 | /// The time the first bytes were received from or sent to the server. 33 | public let initialResponseTime: CFAbsoluteTime 34 | 35 | /// The time when the request was completed. 36 | public let requestCompletedTime: CFAbsoluteTime 37 | 38 | /// The time when the response serialization was completed. 39 | public let serializationCompletedTime: CFAbsoluteTime 40 | 41 | /// The time interval in seconds from the time the request started to the initial response from the server. 42 | public let latency: TimeInterval 43 | 44 | /// The time interval in seconds from the time the request started to the time the request completed. 45 | public let requestDuration: TimeInterval 46 | 47 | /// The time interval in seconds from the time the request completed to the time response serialization completed. 48 | public let serializationDuration: TimeInterval 49 | 50 | /// The time interval in seconds from the time the request started to the time response serialization completed. 51 | public let totalDuration: TimeInterval 52 | 53 | /// Creates a new `Timeline` instance with the specified request times. 54 | /// 55 | /// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`. 56 | /// - parameter initialResponseTime: The time the first bytes were received from or sent to the server. 57 | /// Defaults to `0.0`. 58 | /// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`. 59 | /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults 60 | /// to `0.0`. 61 | /// 62 | /// - returns: The new `Timeline` instance. 63 | public init( 64 | requestStartTime: CFAbsoluteTime = 0.0, 65 | initialResponseTime: CFAbsoluteTime = 0.0, 66 | requestCompletedTime: CFAbsoluteTime = 0.0, 67 | serializationCompletedTime: CFAbsoluteTime = 0.0) 68 | { 69 | self.requestStartTime = requestStartTime 70 | self.initialResponseTime = initialResponseTime 71 | self.requestCompletedTime = requestCompletedTime 72 | self.serializationCompletedTime = serializationCompletedTime 73 | 74 | self.latency = initialResponseTime - requestStartTime 75 | self.requestDuration = requestCompletedTime - requestStartTime 76 | self.serializationDuration = serializationCompletedTime - requestCompletedTime 77 | self.totalDuration = serializationCompletedTime - requestStartTime 78 | } 79 | } 80 | 81 | // MARK: - CustomStringConvertible 82 | 83 | extension Timeline: CustomStringConvertible { 84 | /// The textual representation used when written to an output stream, which includes the latency, the request 85 | /// duration and the total duration. 86 | public var description: String { 87 | let latency = String(format: "%.3f", self.latency) 88 | let requestDuration = String(format: "%.3f", self.requestDuration) 89 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 90 | let totalDuration = String(format: "%.3f", self.totalDuration) 91 | 92 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is 93 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. 94 | let timings = [ 95 | "\"Latency\": " + latency + " secs", 96 | "\"Request Duration\": " + requestDuration + " secs", 97 | "\"Serialization Duration\": " + serializationDuration + " secs", 98 | "\"Total Duration\": " + totalDuration + " secs" 99 | ] 100 | 101 | return "Timeline: { " + timings.joined(separator: ", ") + " }" 102 | } 103 | } 104 | 105 | // MARK: - CustomDebugStringConvertible 106 | 107 | extension Timeline: CustomDebugStringConvertible { 108 | /// The textual representation used when written to an output stream, which includes the request start time, the 109 | /// initial response time, the request completed time, the serialization completed time, the latency, the request 110 | /// duration and the total duration. 111 | public var debugDescription: String { 112 | let requestStartTime = String(format: "%.3f", self.requestStartTime) 113 | let initialResponseTime = String(format: "%.3f", self.initialResponseTime) 114 | let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime) 115 | let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime) 116 | let latency = String(format: "%.3f", self.latency) 117 | let requestDuration = String(format: "%.3f", self.requestDuration) 118 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 119 | let totalDuration = String(format: "%.3f", self.totalDuration) 120 | 121 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is 122 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. 123 | let timings = [ 124 | "\"Request Start Time\": " + requestStartTime, 125 | "\"Initial Response Time\": " + initialResponseTime, 126 | "\"Request Completed Time\": " + requestCompletedTime, 127 | "\"Serialization Completed Time\": " + serializationCompletedTime, 128 | "\"Latency\": " + latency + " secs", 129 | "\"Request Duration\": " + requestDuration + " secs", 130 | "\"Serialization Duration\": " + serializationDuration + " secs", 131 | "\"Total Duration\": " + totalDuration + " secs" 132 | ] 133 | 134 | return "Timeline: { " + timings.joined(separator: ", ") + " }" 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Alamofire/Source/Validation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Validation.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Request { 28 | 29 | // MARK: Helper Types 30 | 31 | fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason 32 | 33 | /// Used to represent whether validation was successful or encountered an error resulting in a failure. 34 | /// 35 | /// - success: The validation was successful. 36 | /// - failure: The validation failed encountering the provided error. 37 | public enum ValidationResult { 38 | case success 39 | case failure(Error) 40 | } 41 | 42 | fileprivate struct MIMEType { 43 | let type: String 44 | let subtype: String 45 | 46 | var isWildcard: Bool { return type == "*" && subtype == "*" } 47 | 48 | init?(_ string: String) { 49 | let components: [String] = { 50 | let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) 51 | let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex) 52 | return split.components(separatedBy: "/") 53 | }() 54 | 55 | if let type = components.first, let subtype = components.last { 56 | self.type = type 57 | self.subtype = subtype 58 | } else { 59 | return nil 60 | } 61 | } 62 | 63 | func matches(_ mime: MIMEType) -> Bool { 64 | switch (type, subtype) { 65 | case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): 66 | return true 67 | default: 68 | return false 69 | } 70 | } 71 | } 72 | 73 | // MARK: Properties 74 | 75 | fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) } 76 | 77 | fileprivate var acceptableContentTypes: [String] { 78 | if let accept = request?.value(forHTTPHeaderField: "Accept") { 79 | return accept.components(separatedBy: ",") 80 | } 81 | 82 | return ["*/*"] 83 | } 84 | 85 | // MARK: Status Code 86 | 87 | fileprivate func validate( 88 | statusCode acceptableStatusCodes: S, 89 | response: HTTPURLResponse) 90 | -> ValidationResult 91 | where S.Iterator.Element == Int 92 | { 93 | if acceptableStatusCodes.contains(response.statusCode) { 94 | return .success 95 | } else { 96 | let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode) 97 | return .failure(AFError.responseValidationFailed(reason: reason)) 98 | } 99 | } 100 | 101 | // MARK: Content Type 102 | 103 | fileprivate func validate( 104 | contentType acceptableContentTypes: S, 105 | response: HTTPURLResponse, 106 | data: Data?) 107 | -> ValidationResult 108 | where S.Iterator.Element == String 109 | { 110 | guard let data = data, data.count > 0 else { return .success } 111 | 112 | guard 113 | let responseContentType = response.mimeType, 114 | let responseMIMEType = MIMEType(responseContentType) 115 | else { 116 | for contentType in acceptableContentTypes { 117 | if let mimeType = MIMEType(contentType), mimeType.isWildcard { 118 | return .success 119 | } 120 | } 121 | 122 | let error: AFError = { 123 | let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes)) 124 | return AFError.responseValidationFailed(reason: reason) 125 | }() 126 | 127 | return .failure(error) 128 | } 129 | 130 | for contentType in acceptableContentTypes { 131 | if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { 132 | return .success 133 | } 134 | } 135 | 136 | let error: AFError = { 137 | let reason: ErrorReason = .unacceptableContentType( 138 | acceptableContentTypes: Array(acceptableContentTypes), 139 | responseContentType: responseContentType 140 | ) 141 | 142 | return AFError.responseValidationFailed(reason: reason) 143 | }() 144 | 145 | return .failure(error) 146 | } 147 | } 148 | 149 | // MARK: - 150 | 151 | extension DataRequest { 152 | /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the 153 | /// request was valid. 154 | public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult 155 | 156 | /// Validates the request, using the specified closure. 157 | /// 158 | /// If validation fails, subsequent calls to response handlers will have an associated error. 159 | /// 160 | /// - parameter validation: A closure to validate the request. 161 | /// 162 | /// - returns: The request. 163 | @discardableResult 164 | public func validate(_ validation: @escaping Validation) -> Self { 165 | let validationExecution: () -> Void = { [unowned self] in 166 | if 167 | let response = self.response, 168 | self.delegate.error == nil, 169 | case let .failure(error) = validation(self.request, response, self.delegate.data) 170 | { 171 | self.delegate.error = error 172 | } 173 | } 174 | 175 | validations.append(validationExecution) 176 | 177 | return self 178 | } 179 | 180 | /// Validates that the response has a status code in the specified sequence. 181 | /// 182 | /// If validation fails, subsequent calls to response handlers will have an associated error. 183 | /// 184 | /// - parameter range: The range of acceptable status codes. 185 | /// 186 | /// - returns: The request. 187 | @discardableResult 188 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 189 | return validate { [unowned self] _, response, _ in 190 | return self.validate(statusCode: acceptableStatusCodes, response: response) 191 | } 192 | } 193 | 194 | /// Validates that the response has a content type in the specified sequence. 195 | /// 196 | /// If validation fails, subsequent calls to response handlers will have an associated error. 197 | /// 198 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 199 | /// 200 | /// - returns: The request. 201 | @discardableResult 202 | public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { 203 | return validate { [unowned self] _, response, data in 204 | return self.validate(contentType: acceptableContentTypes, response: response, data: data) 205 | } 206 | } 207 | 208 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 209 | /// type matches any specified in the Accept HTTP header field. 210 | /// 211 | /// If validation fails, subsequent calls to response handlers will have an associated error. 212 | /// 213 | /// - returns: The request. 214 | @discardableResult 215 | public func validate() -> Self { 216 | return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) 217 | } 218 | } 219 | 220 | // MARK: - 221 | 222 | extension DownloadRequest { 223 | /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a 224 | /// destination URL, and returns whether the request was valid. 225 | public typealias Validation = ( 226 | _ request: URLRequest?, 227 | _ response: HTTPURLResponse, 228 | _ temporaryURL: URL?, 229 | _ destinationURL: URL?) 230 | -> ValidationResult 231 | 232 | /// Validates the request, using the specified closure. 233 | /// 234 | /// If validation fails, subsequent calls to response handlers will have an associated error. 235 | /// 236 | /// - parameter validation: A closure to validate the request. 237 | /// 238 | /// - returns: The request. 239 | @discardableResult 240 | public func validate(_ validation: @escaping Validation) -> Self { 241 | let validationExecution: () -> Void = { [unowned self] in 242 | let request = self.request 243 | let temporaryURL = self.downloadDelegate.temporaryURL 244 | let destinationURL = self.downloadDelegate.destinationURL 245 | 246 | if 247 | let response = self.response, 248 | self.delegate.error == nil, 249 | case let .failure(error) = validation(request, response, temporaryURL, destinationURL) 250 | { 251 | self.delegate.error = error 252 | } 253 | } 254 | 255 | validations.append(validationExecution) 256 | 257 | return self 258 | } 259 | 260 | /// Validates that the response has a status code in the specified sequence. 261 | /// 262 | /// If validation fails, subsequent calls to response handlers will have an associated error. 263 | /// 264 | /// - parameter range: The range of acceptable status codes. 265 | /// 266 | /// - returns: The request. 267 | @discardableResult 268 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 269 | return validate { [unowned self] _, response, _, _ in 270 | return self.validate(statusCode: acceptableStatusCodes, response: response) 271 | } 272 | } 273 | 274 | /// Validates that the response has a content type in the specified sequence. 275 | /// 276 | /// If validation fails, subsequent calls to response handlers will have an associated error. 277 | /// 278 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 279 | /// 280 | /// - returns: The request. 281 | @discardableResult 282 | public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { 283 | return validate { [unowned self] _, response, _, _ in 284 | let fileURL = self.downloadDelegate.fileURL 285 | 286 | guard let validFileURL = fileURL else { 287 | return .failure(AFError.responseValidationFailed(reason: .dataFileNil)) 288 | } 289 | 290 | do { 291 | let data = try Data(contentsOf: validFileURL) 292 | return self.validate(contentType: acceptableContentTypes, response: response, data: data) 293 | } catch { 294 | return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL))) 295 | } 296 | } 297 | } 298 | 299 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 300 | /// type matches any specified in the Accept HTTP header field. 301 | /// 302 | /// If validation fails, subsequent calls to response handlers will have an associated error. 303 | /// 304 | /// - returns: The request. 305 | @discardableResult 306 | public func validate() -> Self { 307 | return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.0.1) 3 | - SwiftyJSON (3.1.1) 4 | 5 | DEPENDENCIES: 6 | - Alamofire (~> 4.0) 7 | - SwiftyJSON 8 | 9 | SPEC CHECKSUMS: 10 | Alamofire: 7682d43245de14874acd142ec137b144aa1dd335 11 | SwiftyJSON: f0be2e604f83e8405a624e9f891898bf6ed4e019 12 | 13 | PODFILE CHECKSUM: 53075ccc16f565c7c7673fe7e66201fd5dc8a2cd 14 | 15 | COCOAPODS: 1.1.1 16 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/SwiftyJSON/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ruoyu Fu 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 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/SwiftyJSON/README.md: -------------------------------------------------------------------------------- 1 | #SwiftyJSON 2 | 3 | [![Travis CI](https://travis-ci.org/SwiftyJSON/SwiftyJSON.svg?branch=master)](https://travis-ci.org/SwiftyJSON/SwiftyJSON) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![CocoaPods](https://img.shields.io/cocoapods/v/SwiftyJSON.svg) ![Platform](https://img.shields.io/badge/platforms-iOS%208.0+%20%7C%20macOS%2010.10+%20%7C%20tvOS%209.0+%20%7C%20watchOS%202.0+-333333.svg) 4 | 5 | SwiftyJSON makes it easy to deal with JSON data in Swift. 6 | 7 | 1. [Why is the typical JSON handling in Swift NOT good](#why-is-the-typical-json-handling-in-swift-not-good) 8 | 2. [Requirements](#requirements) 9 | 3. [Integration](#integration) 10 | 4. [Usage](#usage) 11 | - [Initialization](#initialization) 12 | - [Subscript](#subscript) 13 | - [Loop](#loop) 14 | - [Error](#error) 15 | - [Optional getter](#optional-getter) 16 | - [Non-optional getter](#non-optional-getter) 17 | - [Setter](#setter) 18 | - [Raw object](#raw-object) 19 | - [Literal convertibles](#literal-convertibles) 20 | 5. [Work with Alamofire](#work-with-alamofire) 21 | 22 | > For Legacy Swift support, take a look at the [swift2 branch](https://github.com/SwiftyJSON/SwiftyJSON/tree/swift2) 23 | 24 | > [中文介绍](http://tangplin.github.io/swiftyjson/) 25 | 26 | 27 | ##Why is the typical JSON handling in Swift NOT good? 28 | Swift is very strict about types. But although explicit typing is good for saving us from mistakes, it becomes painful when dealing with JSON and other areas that are, by nature, implicit about types. 29 | 30 | Take the Twitter API for example. Say we want to retrieve a user's "name" value of some tweet in Swift (according to Twitter's API https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline). 31 | 32 | The code would look like this: 33 | 34 | ```swift 35 | 36 | if let statusesArray = try? NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]], 37 | let user = statusesArray[0]["user"] as? [String: AnyObject], 38 | let username = user["name"] as? String { 39 | // Finally we got the username 40 | } 41 | 42 | ``` 43 | 44 | It's not good. 45 | 46 | Even if we use optional chaining, it would be messy: 47 | 48 | ```swift 49 | 50 | if let JSONObject = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]], 51 | let username = (JSONObject[0]["user"] as? [String: AnyObject])?["name"] as? String { 52 | // There's our username 53 | } 54 | 55 | ``` 56 | An unreadable mess--for something that should really be simple! 57 | 58 | With SwiftyJSON all you have to do is: 59 | 60 | ```swift 61 | 62 | let json = JSON(data: dataFromNetworking) 63 | if let userName = json[0]["user"]["name"].string { 64 | //Now you got your value 65 | } 66 | 67 | ``` 68 | 69 | And don't worry about the Optional Wrapping thing. It's done for you automatically. 70 | 71 | ```swift 72 | 73 | let json = JSON(data: dataFromNetworking) 74 | if let userName = json[999999]["wrong_key"]["wrong_name"].string { 75 | //Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety 76 | } else { 77 | //Print the error 78 | print(json[999999]["wrong_key"]["wrong_name"]) 79 | } 80 | 81 | ``` 82 | 83 | ## Requirements 84 | 85 | - iOS 8.0+ | macOS 10.10+ | tvOS 9.0+ | watchOS 2.0+ 86 | - Xcode 8 87 | 88 | ##Integration 89 | 90 | ####CocoaPods (iOS 8+, OS X 10.9+) 91 | You can use [CocoaPods](http://cocoapods.org/) to install `SwiftyJSON`by adding it to your `Podfile`: 92 | ```ruby 93 | platform :ios, '8.0' 94 | use_frameworks! 95 | 96 | target 'MyApp' do 97 | pod 'SwiftyJSON' 98 | end 99 | ``` 100 | Note that this requires CocoaPods version 36, and your iOS deployment target to be at least 8.0: 101 | 102 | 103 | ####Carthage (iOS 8+, OS X 10.9+) 104 | You can use [Carthage](https://github.com/Carthage/Carthage) to install `SwiftyJSON` by adding it to your `Cartfile`: 105 | ``` 106 | github "SwiftyJSON/SwiftyJSON" 107 | ``` 108 | 109 | ####Swift Package Manager 110 | You can use [The Swift Package Manager](https://swift.org/package-manager) to install `SwiftyJSON` by adding the proper description to your `Package.swift` file: 111 | ```swift 112 | import PackageDescription 113 | 114 | let package = Package( 115 | name: "YOUR_PROJECT_NAME", 116 | targets: [], 117 | dependencies: [ 118 | .Package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", versions: Version(1,0,0).. = json["list"].arrayValue 292 | ``` 293 | ```swift 294 | //If not a Dictionary or nil, return [:] 295 | let user: Dictionary = json["user"].dictionaryValue 296 | ``` 297 | 298 | ####Setter 299 | ```swift 300 | json["name"] = JSON("new-name") 301 | json[0] = JSON(1) 302 | ``` 303 | ```swift 304 | json["id"].int = 1234567890 305 | json["coordinate"].double = 8766.766 306 | json["name"].string = "Jack" 307 | json.arrayObject = [1,2,3,4] 308 | json.dictionaryObject = ["name":"Jack", "age":25] 309 | ``` 310 | 311 | ####Raw object 312 | ```swift 313 | let jsonObject: AnyObject = json.object 314 | ``` 315 | ```swift 316 | if let jsonObject: AnyObject = json.rawValue 317 | ``` 318 | ```swift 319 | //convert the JSON to raw NSData 320 | if let data = json.rawData() { 321 | //Do something you want 322 | } 323 | ``` 324 | ```swift 325 | //convert the JSON to a raw String 326 | if let string = json.rawString() { 327 | //Do something you want 328 | } 329 | ``` 330 | ####Existence 331 | ```swift 332 | //shows you whether value specified in JSON or not 333 | if json["name"].exists() 334 | ``` 335 | 336 | ####Literal convertibles 337 | For more info about literal convertibles: [Swift Literal Convertibles](http://nshipster.com/swift-literal-convertible/) 338 | ```swift 339 | //StringLiteralConvertible 340 | let json: JSON = "I'm a json" 341 | ``` 342 | ```swift 343 | //IntegerLiteralConvertible 344 | let json: JSON = 12345 345 | ``` 346 | ```swift 347 | //BooleanLiteralConvertible 348 | let json: JSON = true 349 | ``` 350 | ```swift 351 | //FloatLiteralConvertible 352 | let json: JSON = 2.8765 353 | ``` 354 | ```swift 355 | //DictionaryLiteralConvertible 356 | let json: JSON = ["I":"am", "a":"json"] 357 | ``` 358 | ```swift 359 | //ArrayLiteralConvertible 360 | let json: JSON = ["I", "am", "a", "json"] 361 | ``` 362 | ```swift 363 | //NilLiteralConvertible 364 | let json: JSON = nil 365 | ``` 366 | ```swift 367 | //With subscript in array 368 | var json: JSON = [1,2,3] 369 | json[0] = 100 370 | json[1] = 200 371 | json[2] = 300 372 | json[999] = 300 //Don't worry, nothing will happen 373 | ``` 374 | ```swift 375 | //With subscript in dictionary 376 | var json: JSON = ["name": "Jack", "age": 25] 377 | json["name"] = "Mike" 378 | json["age"] = "25" //It's OK to set String 379 | json["address"] = "L.A." // Add the "address": "L.A." in json 380 | ``` 381 | ```swift 382 | //Array & Dictionary 383 | var json: JSON = ["name": "Jack", "age": 25, "list": ["a", "b", "c", ["what": "this"]]] 384 | json["list"][3]["what"] = "that" 385 | json["list",3,"what"] = "that" 386 | let path = ["list",3,"what"] 387 | json[path] = "that" 388 | ``` 389 | ```swift 390 | //With other JSON objects 391 | let user: JSON = ["username" : "Steve", "password": "supersecurepassword"] 392 | let auth: JSON = [ 393 | "user": user.object //use user.object instead of just user 394 | "apikey": "supersecretapitoken" 395 | ] 396 | ```` 397 | 398 | ##Work with Alamofire 399 | 400 | SwiftyJSON nicely wraps the result of the Alamofire JSON response handler: 401 | ```swift 402 | Alamofire.request(.GET, url).validate().responseJSON { response in 403 | switch response.result { 404 | case .success(let value): 405 | let json = JSON(value) 406 | print("JSON: \(json)") 407 | case .failure(let error): 408 | print(error) 409 | } 410 | } 411 | ``` 412 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | 6 | FOUNDATION_EXPORT double AlamofireVersionNumber; 7 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 8 | 9 | -------------------------------------------------------------------------------- /swift-mvvm/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 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Alamofire/Alamofire.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Alamofire/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm-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 | 27 | ## SwiftyJSON 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2016 Ruoyu Fu 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in 41 | all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 49 | THE SOFTWARE. 50 | 51 | Generated by CocoaPods - https://cocoapods.org 52 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Alamofire 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | The MIT License (MIT) 47 | 48 | Copyright (c) 2016 Ruoyu Fu 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy 51 | of this software and associated documentation files (the "Software"), to deal 52 | in the Software without restriction, including without limitation the rights 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 54 | copies of the Software, and to permit persons to whom the Software is 55 | furnished to do so, subject to the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be included in 58 | all copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 61 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 63 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 64 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 65 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 66 | THE SOFTWARE. 67 | 68 | License 69 | MIT 70 | Title 71 | SwiftyJSON 72 | Type 73 | PSGroupSpecifier 74 | 75 | 76 | FooterText 77 | Generated by CocoaPods - https://cocoapods.org 78 | Title 79 | 80 | Type 81 | PSGroupSpecifier 82 | 83 | 84 | StringsTable 85 | Acknowledgements 86 | Title 87 | Acknowledgements 88 | 89 | 90 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_swift_mvvm : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_swift_mvvm 5 | @end 6 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --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 "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework" 88 | install_framework "$BUILT_PRODUCTS_DIR/SwiftyJSON/SwiftyJSON.framework" 89 | fi 90 | if [[ "$CONFIGURATION" == "Release" ]]; then 91 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework" 92 | install_framework "$BUILT_PRODUCTS_DIR/SwiftyJSON/SwiftyJSON.framework" 93 | fi 94 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | install_resource() 27 | { 28 | if [[ "$1" = /* ]] ; then 29 | RESOURCE_PATH="$1" 30 | else 31 | RESOURCE_PATH="${PODS_ROOT}/$1" 32 | fi 33 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 34 | cat << EOM 35 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 36 | EOM 37 | exit 1 38 | fi 39 | case $RESOURCE_PATH in 40 | *.storyboard) 41 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 42 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 43 | ;; 44 | *.xib) 45 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 46 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 47 | ;; 48 | *.framework) 49 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 50 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 51 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 52 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | ;; 54 | *.xcdatamodel) 55 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 56 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 57 | ;; 58 | *.xcdatamodeld) 59 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 60 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 61 | ;; 62 | *.xcmappingmodel) 63 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 64 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 65 | ;; 66 | *.xcassets) 67 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 68 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 69 | ;; 70 | *) 71 | echo "$RESOURCE_PATH" 72 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 73 | ;; 74 | esac 75 | } 76 | 77 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 78 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 79 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 80 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | fi 83 | rm -f "$RESOURCES_TO_COPY" 84 | 85 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 86 | then 87 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 88 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 89 | while read line; do 90 | if [[ $line != "${PODS_ROOT}*" ]]; then 91 | XCASSET_FILES+=("$line") 92 | fi 93 | done <<<"$OTHER_XCASSETS" 94 | 95 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | fi 97 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | 6 | FOUNDATION_EXPORT double Pods_swift_mvvmVersionNumber; 7 | FOUNDATION_EXPORT const unsigned char Pods_swift_mvvmVersionString[]; 8 | 9 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON/SwiftyJSON.framework/Headers" 7 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "SwiftyJSON" 8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_swift_mvvm { 2 | umbrella header "Pods-swift-mvvm-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON/SwiftyJSON.framework/Headers" 7 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "SwiftyJSON" 8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/SwiftyJSON/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SwiftyJSON : NSObject 3 | @end 4 | @implementation PodsDummy_SwiftyJSON 5 | @end 6 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | 6 | FOUNDATION_EXPORT double SwiftyJSONVersionNumber; 7 | FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[]; 8 | 9 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap: -------------------------------------------------------------------------------- 1 | framework module SwiftyJSON { 2 | umbrella header "SwiftyJSON-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /swift-mvvm/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | SWIFT_VERSION = 3.0 11 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C4D24F7751D0FF33C783FD0E /* Pods_swift_mvvm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 401B9721B4D366D89D25D072 /* Pods_swift_mvvm.framework */; }; 11 | E82265671DEF99E500877497 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E82265661DEF99E500877497 /* NetworkManager.swift */; }; 12 | E82265691DEF9FCE00877497 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E82265681DEF9FCE00877497 /* Utilities.swift */; }; 13 | E822656D1DEFA05800877497 /* WeatherData.swift in Sources */ = {isa = PBXBuildFile; fileRef = E822656C1DEFA05800877497 /* WeatherData.swift */; }; 14 | E822656F1DEFA05F00877497 /* WeatherViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E822656E1DEFA05F00877497 /* WeatherViewModel.swift */; }; 15 | E82265711DEFA06700877497 /* WeatherTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E82265701DEFA06700877497 /* WeatherTableViewCell.swift */; }; 16 | E8F4E16F1DEE4C510025B1CD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8F4E16E1DEE4C510025B1CD /* AppDelegate.swift */; }; 17 | E8F4E1711DEE4C510025B1CD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8F4E1701DEE4C510025B1CD /* ViewController.swift */; }; 18 | E8F4E1741DEE4C510025B1CD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E8F4E1721DEE4C510025B1CD /* Main.storyboard */; }; 19 | E8F4E1761DEE4C510025B1CD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E8F4E1751DEE4C510025B1CD /* Assets.xcassets */; }; 20 | E8F4E1791DEE4C510025B1CD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E8F4E1771DEE4C510025B1CD /* LaunchScreen.storyboard */; }; 21 | E8F4E1811DEE526D0025B1CD /* keys.plist in Resources */ = {isa = PBXBuildFile; fileRef = E8F4E1801DEE526D0025B1CD /* keys.plist */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 401B9721B4D366D89D25D072 /* Pods_swift_mvvm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_swift_mvvm.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | A7E9BEB9FC48B542360C4821 /* Pods-swift-mvvm.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-swift-mvvm.release.xcconfig"; path = "Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm.release.xcconfig"; sourceTree = ""; }; 27 | AA5D8DE1127234484EA1F477 /* Pods-swift-mvvm.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-swift-mvvm.debug.xcconfig"; path = "Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm.debug.xcconfig"; sourceTree = ""; }; 28 | E82265661DEF99E500877497 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; 29 | E82265681DEF9FCE00877497 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; 30 | E822656C1DEFA05800877497 /* WeatherData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherData.swift; sourceTree = ""; }; 31 | E822656E1DEFA05F00877497 /* WeatherViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherViewModel.swift; sourceTree = ""; }; 32 | E82265701DEFA06700877497 /* WeatherTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherTableViewCell.swift; sourceTree = ""; }; 33 | E8F4E16B1DEE4C510025B1CD /* swift-mvvm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "swift-mvvm.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | E8F4E16E1DEE4C510025B1CD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | E8F4E1701DEE4C510025B1CD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 36 | E8F4E1731DEE4C510025B1CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 37 | E8F4E1751DEE4C510025B1CD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | E8F4E1781DEE4C510025B1CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 39 | E8F4E17A1DEE4C510025B1CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | E8F4E1801DEE526D0025B1CD /* keys.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = keys.plist; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | E8F4E1681DEE4C510025B1CD /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | C4D24F7751D0FF33C783FD0E /* Pods_swift_mvvm.framework in Frameworks */, 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXFrameworksBuildPhase section */ 53 | 54 | /* Begin PBXGroup section */ 55 | 1EADF757FC6E709DBEEECA92 /* Frameworks */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 401B9721B4D366D89D25D072 /* Pods_swift_mvvm.framework */, 59 | ); 60 | name = Frameworks; 61 | sourceTree = ""; 62 | }; 63 | 7F878293CE9FEEBD50095EC4 /* Pods */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | AA5D8DE1127234484EA1F477 /* Pods-swift-mvvm.debug.xcconfig */, 67 | A7E9BEB9FC48B542360C4821 /* Pods-swift-mvvm.release.xcconfig */, 68 | ); 69 | name = Pods; 70 | sourceTree = ""; 71 | }; 72 | E8F4E1621DEE4C500025B1CD = { 73 | isa = PBXGroup; 74 | children = ( 75 | E8F4E16D1DEE4C510025B1CD /* swift-mvvm */, 76 | E8F4E16C1DEE4C510025B1CD /* Products */, 77 | 7F878293CE9FEEBD50095EC4 /* Pods */, 78 | 1EADF757FC6E709DBEEECA92 /* Frameworks */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | E8F4E16C1DEE4C510025B1CD /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | E8F4E16B1DEE4C510025B1CD /* swift-mvvm.app */, 86 | ); 87 | name = Products; 88 | sourceTree = ""; 89 | }; 90 | E8F4E16D1DEE4C510025B1CD /* swift-mvvm */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | E8F4E16E1DEE4C510025B1CD /* AppDelegate.swift */, 94 | E8F4E1701DEE4C510025B1CD /* ViewController.swift */, 95 | E82265661DEF99E500877497 /* NetworkManager.swift */, 96 | E82265681DEF9FCE00877497 /* Utilities.swift */, 97 | E822656C1DEFA05800877497 /* WeatherData.swift */, 98 | E822656E1DEFA05F00877497 /* WeatherViewModel.swift */, 99 | E82265701DEFA06700877497 /* WeatherTableViewCell.swift */, 100 | E8F4E1721DEE4C510025B1CD /* Main.storyboard */, 101 | E8F4E1751DEE4C510025B1CD /* Assets.xcassets */, 102 | E8F4E1771DEE4C510025B1CD /* LaunchScreen.storyboard */, 103 | E8F4E17A1DEE4C510025B1CD /* Info.plist */, 104 | E8F4E1801DEE526D0025B1CD /* keys.plist */, 105 | ); 106 | path = "swift-mvvm"; 107 | sourceTree = ""; 108 | }; 109 | /* End PBXGroup section */ 110 | 111 | /* Begin PBXNativeTarget section */ 112 | E8F4E16A1DEE4C510025B1CD /* swift-mvvm */ = { 113 | isa = PBXNativeTarget; 114 | buildConfigurationList = E8F4E17D1DEE4C510025B1CD /* Build configuration list for PBXNativeTarget "swift-mvvm" */; 115 | buildPhases = ( 116 | 51B7C80E9CFE6E39273BE392 /* [CP] Check Pods Manifest.lock */, 117 | E8F4E1671DEE4C510025B1CD /* Sources */, 118 | E8F4E1681DEE4C510025B1CD /* Frameworks */, 119 | E8F4E1691DEE4C510025B1CD /* Resources */, 120 | 3030603CDD016B0D7950AA45 /* [CP] Embed Pods Frameworks */, 121 | 959B3AB11D2679A2A7DB2AB4 /* [CP] Copy Pods Resources */, 122 | ); 123 | buildRules = ( 124 | ); 125 | dependencies = ( 126 | ); 127 | name = "swift-mvvm"; 128 | productName = "swift-mvvm"; 129 | productReference = E8F4E16B1DEE4C510025B1CD /* swift-mvvm.app */; 130 | productType = "com.apple.product-type.application"; 131 | }; 132 | /* End PBXNativeTarget section */ 133 | 134 | /* Begin PBXProject section */ 135 | E8F4E1631DEE4C500025B1CD /* Project object */ = { 136 | isa = PBXProject; 137 | attributes = { 138 | LastSwiftUpdateCheck = 0810; 139 | LastUpgradeCheck = 0810; 140 | ORGANIZATIONNAME = ISL; 141 | TargetAttributes = { 142 | E8F4E16A1DEE4C510025B1CD = { 143 | CreatedOnToolsVersion = 8.1; 144 | DevelopmentTeam = Z3QGFL558K; 145 | ProvisioningStyle = Automatic; 146 | }; 147 | }; 148 | }; 149 | buildConfigurationList = E8F4E1661DEE4C500025B1CD /* Build configuration list for PBXProject "swift-mvvm" */; 150 | compatibilityVersion = "Xcode 3.2"; 151 | developmentRegion = English; 152 | hasScannedForEncodings = 0; 153 | knownRegions = ( 154 | en, 155 | Base, 156 | ); 157 | mainGroup = E8F4E1621DEE4C500025B1CD; 158 | productRefGroup = E8F4E16C1DEE4C510025B1CD /* Products */; 159 | projectDirPath = ""; 160 | projectRoot = ""; 161 | targets = ( 162 | E8F4E16A1DEE4C510025B1CD /* swift-mvvm */, 163 | ); 164 | }; 165 | /* End PBXProject section */ 166 | 167 | /* Begin PBXResourcesBuildPhase section */ 168 | E8F4E1691DEE4C510025B1CD /* Resources */ = { 169 | isa = PBXResourcesBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | E8F4E1811DEE526D0025B1CD /* keys.plist in Resources */, 173 | E8F4E1791DEE4C510025B1CD /* LaunchScreen.storyboard in Resources */, 174 | E8F4E1761DEE4C510025B1CD /* Assets.xcassets in Resources */, 175 | E8F4E1741DEE4C510025B1CD /* Main.storyboard in Resources */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXResourcesBuildPhase section */ 180 | 181 | /* Begin PBXShellScriptBuildPhase section */ 182 | 3030603CDD016B0D7950AA45 /* [CP] Embed Pods Frameworks */ = { 183 | isa = PBXShellScriptBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | ); 187 | inputPaths = ( 188 | ); 189 | name = "[CP] Embed Pods Frameworks"; 190 | outputPaths = ( 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | shellPath = /bin/sh; 194 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm-frameworks.sh\"\n"; 195 | showEnvVarsInLog = 0; 196 | }; 197 | 51B7C80E9CFE6E39273BE392 /* [CP] Check Pods Manifest.lock */ = { 198 | isa = PBXShellScriptBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | ); 202 | inputPaths = ( 203 | ); 204 | name = "[CP] Check Pods Manifest.lock"; 205 | outputPaths = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | shellPath = /bin/sh; 209 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 210 | showEnvVarsInLog = 0; 211 | }; 212 | 959B3AB11D2679A2A7DB2AB4 /* [CP] Copy Pods Resources */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Copy Pods Resources"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-swift-mvvm/Pods-swift-mvvm-resources.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | /* End PBXShellScriptBuildPhase section */ 228 | 229 | /* Begin PBXSourcesBuildPhase section */ 230 | E8F4E1671DEE4C510025B1CD /* Sources */ = { 231 | isa = PBXSourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | E82265671DEF99E500877497 /* NetworkManager.swift in Sources */, 235 | E822656F1DEFA05F00877497 /* WeatherViewModel.swift in Sources */, 236 | E82265711DEFA06700877497 /* WeatherTableViewCell.swift in Sources */, 237 | E822656D1DEFA05800877497 /* WeatherData.swift in Sources */, 238 | E8F4E1711DEE4C510025B1CD /* ViewController.swift in Sources */, 239 | E82265691DEF9FCE00877497 /* Utilities.swift in Sources */, 240 | E8F4E16F1DEE4C510025B1CD /* AppDelegate.swift in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXSourcesBuildPhase section */ 245 | 246 | /* Begin PBXVariantGroup section */ 247 | E8F4E1721DEE4C510025B1CD /* Main.storyboard */ = { 248 | isa = PBXVariantGroup; 249 | children = ( 250 | E8F4E1731DEE4C510025B1CD /* Base */, 251 | ); 252 | name = Main.storyboard; 253 | sourceTree = ""; 254 | }; 255 | E8F4E1771DEE4C510025B1CD /* LaunchScreen.storyboard */ = { 256 | isa = PBXVariantGroup; 257 | children = ( 258 | E8F4E1781DEE4C510025B1CD /* Base */, 259 | ); 260 | name = LaunchScreen.storyboard; 261 | sourceTree = ""; 262 | }; 263 | /* End PBXVariantGroup section */ 264 | 265 | /* Begin XCBuildConfiguration section */ 266 | E8F4E17B1DEE4C510025B1CD /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_ANALYZER_NONNULL = YES; 271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 272 | CLANG_CXX_LIBRARY = "libc++"; 273 | CLANG_ENABLE_MODULES = YES; 274 | CLANG_ENABLE_OBJC_ARC = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_CONSTANT_CONVERSION = YES; 277 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 278 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 284 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 285 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 286 | CLANG_WARN_UNREACHABLE_CODE = YES; 287 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 288 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 289 | COPY_PHASE_STRIP = NO; 290 | DEBUG_INFORMATION_FORMAT = dwarf; 291 | ENABLE_STRICT_OBJC_MSGSEND = YES; 292 | ENABLE_TESTABILITY = YES; 293 | GCC_C_LANGUAGE_STANDARD = gnu99; 294 | GCC_DYNAMIC_NO_PIC = NO; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_OPTIMIZATION_LEVEL = 0; 297 | GCC_PREPROCESSOR_DEFINITIONS = ( 298 | "DEBUG=1", 299 | "$(inherited)", 300 | ); 301 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 302 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 303 | GCC_WARN_UNDECLARED_SELECTOR = YES; 304 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 305 | GCC_WARN_UNUSED_FUNCTION = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 308 | MTL_ENABLE_DEBUG_INFO = YES; 309 | ONLY_ACTIVE_ARCH = YES; 310 | SDKROOT = iphoneos; 311 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 312 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 313 | }; 314 | name = Debug; 315 | }; 316 | E8F4E17C1DEE4C510025B1CD /* Release */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | ALWAYS_SEARCH_USER_PATHS = NO; 320 | CLANG_ANALYZER_NONNULL = YES; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_CONSTANT_CONVERSION = YES; 327 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 328 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 329 | CLANG_WARN_EMPTY_BODY = YES; 330 | CLANG_WARN_ENUM_CONVERSION = YES; 331 | CLANG_WARN_INFINITE_RECURSION = YES; 332 | CLANG_WARN_INT_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 341 | ENABLE_NS_ASSERTIONS = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu99; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 352 | MTL_ENABLE_DEBUG_INFO = NO; 353 | SDKROOT = iphoneos; 354 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Release; 358 | }; 359 | E8F4E17E1DEE4C510025B1CD /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | baseConfigurationReference = AA5D8DE1127234484EA1F477 /* Pods-swift-mvvm.debug.xcconfig */; 362 | buildSettings = { 363 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 365 | DEVELOPMENT_TEAM = Z3QGFL558K; 366 | INFOPLIST_FILE = "swift-mvvm/Info.plist"; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 368 | PRODUCT_BUNDLE_IDENTIFIER = "com.istrategylabs.swift-mvvm"; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | SWIFT_VERSION = 3.0; 371 | }; 372 | name = Debug; 373 | }; 374 | E8F4E17F1DEE4C510025B1CD /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | baseConfigurationReference = A7E9BEB9FC48B542360C4821 /* Pods-swift-mvvm.release.xcconfig */; 377 | buildSettings = { 378 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 379 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 380 | DEVELOPMENT_TEAM = Z3QGFL558K; 381 | INFOPLIST_FILE = "swift-mvvm/Info.plist"; 382 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 383 | PRODUCT_BUNDLE_IDENTIFIER = "com.istrategylabs.swift-mvvm"; 384 | PRODUCT_NAME = "$(TARGET_NAME)"; 385 | SWIFT_VERSION = 3.0; 386 | }; 387 | name = Release; 388 | }; 389 | /* End XCBuildConfiguration section */ 390 | 391 | /* Begin XCConfigurationList section */ 392 | E8F4E1661DEE4C500025B1CD /* Build configuration list for PBXProject "swift-mvvm" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | E8F4E17B1DEE4C510025B1CD /* Debug */, 396 | E8F4E17C1DEE4C510025B1CD /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | E8F4E17D1DEE4C510025B1CD /* Build configuration list for PBXNativeTarget "swift-mvvm" */ = { 402 | isa = XCConfigurationList; 403 | buildConfigurations = ( 404 | E8F4E17E1DEE4C510025B1CD /* Debug */, 405 | E8F4E17F1DEE4C510025B1CD /* Release */, 406 | ); 407 | defaultConfigurationIsVisible = 0; 408 | defaultConfigurationName = Release; 409 | }; 410 | /* End XCConfigurationList section */ 411 | }; 412 | rootObject = E8F4E1631DEE4C500025B1CD /* Project object */; 413 | } 414 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // swift-mvvm 4 | // 5 | // Created by Taylor Guidon on 11/29/16. 6 | // Copyright © 2016 ISL. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/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 | } -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/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 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/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 | 41 | 47 | 53 | 59 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/NetworkManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkManager.swift 3 | // swift-mvvm 4 | // 5 | // Created by Taylor Guidon on 11/30/16. 6 | // Copyright © 2016 ISL. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | protocol NetworkManagerDelegate { 13 | func dataReceived(data: Any?, error: NSError?) 14 | } 15 | 16 | class NetworkManager: NSObject { 17 | let baseURLString: String = "https://api.darksky.net/forecast/" 18 | var baseURLWithAPIKeyString: String 19 | var delegate: NetworkManagerDelegate? 20 | 21 | init(apiKey: String) { 22 | self.baseURLWithAPIKeyString = "\(self.baseURLString)\(apiKey)/" 23 | 24 | super.init() 25 | } 26 | 27 | func getWeatherForCoordinate(coordinate: String) { 28 | let requestURL: String = "\(baseURLWithAPIKeyString)\(coordinate)" 29 | Alamofire.request(requestURL).responseJSON { (response) in 30 | switch response.result { 31 | case .success: 32 | guard let json = response.result.value else { 33 | return 34 | } 35 | self.delegate?.dataReceived(data: json, error: nil) 36 | case .failure(let error): 37 | self.delegate?.dataReceived(data: nil, error: error as NSError) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/Utilities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utilities.swift 3 | // swift-mvvm 4 | // 5 | // Created by Taylor Guidon on 11/30/16. 6 | // Copyright © 2016 ISL. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SwiftyJSON 11 | 12 | class Utilities { 13 | class func loadWeatherFromJSON(json: JSON) -> [WeatherData] { 14 | var weatherDataArray: [WeatherData] = [] 15 | 16 | guard let dailyWeatherArray = json["daily"]["data"].array else { 17 | print("No array") 18 | return [] 19 | } 20 | 21 | for day in dailyWeatherArray { 22 | guard let rawUnixTime = day["time"].double, 23 | let minTemp = day["temperatureMin"].double, 24 | let maxTemp = day["temperatureMax"].double, 25 | let summary = day["summary"].string else { 26 | print("Error with data") 27 | return [] 28 | } 29 | let weatherData = WeatherData(rawUnixTime: rawUnixTime, minTemp: minTemp, maxTemp: maxTemp, summary: summary) 30 | weatherDataArray.append(weatherData) 31 | } 32 | 33 | return weatherDataArray 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // swift-mvvm 4 | // 5 | // Created by Taylor Guidon on 11/29/16. 6 | // Copyright © 2016 ISL. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | class ViewController: UIViewController, NetworkManagerDelegate, UITableViewDelegate, UITableViewDataSource { 13 | @IBOutlet weak var tableView: UITableView! 14 | @IBOutlet weak var locationLabel: UILabel! 15 | 16 | let coordinateString: String = "38.914504,-77.021181" 17 | var weatherDataArray = [WeatherData]() 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | tableView.delegate = self 23 | tableView.dataSource = self 24 | 25 | locationLabel.text = "Weather for \(coordinateString)" 26 | 27 | guard let path = Bundle.main.path(forResource: "keys", ofType: "plist"), 28 | let keys = NSDictionary(contentsOfFile: path), 29 | let apiKey = keys["apiKey"] as? String else { 30 | print("Issue with keys.plist") 31 | return 32 | } 33 | 34 | let networkManager = NetworkManager(apiKey: apiKey) 35 | networkManager.delegate = self 36 | networkManager.getWeatherForCoordinate(coordinate: coordinateString) 37 | } 38 | 39 | func dataReceived(data: Any?, error: NSError?) { 40 | if error != nil { 41 | print("Error: \(error)") 42 | } 43 | 44 | guard let data = data else { 45 | print("Error: No data") 46 | return 47 | } 48 | let json = JSON(data) 49 | weatherDataArray = Utilities.loadWeatherFromJSON(json: json) 50 | tableView.reloadData() 51 | } 52 | 53 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 54 | return weatherDataArray.count 55 | } 56 | 57 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 58 | let weatherData = weatherDataArray[indexPath.row] 59 | let weatherViewModel = WeatherViewModel(weatherData) 60 | return weatherViewModel.cellInstance(tableView, indexPath: indexPath) 61 | } 62 | 63 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 64 | let weatherData = weatherDataArray[indexPath.row] 65 | let weatherViewModel = WeatherViewModel(weatherData) 66 | weatherViewModel.tapCell(tableView, indexPath: indexPath) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/WeatherData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeatherData.swift 3 | // swift-mvvm 4 | // 5 | // Created by Taylor Guidon on 11/30/16. 6 | // Copyright © 2016 ISL. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class WeatherData: NSObject { 12 | var rawUnixTime: Double 13 | var minTemp: Double 14 | var maxTemp: Double 15 | var summary: String 16 | 17 | init(rawUnixTime: Double, minTemp: Double, maxTemp: Double, summary: String) { 18 | self.rawUnixTime = rawUnixTime 19 | self.minTemp = minTemp 20 | self.maxTemp = maxTemp 21 | self.summary = summary 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/WeatherTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeatherTableViewCell.swift 3 | // swift-mvvm 4 | // 5 | // Created by Taylor Guidon on 11/30/16. 6 | // Copyright © 2016 ISL. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class WeatherTableViewCell: UITableViewCell { 12 | @IBOutlet weak var dateLabel: UILabel! 13 | @IBOutlet weak var minTempLabel: UILabel! 14 | @IBOutlet weak var maxTempLabel: UILabel! 15 | @IBOutlet weak var summaryLabel: UILabel! 16 | 17 | override func awakeFromNib() { 18 | super.awakeFromNib() 19 | } 20 | 21 | func setup(_ viewModel: WeatherViewModel) { 22 | self.selectionStyle = .none 23 | 24 | guard let minTemp = viewModel.minTemp, 25 | let maxTemp = viewModel.maxTemp, 26 | let summary = viewModel.summary else { 27 | print("ViewModel is invalid") 28 | return 29 | } 30 | 31 | dateLabel.text = viewModel.dateString 32 | minTempLabel.text = String(minTemp) 33 | maxTempLabel.text = String(maxTemp) 34 | summaryLabel.text = summary 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /swift-mvvm/swift-mvvm/WeatherViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeatherViewModel.swift 3 | // swift-mvvm 4 | // 5 | // Created by Taylor Guidon on 11/30/16. 6 | // Copyright © 2016 ISL. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SwiftyJSON 11 | 12 | class WeatherViewModel { 13 | var weatherData: WeatherData 14 | 15 | var rawUnixTime: Double? 16 | var minTemp: Int? 17 | var maxTemp: Int? 18 | var summary: String? 19 | var dateString: String = "" 20 | 21 | let reuseIdentifier = "WeatherCell" 22 | 23 | init(_ weatherData: WeatherData) { 24 | self.weatherData = weatherData 25 | 26 | rawUnixTime = weatherData.rawUnixTime 27 | minTemp = Int(weatherData.minTemp.rounded()) 28 | maxTemp = Int(weatherData.maxTemp.rounded()) 29 | summary = weatherData.summary 30 | 31 | guard let unixTime = rawUnixTime else { 32 | print("Invalid unix time") 33 | return 34 | } 35 | 36 | let date = Date(timeIntervalSince1970: unixTime) 37 | let dateFormatter = DateFormatter() 38 | dateFormatter.dateStyle = .medium 39 | dateString = dateFormatter.string(from: date) 40 | } 41 | 42 | func cellInstance(_ tableView: UITableView, indexPath: IndexPath) -> UITableViewCell { 43 | let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! WeatherTableViewCell 44 | cell.setup(self) 45 | 46 | return cell 47 | } 48 | 49 | func tapCell(_ tableView: UITableView, indexPath: IndexPath) { 50 | print("Tapped a cell") 51 | } 52 | } 53 | --------------------------------------------------------------------------------