This repository was archived by the owner on Apr 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAPIHandler.swift
203 lines (185 loc) · 8.89 KB
/
APIHandler.swift
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
//
// APIRequest.swift
// mobile
//
// Created by cb on 06.09.23.
//
import Foundation
class RequestHandler {
static func performRequest<T: Decodable>(
url: URL,
httpMethod: HTTPMethod,
accessToken: String? = nil, // Optional access token (e.g. verify, createAccount)
responseType: T.Type,
requestBody: Any? = nil, // Optional request body TODO: FIX TYPES
queryParameters: QueryParameters? = nil, // Optional query parameters
authentication: Authentication? = nil, // Optional authentication information
requestHeaders: RequestHeaders? = nil, // Optional request headers
completion: @escaping (Result<T, APIError>) -> Void
) {
// Create a URLComponents object to build the URL with query parameters
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
// Add query parameters if provided
if let queryParameters = queryParameters {
components?.queryItems = queryParameters.map { key, value in
URLQueryItem(name: key, value: value)
}
}
// Create the final URL with query parameters
if let finalURL = components?.url {
print("finalURL: \(finalURL)")
var request = URLRequest(url: finalURL)
request.httpMethod = String(describing: httpMethod)
if let accessToken {
request.addValue(accessToken, forHTTPHeaderField: "access_token")
}
// Set optional authentication information in request headers
if let authentication = authentication {
request.setValue(authentication.value, forHTTPHeaderField: authentication.field)
}
// Set optional request headers
requestHeaders?.forEach { field, value in
request.setValue(value, forHTTPHeaderField: field)
}
// Set optional request body
if let requestBody = requestBody {
do {
print("Requestbody: \(requestBody)")
let jsonData = try JSONSerialization.data(withJSONObject: requestBody)
request.httpBody = jsonData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
} catch {
completion(.failure(APIError.jsonEncodingError(error)))
return
}
}
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Error fetching data: \(error)")
completion(.failure(APIError.networkError(error)))
return
}
if let httpResponse = response as? HTTPURLResponse {
let statusCode = httpResponse.statusCode
print("HTTP Response Code: \(statusCode)")
print("HTTP Response: \(response.debugDescription)")
handleApiErrorsNew(data: data, statusCode: statusCode, completion: completion)
switch statusCode {
case 204:
completion(.failure(APIError.noContent(String(describing: T.self))))
case 200:
if let data = data {
do {
if let responseString = String(data: data, encoding: .utf8) {
print("Data as String: \(responseString)")
} else {
print("Failed to convert data to string")
}
let responseData = try JSONDecoder().decode(T.self, from: data)
completion(.success(responseData))
} catch {
print("JSON Error: \(error)")
completion(.failure(APIError.jsonParsingError(error)))
}
} else {
completion(.failure(APIError.unknownError))
}
default:
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print("Error JSON = \(json)")
} catch {
completion(.failure(APIError.jsonParsingError(error)))
}
} else {
completion(.failure(APIError.unknownError))
}
}
}
}.resume()
} else {
completion(.failure(APIError.unknownError))
}
}
static func handleApiErrorsNew<T: Decodable>(data: Data?, statusCode: Int, completion: @escaping (Result<T, APIError>) -> Void) {
switch statusCode {
case 400:
break
case 401:
completion(.failure(APIError.authenticationError))
case 403:
completion(.failure(APIError.forbidden))
case 404:
completion(.failure(APIError.notFound))
case 422:
break
case 500:
completion(.failure(APIError.internalServerError))
default: completion(.failure(APIError.unknownError))
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let errorJSON = json as? [String: Any], let errors = errorJSON["errors"] as? [String] {
let errorMessage = errors.joined(separator: ". ")
completion(.failure(statusCode == 422 ? APIError.validationError(errorMessage) : APIError.argumentError(errorMessage)))
} else if let errorDict = json as? [String: Any] {
var errorMessages = [String]()
for errorKey in errorDict.keys {
if let errorTokenArray = errorDict[errorKey] as? [[String: Any]] {
let errorMessagesForKey = errorTokenArray.map { item in
if let description = item["description"] {
return "\n\(errorKey) (\(description))"
} else {
return ""
}
}
errorMessages.append(contentsOf: errorMessagesForKey)
}
}
if !errorMessages.isEmpty {
let errorMessage = errorMessages.filter { !$0.isEmpty }.joined(separator: ". ")
completion(.failure(statusCode == 422 ? APIError.validationError(errorMessage) : APIError.argumentError(errorMessage)))
} else {
completion(.failure(APIError.unknownError))
}
} else {
completion(.failure(APIError.unknownError))
}
} catch {
completion(.failure(APIError.jsonParsingError(error)))
}
} else {
completion(.failure(APIError.unknownError))
}
}
// TODO: @deprecated: Remove once TokenHandler has been updated
static func handleApiErrors<T: Decodable>(json: Any, errorKeys: [String], statusCode: Int, completion: @escaping (Result<T, APIError>) -> Void) {
if let errorDict = json as? [String: Any] {
var errorMessages: [String] = []
for errorKey in errorKeys {
if let errorTokenArray = errorDict[errorKey] as? [[String: Any]] {
let errorMessagesForKey = errorTokenArray.map { item in
return "\(item["description"] ?? "") (\(errorKey): \(item["error"] ?? ""))"
}
errorMessages.append(contentsOf: errorMessagesForKey)
}
}
if !errorMessages.isEmpty {
let errorMessage = errorMessages.joined(separator: ", ")
print("Error Message: \(errorMessage)")
let error = APIError.validationError(errorMessage)
completion(.failure(error))
} else {
// Handle unexpected error response format
let error = APIError.unknownError
completion(.failure(error))
}
} else {
// Handle unexpected error response format
let error = APIError.unknownError
completion(.failure(error))
}
}
}