How to make simple networking client in Swift

Issue #222

For more mature networking, visit https://github.com/onmyway133/Miami

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
final class NetworkClient {
let session: URLSession
let baseUrl: URL

init(session: URLSession = .shared, baseUrl: URL) {
self.session = session
self.baseUrl = baseUrl
}

func make(options: Options, completion: @escaping (Result<Data, Error>) -> Void) {
guard let request = options.toRequest(baseUrl: baseUrl) else {
completion(.failure(AppError.request))
return
}

let task = session.dataTask(with: request, completionHandler: { data, _, error in
if let data = data {
completion(.success(data))
} else if let error = error {
completion(.failure(error))
} else {
completion(.failure(AppError.unknown))
}
})

task.resume()
}

func makeJson(options: Options, completion: @escaping (Result<[String: Any], Error>) -> Void) {
make(options: options, completion: { result in
let mapped = result.flatMap({ data -> Result<[String: Any], Error> in
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let json = json as? [String: Any] {
return Result<[String: Any], Error>.success(json)
} else {
return Result<[String: Any], Error>.failure(AppError.parse)
}
} catch {
return Result<[String: Any], Error>.failure(error)
}
})

completion(mapped)
})
}
}

struct Options {
var path: String = ""
var httpMethod: HttpMethod = .get
var parameters: [String: Any] = [:]

func toRequest(baseUrl: URL) -> URLRequest? {
let url = baseUrl.appendingPathComponent(path)
let items: [URLQueryItem] = parameters.map({ tuple -> URLQueryItem in
return URLQueryItem(name: tuple.key, value: "\(tuple.value)")
})

var components = URLComponents(url: url, resolvingAgainstBaseURL: false)

if httpMethod == .get {
components?.queryItems = items
}

guard let finalUrl = components?.url else {
return nil
}

var request = URLRequest(url: finalUrl)

if httpMethod == .post {
let data = try? JSONSerialization.data(withJSONObject: parameters, options: [])
request.httpBody = data
}

request.httpMethod = httpMethod.rawValue
return request
}
}

enum AppError: Error {
case request
case unknown
case parse
}

enum HttpMethod: String {
case get = "GET"
case put = "PUT"
case post = "POST"
case patch = "PATCH"
}