Skip to content

Commit 2354d40

Browse files
authored
[swift6] promote to beta and improve documentation (#19856)
* [swift6] promote to beta * [swift6] format code * [swift] authentication docs * [swift] update docs * [swift] update docs * [swift] update docs * [swift] update docs * [swift] update docs * [swift] update docs * [swift] update docs * [swift] update docs * [swift] update docs * [swift] update docs
1 parent b5b760a commit 2354d40

103 files changed

Lines changed: 3863 additions & 812 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/faq-generators.md

Lines changed: 395 additions & 9 deletions
Large diffs are not rendered by default.

docs/generators.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ The following generators are available:
6666
* [scalaz](generators/scalaz.md)
6767
* [swift-combine](generators/swift-combine.md)
6868
* [swift5](generators/swift5.md)
69-
* [swift6 (experimental)](generators/swift6.md)
69+
* [swift6 (beta)](generators/swift6.md)
7070
* [typescript (experimental)](generators/typescript.md)
7171
* [typescript-angular](generators/typescript-angular.md)
7272
* [typescript-aurelia](generators/typescript-aurelia.md)

docs/generators/swift6.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ title: Documentation for the swift6 Generator
77
| Property | Value | Notes |
88
| -------- | ----- | ----- |
99
| generator name | swift6 | pass this to the generate command after -g |
10-
| generator stability | EXPERIMENTAL | |
10+
| generator stability | BETA | |
1111
| generator type | CLIENT | |
1212
| generator language | Swift | |
1313
| generator default templating engine | mustache | |

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,11 @@ public void postProcess() {
13051305
System.out.println("# #");
13061306
System.out.println("# swift5 generator is contributed by Bruno Coelho (https://github.com/4brunu). #");
13071307
System.out.println("# Please support his work directly via https://paypal.com/paypalme/4brunu \uD83D\uDE4F #");
1308+
System.out.println("# #");
1309+
System.out.println("# There is a new `swift6` generator, that is currently in beta. #");
1310+
System.out.println("# Try it and give us your feedback. #");
1311+
System.out.println("# https://openapi-generator.tech/docs/generators/swift6 #");
1312+
System.out.println("# https://openapi-generator.tech/docs/faq-generators/#how-do-i-migrate-from-the-swift-5-generator-to-the-swift-6-generator");
13081313
System.out.println("################################################################################");
13091314
}
13101315

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public Swift6ClientCodegen() {
138138
this.useOneOfInterfaces = true;
139139

140140
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
141-
.stability(Stability.EXPERIMENTAL)
141+
.stability(Stability.BETA)
142142
.build();
143143

144144
outputFolder = "generated-code" + File.separator + "swift";

modules/openapi-generator/src/main/resources/swift5/README.mustache

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,252 @@ Class | Method | HTTP request | Description
7878

7979
{{/authMethods}}
8080

81+
{{#useURLSession}}
82+
### How do I implement bearer token authentication with URLSession on the Swift 5 API client?
83+
84+
First you subclass RequestBuilderFactory
85+
```
86+
class BearerRequestBuilderFactory: RequestBuilderFactory {
87+
func getNonDecodableBuilder<T>() -> RequestBuilder<T>.Type {
88+
BearerRequestBuilder<T>.self
89+
}
90+
91+
func getBuilder<T: Decodable>() -> RequestBuilder<T>.Type {
92+
BearerDecodableRequestBuilder<T>.self
93+
}
94+
}
95+
```
96+
97+
Then you subclass URLSessionRequestBuilder and URLSessionDecodableRequestBuilder
98+
```
99+
class BearerRequestBuilder<T>: URLSessionRequestBuilder<T> {
100+
@discardableResult
101+
override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, ErrorResponse>) -> Void) -> RequestTask {
102+
103+
// Before making the request, we can validate if we have a bearer token to be able to make a request
104+
BearerTokenHandler.refreshTokenIfDoesntExist {
105+
106+
// Here we make the request
107+
super.execute(apiResponseQueue) { result in
108+
109+
switch result {
110+
case .success:
111+
// If we got a successful response, we send the response to the completion block
112+
completion(result)
113+
114+
case let .failure(error):
115+
116+
// If we got a failure response, we will analyse the error to see what we should do with it
117+
if case let ErrorResponse.error(_, data, response, error) = error {
118+
119+
// If the error is an ErrorResponse.error() we will analyse it to see if it's a 401, and if it's a 401, we will refresh the token and retry the request
120+
BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
121+
data: data,
122+
response: response,
123+
error: error
124+
) { wasTokenRefreshed in
125+
126+
if wasTokenRefreshed {
127+
// If the token was refreshed, it's because it was a 401 error, so we refreshed the token, and we are going to retry the request by calling self.execute()
128+
self.execute(apiResponseQueue, completion)
129+
} else {
130+
// If the token was not refreshed, it's because it was not a 401 error, so we send the response to the completion block
131+
completion(result)
132+
}
133+
}
134+
} else {
135+
// If it's an unknown error, we send the response to the completion block
136+
completion(result)
137+
}
138+
139+
}
140+
}
141+
}
142+
143+
return requestTask
144+
}
145+
}
146+
147+
class BearerDecodableRequestBuilder<T: Decodable>: URLSessionDecodableRequestBuilder<T> {
148+
@discardableResult
149+
override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, ErrorResponse>) -> Void) -> RequestTask {
150+
// Before making the request, we can validate if we have a bearer token to be able to make a request
151+
BearerTokenHandler.refreshTokenIfDoesntExist {
152+
153+
// Here we make the request
154+
super.execute(apiResponseQueue) { result in
155+
156+
switch result {
157+
case .success:
158+
// If we got a successful response, we send the response to the completion block
159+
completion(result)
160+
161+
case let .failure(error):
162+
163+
// If we got a failure response, we will analyse the error to see what we should do with it
164+
if case let ErrorResponse.error(_, data, response, error) = error {
165+
166+
// If the error is an ErrorResponse.error() we will analyse it to see if it's a 401, and if it's a 401, we will refresh the token and retry the request
167+
BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
168+
data: data,
169+
response: response,
170+
error: error
171+
) { wasTokenRefreshed in
172+
173+
if wasTokenRefreshed {
174+
// If the token was refreshed, it's because it was a 401 error, so we refreshed the token, and we are going to retry the request by calling self.execute()
175+
self.execute(apiResponseQueue, completion)
176+
} else {
177+
// If the token was not refreshed, it's because it was not a 401 error, so we send the response to the completion block
178+
completion(result)
179+
}
180+
}
181+
} else {
182+
// If it's an unknown error, we send the response to the completion block
183+
completion(result)
184+
}
185+
186+
}
187+
}
188+
}
189+
190+
return requestTask
191+
}
192+
}
193+
194+
class BearerTokenHandler {
195+
private static var bearerToken: String? = nil
196+
197+
static func refreshTokenIfDoesntExist(completionHandler: @escaping () -> Void) {
198+
if bearerToken != nil {
199+
completionHandler()
200+
} else {
201+
startRefreshingToken {
202+
completionHandler()
203+
}
204+
}
205+
}
206+
207+
static func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse?, error: Error?, completionHandler: @escaping (Bool) -> Void) {
208+
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
209+
startRefreshingToken {
210+
completionHandler(true)
211+
}
212+
} else {
213+
completionHandler(false)
214+
}
215+
}
216+
217+
private static func startRefreshingToken(completionHandler: @escaping () -> Void) {
218+
// Get a bearer token
219+
let dummyBearerToken = "..."
220+
221+
bearerToken = dummyBearerToken
222+
PetstoreClientAPI.customHeaders["Authorization"] = "Bearer \(dummyBearerToken)"
223+
224+
completionHandler()
225+
}
226+
}
227+
```
228+
229+
Then you assign the `BearerRequestBuilderFactory` to the property `requestBuilderFactory`.
230+
231+
`PetstoreClientAPI.requestBuilderFactory = BearerRequestBuilderFactory()`
232+
233+
The name `PetstoreClientAPI.requestBuilderFactory` will change depending on your project name.
234+
235+
Here is a working sample that put's together all of this.
236+
[AppDelegate.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/urlsessionLibrary/SwaggerClientTests/SwaggerClient/AppDelegate.swift)
237+
[BearerDecodableRequestBuilder.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/urlsessionLibrary/SwaggerClientTests/SwaggerClient/BearerDecodableRequestBuilder.swift)
238+
{{/useURLSession}}
239+
{{#useAlamofire}}
240+
### How do I implement bearer token authentication with Alamofire on the Swift 5 API client?
241+
242+
First you subclass RequestBuilderFactory
243+
```
244+
class BearerRequestBuilderFactory: RequestBuilderFactory {
245+
func getNonDecodableBuilder<T>() -> RequestBuilder<T>.Type {
246+
BearerRequestBuilder<T>.self
247+
}
248+
249+
func getBuilder<T: Decodable>() -> RequestBuilder<T>.Type {
250+
BearerDecodableRequestBuilder<T>.self
251+
}
252+
}
253+
```
254+
255+
Then you subclass AlamofireRequestBuilder and AlamofireDecodableRequestBuilder
256+
```
257+
class BearerRequestBuilder<T>: AlamofireRequestBuilder<T> {
258+
override func createSessionManager() -> SessionManager {
259+
let sessionManager = super.createSessionManager()
260+
261+
let bearerTokenHandler = BearerTokenHandler()
262+
sessionManager.adapter = bearerTokenHandler
263+
sessionManager.retrier = bearerTokenHandler
264+
265+
return sessionManager
266+
}
267+
}
268+
269+
class BearerDecodableRequestBuilder<T: Decodable>: AlamofireDecodableRequestBuilder<T> {
270+
override func createSessionManager() -> SessionManager {
271+
let sessionManager = super.createSessionManager()
272+
273+
let bearerTokenHandler = BearerTokenHandler()
274+
sessionManager.adapter = bearerTokenHandler
275+
sessionManager.retrier = bearerTokenHandler
276+
277+
return sessionManager
278+
}
279+
}
280+
281+
class BearerTokenHandler: RequestAdapter, RequestRetrier {
282+
private static var bearerToken: String? = nil
283+
284+
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
285+
if let bearerToken = Self.bearerToken {
286+
var urlRequest = urlRequest
287+
urlRequest.setValue("Bearer \(bearerToken)", forHTTPHeaderField: "Authorization")
288+
return urlRequest
289+
}
290+
291+
return urlRequest
292+
}
293+
294+
func should(_: SessionManager, retry request: Request, with _: Error, completion: @escaping RequestRetryCompletion) {
295+
if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 {
296+
Self.startRefreshingToken { isTokenRefreshed in
297+
completion(isTokenRefreshed, 0.0)
298+
}
299+
} else {
300+
completion(false, 0.0)
301+
}
302+
}
303+
304+
private static func startRefreshingToken(completionHandler: @escaping (Bool) -> Void) {
305+
// Get a bearer token
306+
let dummyBearerToken = "..."
307+
308+
bearerToken = dummyBearerToken
309+
PetstoreClientAPI.customHeaders["Authorization"] = "Bearer \(dummyBearerToken)"
310+
311+
completionHandler(true)
312+
}
313+
}
314+
```
315+
316+
Then you assign the `BearerRequestBuilderFactory` to the property `requestBuilderFactory`.
317+
318+
`PetstoreClientAPI.requestBuilderFactory = BearerRequestBuilderFactory()`
319+
320+
The name `PetstoreClientAPI.requestBuilderFactory` will change depending on your project name.
321+
322+
Here is a working sample that put's together all of this.
323+
[AppDelegate.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/alamofireLibrary/SwaggerClientTests/SwaggerClient/AppDelegate.swift)
324+
[BearerTokenHandler.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/alamofireLibrary/SwaggerClientTests/SwaggerClient/BearerDecodableRequestBuilder.swift)
325+
{{/useAlamofire}}
326+
81327
## Author
82328

83329
{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}}

0 commit comments

Comments
 (0)