Skip to content

Commit

Permalink
Implement cancellation of URLSession requests for Combine & RxSwift
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhorvath committed Nov 14, 2021
1 parent d91ff3a commit 31f75b4
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ import Vapor
headers[header] = value
}
}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} func execute(_ apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue, _ completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) { }
{{#useURLSession}}{{^useVapor}}{{^usePromiseKit}}{{^useResult}}{{^useAsyncAwait}}
@discardableResult{{/useAsyncAwait}}{{/useResult}}{{/usePromiseKit}}{{/useVapor}}{{/useURLSession}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} func execute(_ apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue, _ completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {{#useURLSession}}{{^useVapor}}{{^usePromiseKit}}{{^useResult}}{{^useAsyncAwait}}-> URLSessionDataTask? {{/useAsyncAwait}}{{/useResult}}{{/usePromiseKit}}{{/useVapor}}{{/useURLSession}}{
{{#useURLSession}}{{^useVapor}}{{^usePromiseKit}}{{^useResult}}{{^useAsyncAwait}}return nil{{/useAsyncAwait}}{{/useResult}}{{/usePromiseKit}}{{/useVapor}}{{/useURLSession}}
}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func addHeader(name: String, value: String) -> Self {
if !value.isEmpty {
Expand Down
25 changes: 17 additions & 8 deletions modules/openapi-generator/src/main/resources/swift5/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ extension {{projectName}}API {
*/
{{#isDeprecated}}
@available(*, deprecated, message: "This operation is deprecated.")
{{/isDeprecated}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue, completion: @escaping ((_ data: {{{returnType}}}{{^returnType}}Void{{/returnType}}?, _ error: Error?) -> Void)) {
{{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}).execute(apiResponseQueue) { result in
{{/isDeprecated}}{{^useAlamofire}}
@discardableResult{{/useAlamofire}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue, completion: @escaping ((_ data: {{{returnType}}}{{^returnType}}Void{{/returnType}}?, _ error: Error?) -> Void)){{^useAlamofire}} -> URLSessionDataTask?{{/useAlamofire}} {
{{^useAlamofire}}return {{/useAlamofire}}{{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}).execute(apiResponseQueue) { result in
switch result {
{{#returnType}}
case let .success(response):
Expand Down Expand Up @@ -122,8 +123,8 @@ extension {{projectName}}API {
@available(*, deprecated, message: "This operation is deprecated.")
{{/isDeprecated}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue) -> Observable<{{{returnType}}}{{^returnType}}Void{{/returnType}}> {
return Observable.create { observer -> Disposable in
{{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}).execute(apiResponseQueue) { result in
{{#useURLSession}}return {{/useURLSession}}Observable.create { observer -> Disposable in
{{#useURLSession}}let dataTask = {{/useURLSession}}{{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}).execute(apiResponseQueue) { result in
switch result {
{{#returnType}}
case let .success(response):
Expand All @@ -138,7 +139,10 @@ extension {{projectName}}API {
}
observer.onCompleted()
}
return Disposables.create()

return Disposables.create{{^useURLSession}}(){{/useURLSession}}{{#useURLSession}} {
dataTask?.cancel()
}{{/useURLSession}}
}
}
{{/useRxSwift}}
Expand All @@ -157,8 +161,9 @@ extension {{projectName}}API {
{{/isDeprecated}}
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue) -> AnyPublisher<{{{returnType}}}{{^returnType}}Void{{/returnType}}, Error> {
{{#useURLSession}}var dataTask: URLSessionDataTask?{{/useURLSession}}
return Future<{{{returnType}}}{{^returnType}}Void{{/returnType}}, Error> { promise in
{{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}).execute(apiResponseQueue) { result in
{{#useURLSession}}dataTask = {{/useURLSession}}{{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}).execute(apiResponseQueue) { result in
switch result {
{{#returnType}}
case let .success(response):
Expand All @@ -172,7 +177,11 @@ extension {{projectName}}API {
promise(.failure(error))
}
}
}.eraseToAnyPublisher()
}{{#useURLSession}}
.handleEvents(receiveCancel: {
dataTask?.cancel()
}){{/useURLSession}}
.eraseToAnyPublisher()
}
#endif
{{/useCombine}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ private var credentialStore = SynchronizedDictionary<Int, URLCredential>()

return modifiedRequest
}

override {{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} func execute(_ apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue, _ completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
{{^useVapor}}{{^usePromiseKit}}{{^useResult}}{{^useAsyncAwait}}
@discardableResult{{/useAsyncAwait}}{{/useResult}}{{/usePromiseKit}}{{/useVapor}}
override {{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} func execute(_ apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue, _ completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {{^useVapor}}{{^usePromiseKit}}{{^useResult}}{{^useAsyncAwait}}-> URLSessionDataTask? {{/useAsyncAwait}}{{/useResult}}{{/usePromiseKit}}{{/useVapor}}{
let urlSession = createURLSession()
guard let xMethod = HTTPMethod(rawValue: method) else {
Expand Down Expand Up @@ -170,11 +171,13 @@ private var credentialStore = SynchronizedDictionary<Int, URLCredential>()
credentialStore[dataTask.taskIdentifier] = credential

dataTask.resume()

{{^useVapor}}{{^usePromiseKit}}{{^useResult}}{{^useAsyncAwait}}
return dataTask{{/useAsyncAwait}}{{/useResult}}{{/usePromiseKit}}{{/useVapor}}
} catch {
apiResponseQueue.async {
completion(.failure(ErrorResponse.error(415, nil, nil, error)))
}
}{{^useVapor}}{{^usePromiseKit}}{{^useResult}}{{^useAsyncAwait}}
return nil{{/useAsyncAwait}}{{/useResult}}{{/usePromiseKit}}{{/useVapor}}
}
}

Expand Down

0 comments on commit 31f75b4

Please sign in to comment.