├── .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 | [](https://travis-ci.org/SwiftyJSON/SwiftyJSON) [](https://github.com/Carthage/Carthage)  
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 |
--------------------------------------------------------------------------------