This repository was archived by the owner on Apr 23, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNetworking.swift
More file actions
208 lines (178 loc) · 6.87 KB
/
Copy pathNetworking.swift
File metadata and controls
208 lines (178 loc) · 6.87 KB
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//
// Networking.swift
// TheHap
//
// Created by Luke Garner on 7/21/16.
// Copyright © 2016 Amalgamated Bitpushers. All rights reserved.
//
import Foundation
import UIKit
public enum Result<T> {
case Success(T)
case Failure(Error)
}
public enum HTTPMethod: String {
case GET = "GET"
case POST = "POST"
case PUT = "PUT"
case DELETE = "DELETE"
}
public enum HTTPResponseError: Error {
case badStatusCode(statusCode: Int)
}
public let ENCODING = String.Encoding.utf8
// **************************
// ** REQUEST **
// **************************
// http://khanlou.com/2016/05/protocol-oriented-programming/
public protocol HTTPRequest {
var baseURL: URL? { get }
var method: HTTPMethod { get }
var basePath: String { get }
var parameters: Dictionary<String, String> { get }
var headers: Dictionary<String, String> { get }
}
public extension HTTPRequest {
var method : HTTPMethod { return .GET }
var basePath : String { return "" }
var parameters : Dictionary<String, String> { return Dictionary() }
var headers : Dictionary<String, String> { return Dictionary() }
}
public protocol ConstructableHTTPRequest: HTTPRequest {
func buildRequest() -> URLRequest?
}
public protocol JSONConstructableHTTPRequest: ConstructableHTTPRequest { }
public extension JSONConstructableHTTPRequest {
func buildRequest() -> URLRequest? {
guard let baseURL = baseURL else { return nil }
guard var urlComponents = URLComponents(url: baseURL, resolvingAgainstBaseURL: true) else { return nil }
urlComponents.path = urlComponents.path + basePath
guard let URL = urlComponents.url else { return nil }
var request = URLRequest(url: URL)
for (headerField, value) in headers {
request.addValue(value, forHTTPHeaderField: headerField)
}
if method == .POST {
request.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: [])
}
request.httpMethod = method.rawValue
return request
}
}
// **************************
// ** PARSING **
// **************************
public protocol Mockable {
static var MockJSON:Data { get }
}
public protocol ResultParsing {
associatedtype ParsedType
func parseData(_ data: Data) -> ParsedType?
}
public protocol StringParsing: ResultParsing { }
public extension StringParsing {
func parseData(_ data: Data) -> String? {
return String(data: data, encoding: ENCODING)
}
}
// Construct base types from AnyObjects:
public protocol JSONConstructable {
static func construct(_ with: AnyObject) -> Self?
static func construct(_ with: [AnyObject]) -> [Self]?
}
public extension JSONConstructable {
static func construct(_ objects: [AnyObject]) -> [Self]? {
return objects.compactMap { self.construct($0) }
}
}
public protocol JSONDictConstructable: JSONConstructable {
static func construct(_ with: [AnyObject]) -> [String: Self]?
}
// Convert raw JSON Data into an associated types:
public protocol JSONParsing: ResultParsing {
associatedtype JSONType: JSONConstructable
func parseData(_ data: Data) -> JSONType?
}
public extension JSONParsing {
func parseData(_ data: Data) -> JSONType? {
guard let deserializedData = try? JSONSerialization.jsonObject(with: data, options: []) else { return nil }
return JSONType.construct(deserializedData as AnyObject)
}
}
public protocol JSONArrayParsing: ResultParsing {
associatedtype JSONType: JSONConstructable
func parseData(_ data: Data) -> [JSONType]?
}
public extension JSONArrayParsing {
func parseData(_ data: Data) -> [JSONType]? {
guard let deserializedData = try? JSONSerialization.jsonObject(with: data, options: []) else { return nil }
guard let deserialzedArray = deserializedData as? [AnyObject] else { return nil }
return JSONType.construct(deserialzedArray)
}
}
public protocol JSONDictParsing: ResultParsing {
associatedtype JSONType: JSONDictConstructable
func parseData(_ data: Data) -> [String: JSONType]?
}
public extension JSONDictParsing {
func parseData(_ data: Data) -> [String: JSONType]? {
guard let deserializedData = try? JSONSerialization.jsonObject(with: data, options: []) else { return nil }
guard let deserialzedArray = deserializedData as? [AnyObject] else { return nil }
return JSONType.construct(deserialzedArray)
}
}
public protocol ImageParsing: ResultParsing {
func parseData(_ data: Data) -> UIImage?
}
public extension ImageParsing {
func parseData(_ data: Data) -> UIImage? {
return UIImage(data: data)
}
}
// **************************
// ** DATA SOURCE **
// **************************
public protocol DataSource: ResultParsing {
func get(withHandler completionHandler:@escaping (Result<ParsedType>) -> Void)
}
public protocol SendableHTTPRequest: ConstructableHTTPRequest, DataSource { }
public extension SendableHTTPRequest {
func sendRequest(withHandler completionHandler:@escaping (Result<ParsedType>) -> Void) {
let session = URLSession.shared
guard let request = buildRequest() else { return }
let task = session.dataTask(with: request, completionHandler: { taskData, taskResponse, taskError in
if let taskError = taskError {
completionHandler(Result.Failure(taskError))
return
}
guard let taskData = taskData else { return }
guard let taskResponse = taskResponse as? HTTPURLResponse else { return }
if (taskResponse.statusCode == 200) {
guard let result = self.parseData(taskData) else { return }
completionHandler(Result.Success(result))
} else {
completionHandler(Result.Failure(HTTPResponseError.badStatusCode(statusCode: taskResponse.statusCode)))
}
})
task.resume()
}
public func get(withHandler completionHandler: @escaping (Result<Self.ParsedType>) -> Void) {
sendRequest(withHandler: completionHandler)
}
}
public protocol MockSendableRequest: DataSource {}
public extension MockSendableRequest where ParsedType: Mockable {
func sendRequest(withHandler completionHandler:@escaping (Result<ParsedType>) -> Void) {
print ("Sending mock")
guard let result = self.parseData(ParsedType.MockJSON) else { return }
completionHandler(Result.Success(result))
}
public func get(withHandler completionHandler:@escaping (Result<Self.ParsedType>) -> Void) {
sendRequest(withHandler: completionHandler)
}
}
// **************************
// ** CONVENIENCE **
// **************************
public protocol Requestable: JSONConstructableHTTPRequest, SendableHTTPRequest {}
public protocol MockRequestable: MockSendableRequest {}