kean / Get

Web API client built using async/await
MIT License
937 stars 74 forks source link

APIClient v2 #36

Closed kean closed 2 years ago

kean commented 2 years ago

I'm considering redesigning the APIClient public API for Get 1.0. It was initially designed to accommodate only the most basic scenarios, but I'd like it to be more flexible. Here is a draft of a new API.

public protocol APIClient2 {
    // No changes in these APIs, except for removal of magic `Response<Data>` and `Response<String>` support (see p3)
    func send<T: Decodable>(_ request: Request<T>) async throws -> Response<T>
    func send<T: Decodable>(_ request: Request<T?>) async throws -> Response<T?>
    @discardableResult func send(_ request: Request<Void>) async throws -> Response<Void>

    // Getting decoded response for `URL`/`URLRequest`
    func value<T: Decodable>(_ type: T.Type, for url: URL) async throws -> Response<T>
    func value<T: Decodable>(_ type: T.Type, for urlRequest: URLRequest) async throws -> Response<T>

    // Getting raw response `Data` (no decoding)
    func data(for url: URL) async throws -> Response<Data>
    func data(for urlRequest: URLRequest) async throws -> Response<Data>
    func data<T>(for request: Request<T>) async throws -> Response<Data>

    // Getting response `String` (no decoding except for .utf8 decoding)
    func string(for url: URL) async throws -> Response<String>
    func string(for urlRequest: URLRequest) async throws -> Response<String>
    func string<T>(for request: Request<T>) async throws -> Response<String>

    // (?) Add `URLSessionDataDelegate` to all APIs too for pogress reporting, etc.
    func response<T: Decodable>(for request: Request<T>, delegate: URLSessionDataDelegate) async throws -> Response<T>
}

Main Changes

  1. You can now use URL and URLRequest in addition to Request
  2. Pass URLSessionDataDelegate for progress reporting, per-task auth, etc.
  3. Remove magic Response<Data> and Response<String> support from send(_:). If you use send(_:), they'll just use the default Decodable implementation for these types. You'll need to call data(for:) or string(for:) instead – faster, more discoverable, self-documenting
kean commented 2 years ago

With the new APIs, you'll be able to use URLRequest, but what if you want to, let's say, change only the cache policy?

func send<Response>(
    _ request: Request<Response>,
    cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy
) async throws -> Response where Response: Decodable
kean commented 2 years ago

Another options to cover the latest requirement:

public struct RequestOptions {
    public var cachePolicy: URLRequest.CachePolicy?
    public var timeoutInterval: TimeInterval?

    // Etc
}

public protocol APIClient2 {
    public func send<T: Decodable>(
        _ request: Request<T>,
        options: RequestOptions = .init(),
        delegate: URLSessionDataDelegate? = nil
    ) async throws -> Response<T>
}
kean commented 2 years ago

Going with RequestOptions and also a low-level API to send URLRequest and decoding a request.