Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • O openapi-generator
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 3,476
    • Issues 3,476
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 402
    • Merge requests 402
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • OpenAPI Tools
  • openapi-generator
  • Issues
  • #11651
Closed
Open
Issue created Feb 18, 2022 by Administrator@rootContributor

[REQ] [Swift] Abstract away URLSession

Created by: Czajnikowski

Is your feature request related to a problem? Please describe.

Yes. I'm trying to implement a caching mechanism that basically overrides the URLSession.dataTask and under the hood it takes the response from the cache (if available) and at the same time loads using the URLSession itself. Currently, the only way of changing the URLSession is via override of URLSessionRequestBuilder.createURLSession (well, I can always write my own RequestBuilder, but that'd be a lot of duplicated code). So to be able to implement my approach I'd have to subclass URLSession and override the dataTask.

The implementation would look like this:

class CacheFirstURLSession: URLSession {
    override func dataTask(
        with request: URLRequest,
        completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
    ) -> URLSessionDataTask {
        if let cachedResponse = configuration.urlCache?.cachedResponse(for: request) {
            completionHandler(cachedResponse.data, cachedResponse.response, nil)
        }

        return super.dataTask(with: request, completionHandler: completionHandler)
    }
}

This is not currently doable (as far as I know, URLSessions are not subclassable). I tried a couple of times to make a subclass, but it does not work - calling dataTask crashes the app. Probably because there's no way to inject configuration into a subclass.

Describe the solution you'd like

So I don't necessarily need a subclass of the URLSession, I can go with a wrapper like :

class URLSessionWrapper {
    private let wrapped: URLSession
    
    init(with wrapped: URLSession) {
        self.wrapped = wrapped
    }
}

extension URLSessionWrapper: DataTaskMaking {
    func dataTask(
        with request: URLRequest,
        completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
    ) -> URLSessionDataTask {
        if let cachedResponse = wrapped.configuration.urlCache?.cachedResponse(for: request) {
            completionHandler(cachedResponse.data, cachedResponse.response, nil)
        }

        return wrapped.dataTask(with: request, completionHandler: completionHandler)
    }
}

The only problem that I have now is that the URLSessionRequestBuilder.createURLSession returns the exact URLSession. If we'd be able to exchange it into a protocol then my problem is solved.

Here's an initial idea of what we'd have to do in the URLSessionImplementation.mustache:

Somewhere at the top (or bottom):

public protocol DataTaskMaking {
    func dataTask(
        with request: URLRequest,
        completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
    ) -> URLSessionDataTask
}

extension URLSession: DataTaskMaking {}

And then change the override:

{...} func createURLSession() -> DataTaskMaking {
    return defaultURLSession
}

Describe alternatives you've considered

So I tried solving the problem with subclassing and extensions which both don't seem to be feasible in our current architecture. Another possible option is to create my own own RequestBuilder, but that'd be a lot of duplicated code.

Additional context

Nope

Assignee
Assign to
Time tracking