-
Notifications
You must be signed in to change notification settings - Fork 2
/
BKTError.swift
229 lines (211 loc) · 9.22 KB
/
BKTError.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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import Foundation
public enum BKTError: Error, Equatable {
case badRequest(message: String)
case unauthorized(message: String)
case forbidden(message: String)
case notFound(message: String)
case clientClosed(message: String)
case unavailable(message: String)
case apiServer(message: String)
case redirectRequest(message: String, statusCode: Int)
case payloadTooLarge(message: String)
case invalidHttpMethod(message: String)
// network errors
case timeout(message: String, error: Error, timeoutMillis: Int64)
case network(message: String, error: Error)
// sdk errors
case illegalArgument(message: String)
case illegalState(message: String)
// unknown errors
case unknownServer(message: String, error: Error, statusCode: Int)
case unknown(message: String, error: Error)
public static func == (lhs: BKTError, rhs: BKTError) -> Bool {
switch (lhs, rhs) {
case (.badRequest(let m1), .badRequest(let m2)),
(.unauthorized(let m1), .unauthorized(let m2)),
(.forbidden(let m1), .forbidden(let m2)),
(.notFound(let m1), .notFound(let m2)),
(.clientClosed(let m1), .clientClosed(let m2)),
(.unavailable(let m1), .unavailable(let m2)),
(.apiServer(let m1), .apiServer(let m2)),
(.illegalArgument(let m1), .illegalArgument(let m2)),
(.illegalState(let m1), .illegalState(let m2)),
(.payloadTooLarge(let m1), .payloadTooLarge(let m2)),
(.invalidHttpMethod(let m1), .invalidHttpMethod(let m2)):
return m1 == m2
case (.timeout(let m1, _, let t1), .timeout(let m2, _, let t2)):
return t1 == t2 && m1 == m2
case (.network(let m1, _), .network(let m2, _)),
(.unknown(let m1, _), .unknown(let m2, _)):
return m1 == m2
case (.unknownServer(let m1, _, let c1), .unknownServer(let m2, _, let c2)),
(.redirectRequest(let m1, let c1), .redirectRequest(let m2, let c2)):
return m1 == m2 && c1 == c2
default:
return false
}
}
}
extension BKTError : LocalizedError {
internal init(error: Error) {
if let bktError = error as? BKTError {
self = bktError
return
}
if let responseError = error as? ResponseError {
switch responseError {
case .unacceptableCode(let code, let errorResponse):
switch code {
// Update error metrics report
// https://github.com/bucketeer-io/ios-client-sdk/issues/65
case 300..<400:
self = .redirectRequest(message: errorResponse?.error.message ?? "RedirectRequest error", statusCode: code)
case 400:
self = .badRequest(message: errorResponse?.error.message ?? "BadRequest error")
case 401:
self = .unauthorized(message: errorResponse?.error.message ?? "Unauthorized error")
case 403:
self = .forbidden(message: errorResponse?.error.message ?? "Forbidden error")
case 404:
self = .notFound(message: errorResponse?.error.message ?? "NotFound error")
case 405:
self = .invalidHttpMethod(message: errorResponse?.error.message ?? "InvalidHttpMethod error")
case 408:
self = .timeout(message: errorResponse?.error.message ?? "RequestTimeout error: 408", error: responseError, timeoutMillis: 0)
case 413:
self = .payloadTooLarge(message: errorResponse?.error.message ?? "PayloadTooLarge error")
case 499:
self = .clientClosed(message: errorResponse?.error.message ?? "Client Closed Request error")
case 500:
self = .apiServer(message: errorResponse?.error.message ?? "InternalServer error")
case 502, 503, 504:
self = .unavailable(message: errorResponse?.error.message ?? "Unavailable error")
default:
var message: String = "no error body"
if let errorResponse = errorResponse {
message = "[\(errorResponse.error.code)] \(errorResponse.error.message)"
}
self = .unknownServer(message: "Unknown server error: \(message)", error: error, statusCode: code)
}
case .unknown(let urlResponse):
var message: String = "no response"
if let urlResponse = urlResponse as? HTTPURLResponse {
message = "[\(urlResponse.statusCode)] \(urlResponse)"
}
self = .network(message: "Network connection error: \(message)", error: error)
case .invalidJSONResponse(let code, _):
let message: String = "invaild JSON response for status \(code)"
self = .unknownServer(message: "Unknown server error: \(message)", error: error, statusCode: code)
}
return
}
let nsError = error as NSError
if nsError.domain == NSURLErrorDomain {
let nsErrorCode = nsError.code
if BKTError.networkErrorCodes.contains(nsErrorCode) {
self = .network(message: "Network connection error: \(error)", error: error)
} else if nsErrorCode == NSURLErrorTimedOut {
self = .timeout(message: "Request timeout error: \(error)", error: error, timeoutMillis: 0)
} else {
self = .unknown(message: "Unknown error: \(error)", error: error)
}
} else {
self = .unknown(message: "Unknown error: \(error)", error: error)
}
}
/// A localized message describing what error occurred.
public var errorDescription: String? {
switch self {
case .badRequest(message: let message):
return message
case .unauthorized(message: let message):
return message
case .forbidden(message: let message):
return message
case .notFound(message: let message):
return message
case .clientClosed(message: let message):
return message
case .unavailable(message: let message):
return message
case .apiServer(message: let message):
return message
case .timeout(message: let message, _, _):
return message
case .network(message: let message, _):
return message
case .illegalArgument(message: let message):
return message
case .illegalState(message: let message):
return message
case .unknownServer(message: let message, _, _):
return message
case .unknown(message: let message, _):
return message
case .redirectRequest(message: let message, _):
return message
case .payloadTooLarge(message: let message):
return message
case .invalidHttpMethod(message: let message):
return message
}
}
/// A localized message describing the reason for the failure.
public var failureReason: String? {
switch self {
case .badRequest,
.unauthorized,
.forbidden,
.notFound,
.clientClosed,
.unavailable,
.apiServer,
.illegalArgument,
.illegalState,
.redirectRequest,
.payloadTooLarge,
.invalidHttpMethod:
return nil
case .timeout(message: _, error: let error, _):
// note: create description for unknown error type
return "\(error)"
case .network(message: _, error: let error):
return "\(error)"
case .unknownServer(message: _, error: let error, _):
return "\(error)"
case .unknown(message: _, error: let error):
return "\(error)"
}
}
}
extension BKTError {
// full list of NSURLError https://developer.apple.com/documentation/foundation/nserror/1448136-nserror_codes#3139076
static let networkErrorCodes = [
NSURLErrorBadURL,
NSURLErrorUnsupportedURL,
NSURLErrorNotConnectedToInternet,
NSURLErrorNetworkConnectionLost,
NSURLErrorCannotFindHost,
NSURLErrorCannotConnectToHost,
NSURLErrorDNSLookupFailed,
// Router, gateway error
NSURLErrorHTTPTooManyRedirects,
NSURLErrorRedirectToNonExistentLocation,
// SSL error
NSURLErrorAppTransportSecurityRequiresSecureConnection,
NSURLErrorSecureConnectionFailed,
NSURLErrorServerCertificateHasBadDate,
NSURLErrorServerCertificateUntrusted,
NSURLErrorServerCertificateHasUnknownRoot,
NSURLErrorServerCertificateNotYetValid,
NSURLErrorClientCertificateRejected,
NSURLErrorClientCertificateRequired,
// Data network errors 3G,4G...
NSURLErrorResourceUnavailable,
NSURLErrorCannotLoadFromNetwork,
NSURLErrorInternationalRoamingOff,
NSURLErrorCallIsActive,
NSURLErrorDataNotAllowed,
NSURLErrorRequestBodyStreamExhausted
]
}