Skip to content

Commit 6a21a2e

Browse files
authored
Merge pull request #141 from niscy-eudiw/feature/support-generic-error-response
Support generic error response
2 parents 044b27c + e466ef7 commit 6a21a2e

File tree

4 files changed

+200
-48
lines changed

4 files changed

+200
-48
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2023 European Commission
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import Foundation
17+
18+
public struct GenericErrorResponse: Codable, Sendable {
19+
public let error: String
20+
public let errorDescription: String?
21+
public let interval: TimeInterval?
22+
23+
private enum CodingKeys: String, CodingKey {
24+
case error
25+
case errorDescription = "error_description"
26+
case interval
27+
}
28+
29+
public init(
30+
error: String,
31+
errorDescription: String? = nil,
32+
interval: TimeInterval? = nil
33+
) {
34+
self.error = error
35+
self.errorDescription = errorDescription
36+
self.interval = interval
37+
}
38+
}

Sources/Utilities/RemoteDataAccess/Poster.swift

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ public enum PostError: Error {
1919
case invalidUrl
2020
case invalidResponse
2121
case networkError(Error)
22-
22+
2323
/**
2424
Provides a localized description of the post error.
25-
25+
2626
- Returns: A string describing the post error.
2727
*/
2828
public var localizedDescription: String {
@@ -38,34 +38,34 @@ public enum PostError: Error {
3838
}
3939

4040
public protocol Posting: Sendable {
41-
41+
4242
var session: Networking { get set }
43-
43+
4444
/**
4545
Performs a POST request with the provided URLRequest.
46-
46+
4747
- Parameters:
48-
- request: The URLRequest to be used for the POST request.
49-
48+
- request: The URLRequest to be used for the POST request.
49+
5050
- Returns: A Result type with the response data or an error.
5151
*/
5252
func post<Response: Codable>(request: URLRequest) async -> Result<Response, PostError>
53-
53+
5454
/**
5555
Performs a POST request with the provided URLRequest.
56-
56+
5757
- Parameters:
58-
- request: The URLRequest to be used for the POST request.
59-
58+
- request: The URLRequest to be used for the POST request.
59+
6060
- Returns: A Result type with a success boolean (based on status code) or an error.
6161
*/
6262
func check(key: String, request: URLRequest) async -> Result<(String, Bool), PostError>
6363
}
6464

6565
public struct Poster: Posting {
66-
66+
6767
public var session: Networking
68-
68+
6969
/**
7070
Initializes a Poster instance.
7171
*/
@@ -74,68 +74,57 @@ public struct Poster: Posting {
7474
) {
7575
self.session = session
7676
}
77-
77+
7878
/**
7979
Performs a POST request with the provided URLRequest.
80-
80+
8181
- Parameters:
82-
- request: The URLRequest to be used for the POST request.
83-
82+
- request: The URLRequest to be used for the POST request.
83+
8484
- Returns: A Result type with the response data or an error.
8585
*/
8686
public func post<Response: Codable>(request: URLRequest) async -> Result<Response, PostError> {
8787
do {
8888
let (data, _) = try await self.session.data(for: request)
8989
let object = try JSONDecoder().decode(Response.self, from: data)
90-
90+
9191
return .success(object)
92-
} catch let error as NSError {
93-
if error.domain == NSURLErrorDomain {
94-
return .failure(.networkError(error))
95-
} else {
96-
return .failure(.networkError(error))
97-
}
9892
} catch {
9993
return .failure(.networkError(error))
10094
}
10195
}
102-
96+
10397
/**
10498
Performs a POST request with the provided URLRequest.
105-
99+
106100
- Parameters:
107-
- request: The URLRequest to be used for the POST request.
108-
101+
- request: The URLRequest to be used for the POST request.
102+
109103
- Returns: A Result type with a success boolean (based on status code) or an error.
110104
*/
111105
public func check(key: String, request: URLRequest) async -> Result<(String, Bool), PostError> {
112106
do {
113-
114-
let (data, response) = try await self.session.data(for: request)
115-
116-
let string = String(data: data, encoding: .utf8)
117-
let dictionary = string?.toDictionary() ?? [:]
118-
let value = dictionary[key] as? String ?? ""
119-
let success = (response as? HTTPURLResponse)?.statusCode.isWithinRange(200...299) ?? false
120-
121-
return .success((value, success))
122-
} catch let error as NSError {
123-
if error.domain == NSURLErrorDomain {
124-
return .failure(.networkError(error))
125-
} else {
126-
return .failure(.networkError(error))
127-
}
107+
let (data, response) = try await session.data(for: request)
108+
let success = (response as? HTTPURLResponse)?
109+
.statusCode
110+
.isWithinRange(200...299) ?? false
111+
112+
let description = success
113+
? descriptionForKey(key, in: data)
114+
: errorDescription(in: data)
115+
116+
return .success((description, success))
128117
} catch {
129118
return .failure(.networkError(error))
130119
}
131120
}
132-
121+
133122
/**
134123
Performs a POST request with the provided URLRequest.
135-
124+
136125
- Parameters:
137-
- request: The URLRequest to be used for the POST request.
138-
126+
- request: The URLRequest to be used for the POST request.
127+
139128
- Returns: A String or an error.
140129
*/
141130
public func postString(request: URLRequest) async -> Result<String, PostError> {
@@ -145,7 +134,7 @@ public struct Poster: Posting {
145134
if !statusCode.isWithinRange(200...299) {
146135
return .failure(.invalidResponse)
147136
}
148-
137+
149138
if let string = String(data: data, encoding: .utf8) {
150139
return .success(string)
151140
} else {
@@ -156,3 +145,20 @@ public struct Poster: Posting {
156145
}
157146
}
158147
}
148+
149+
private extension Poster {
150+
private func descriptionForKey(_ key: String, in data: Data) -> String {
151+
// Avoid String -> Dict conversions; go straight from Data to [String: Any]
152+
guard
153+
let json = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any],
154+
let value = json[key] as? String
155+
else {
156+
return ""
157+
}
158+
return value
159+
}
160+
161+
private func errorDescription(in data: Data) -> String {
162+
(try? JSONDecoder().decode(GenericErrorResponse.self, from: data))?.errorDescription ?? ""
163+
}
164+
}

0 commit comments

Comments
 (0)