Skip to content

[swift6] promote to beta and improve documentation #19856

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Oct 14, 2024
404 changes: 395 additions & 9 deletions docs/faq-generators.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ The following generators are available:
* [scalaz](generators/scalaz.md)
* [swift-combine](generators/swift-combine.md)
* [swift5](generators/swift5.md)
* [swift6 (experimental)](generators/swift6.md)
* [swift6 (beta)](generators/swift6.md)
* [typescript (experimental)](generators/typescript.md)
* [typescript-angular](generators/typescript-angular.md)
* [typescript-aurelia](generators/typescript-aurelia.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/generators/swift6.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: Documentation for the swift6 Generator
| Property | Value | Notes |
| -------- | ----- | ----- |
| generator name | swift6 | pass this to the generate command after -g |
| generator stability | EXPERIMENTAL | |
| generator stability | BETA | |
| generator type | CLIENT | |
| generator language | Swift | |
| generator default templating engine | mustache | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,11 @@ public void postProcess() {
System.out.println("# #");
System.out.println("# swift5 generator is contributed by Bruno Coelho (https://github.com/4brunu). #");
System.out.println("# Please support his work directly via https://paypal.com/paypalme/4brunu \uD83D\uDE4F #");
System.out.println("# #");
System.out.println("# There is a new `swift6` generator, that is currently in beta. #");
System.out.println("# Try it and give us your feedback. #");
System.out.println("# https://openapi-generator.tech/docs/generators/swift6 #");
System.out.println("# https://openapi-generator.tech/docs/faq-generators/#how-do-i-migrate-from-the-swift-5-generator-to-the-swift-6-generator");
System.out.println("################################################################################");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public Swift6ClientCodegen() {
this.useOneOfInterfaces = true;

generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.EXPERIMENTAL)
.stability(Stability.BETA)
.build();

outputFolder = "generated-code" + File.separator + "swift";
Expand Down
246 changes: 246 additions & 0 deletions modules/openapi-generator/src/main/resources/swift5/README.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,252 @@ Class | Method | HTTP request | Description

{{/authMethods}}

{{#useURLSession}}
### How do I implement bearer token authentication with URLSession on the Swift 5 API client?

First you subclass RequestBuilderFactory
```
class BearerRequestBuilderFactory: RequestBuilderFactory {
func getNonDecodableBuilder<T>() -> RequestBuilder<T>.Type {
BearerRequestBuilder<T>.self
}

func getBuilder<T: Decodable>() -> RequestBuilder<T>.Type {
BearerDecodableRequestBuilder<T>.self
}
}
```

Then you subclass URLSessionRequestBuilder and URLSessionDecodableRequestBuilder
```
class BearerRequestBuilder<T>: URLSessionRequestBuilder<T> {
@discardableResult
override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, ErrorResponse>) -> Void) -> RequestTask {

// Before making the request, we can validate if we have a bearer token to be able to make a request
BearerTokenHandler.refreshTokenIfDoesntExist {

// Here we make the request
super.execute(apiResponseQueue) { result in

switch result {
case .success:
// If we got a successful response, we send the response to the completion block
completion(result)

case let .failure(error):

// If we got a failure response, we will analyse the error to see what we should do with it
if case let ErrorResponse.error(_, data, response, error) = error {

// 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
BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
data: data,
response: response,
error: error
) { wasTokenRefreshed in

if wasTokenRefreshed {
// 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()
self.execute(apiResponseQueue, completion)
} else {
// If the token was not refreshed, it's because it was not a 401 error, so we send the response to the completion block
completion(result)
}
}
} else {
// If it's an unknown error, we send the response to the completion block
completion(result)
}

}
}
}

return requestTask
}
}

class BearerDecodableRequestBuilder<T: Decodable>: URLSessionDecodableRequestBuilder<T> {
@discardableResult
override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, ErrorResponse>) -> Void) -> RequestTask {
// Before making the request, we can validate if we have a bearer token to be able to make a request
BearerTokenHandler.refreshTokenIfDoesntExist {

// Here we make the request
super.execute(apiResponseQueue) { result in

switch result {
case .success:
// If we got a successful response, we send the response to the completion block
completion(result)

case let .failure(error):

// If we got a failure response, we will analyse the error to see what we should do with it
if case let ErrorResponse.error(_, data, response, error) = error {

// 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
BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
data: data,
response: response,
error: error
) { wasTokenRefreshed in

if wasTokenRefreshed {
// 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()
self.execute(apiResponseQueue, completion)
} else {
// If the token was not refreshed, it's because it was not a 401 error, so we send the response to the completion block
completion(result)
}
}
} else {
// If it's an unknown error, we send the response to the completion block
completion(result)
}

}
}
}

return requestTask
}
}

class BearerTokenHandler {
private static var bearerToken: String? = nil

static func refreshTokenIfDoesntExist(completionHandler: @escaping () -> Void) {
if bearerToken != nil {
completionHandler()
} else {
startRefreshingToken {
completionHandler()
}
}
}

static func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse?, error: Error?, completionHandler: @escaping (Bool) -> Void) {
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
startRefreshingToken {
completionHandler(true)
}
} else {
completionHandler(false)
}
}

private static func startRefreshingToken(completionHandler: @escaping () -> Void) {
// Get a bearer token
let dummyBearerToken = "..."

bearerToken = dummyBearerToken
PetstoreClientAPI.customHeaders["Authorization"] = "Bearer \(dummyBearerToken)"

completionHandler()
}
}
```

Then you assign the `BearerRequestBuilderFactory` to the property `requestBuilderFactory`.

`PetstoreClientAPI.requestBuilderFactory = BearerRequestBuilderFactory()`

The name `PetstoreClientAPI.requestBuilderFactory` will change depending on your project name.

Here is a working sample that put's together all of this.
[AppDelegate.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/urlsessionLibrary/SwaggerClientTests/SwaggerClient/AppDelegate.swift)
[BearerDecodableRequestBuilder.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/urlsessionLibrary/SwaggerClientTests/SwaggerClient/BearerDecodableRequestBuilder.swift)
{{/useURLSession}}
{{#useAlamofire}}
### How do I implement bearer token authentication with Alamofire on the Swift 5 API client?

First you subclass RequestBuilderFactory
```
class BearerRequestBuilderFactory: RequestBuilderFactory {
func getNonDecodableBuilder<T>() -> RequestBuilder<T>.Type {
BearerRequestBuilder<T>.self
}

func getBuilder<T: Decodable>() -> RequestBuilder<T>.Type {
BearerDecodableRequestBuilder<T>.self
}
}
```

Then you subclass AlamofireRequestBuilder and AlamofireDecodableRequestBuilder
```
class BearerRequestBuilder<T>: AlamofireRequestBuilder<T> {
override func createSessionManager() -> SessionManager {
let sessionManager = super.createSessionManager()

let bearerTokenHandler = BearerTokenHandler()
sessionManager.adapter = bearerTokenHandler
sessionManager.retrier = bearerTokenHandler

return sessionManager
}
}

class BearerDecodableRequestBuilder<T: Decodable>: AlamofireDecodableRequestBuilder<T> {
override func createSessionManager() -> SessionManager {
let sessionManager = super.createSessionManager()

let bearerTokenHandler = BearerTokenHandler()
sessionManager.adapter = bearerTokenHandler
sessionManager.retrier = bearerTokenHandler

return sessionManager
}
}

class BearerTokenHandler: RequestAdapter, RequestRetrier {
private static var bearerToken: String? = nil

func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
if let bearerToken = Self.bearerToken {
var urlRequest = urlRequest
urlRequest.setValue("Bearer \(bearerToken)", forHTTPHeaderField: "Authorization")
return urlRequest
}

return urlRequest
}

func should(_: SessionManager, retry request: Request, with _: Error, completion: @escaping RequestRetryCompletion) {
if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 {
Self.startRefreshingToken { isTokenRefreshed in
completion(isTokenRefreshed, 0.0)
}
} else {
completion(false, 0.0)
}
}

private static func startRefreshingToken(completionHandler: @escaping (Bool) -> Void) {
// Get a bearer token
let dummyBearerToken = "..."

bearerToken = dummyBearerToken
PetstoreClientAPI.customHeaders["Authorization"] = "Bearer \(dummyBearerToken)"

completionHandler(true)
}
}
```

Then you assign the `BearerRequestBuilderFactory` to the property `requestBuilderFactory`.

`PetstoreClientAPI.requestBuilderFactory = BearerRequestBuilderFactory()`

The name `PetstoreClientAPI.requestBuilderFactory` will change depending on your project name.

Here is a working sample that put's together all of this.
[AppDelegate.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/alamofireLibrary/SwaggerClientTests/SwaggerClient/AppDelegate.swift)
[BearerTokenHandler.swift](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/swift5/alamofireLibrary/SwaggerClientTests/SwaggerClient/BearerDecodableRequestBuilder.swift)
{{/useAlamofire}}

## Author

{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}}
Expand Down
Loading
Loading