kean / Get

Web API client built using async/await
MIT License
943 stars 75 forks source link

Add `MultiAPIClientDelegate`, a delegate that forwards calls to multiple underlying delegates. #67

Closed grdsdev closed 2 years ago

grdsdev commented 2 years ago

Hi @kean hope you're doing great, first thanks for this awesome library, I'm using it on a lot of my projects.

And one thing I find myself always implementing is this MultiAPIClientDelegate.

An APIClientDelegate implementation that forwards calls to multiple underlying delegates, below is the implementation I use.

if you find this useful for adding to the library, I can PR it.

/// An ``APIClientDelegate`` that forward calls to multiple delegates in order.
public struct MultiAPIClientDelegate: APIClientDelegate {
    let delegates: [APIClientDelegate]

    public init(_ delegates: [APIClientDelegate]) {
        self.delegates = delegates
    }

    public func client(_ client: APIClient, willSendRequest request: inout URLRequest) async throws {
        for delegate in delegates {
            try await delegate.client(client, willSendRequest: &request)
        }
    }

    public func client(_ client: APIClient, shouldRetry task: URLSessionTask, error: Error, attempts: Int) async throws -> Bool {
        for delegate in delegates {
            if try await delegate.client(client, shouldRetry: task, error: error, attempts: attempts) {
                return true
            }
        }
        return false
    }

    public func client(_ client: APIClient, validateResponse response: HTTPURLResponse, data: Data, task: URLSessionTask) throws {
        for delegate in delegates {
            try delegate.client(client, validateResponse: response, data: data, task: task)
        }
    }

    public func client<T>(_ client: APIClient, makeURLForRequest request: Request<T>) throws -> URL? {
        for delegate in delegates {
            if let url = try delegate.client(client, makeURLForRequest: request) {
                return url
            }
        }
        return nil
    }

    public func client<T>(_ client: APIClient, encoderForRequest request: Request<T>) -> JSONEncoder? {
        for delegate in delegates {
            if let encoder = delegate.client(client, encoderForRequest: request) {
                return encoder
            }
        }
        return nil
    }

    public func client<T>(_ client: APIClient, decoderForRequest request: Request<T>) -> JSONDecoder? {
        for delegate in delegates {
            if let decoder = delegate.client(client, decoderForRequest: request) {
                return decoder
            }
        }
        return nil
    }
}
kean commented 2 years ago

Thanks, @grsouza! Yeah, I've done that before for other delegates. But I think it's a niche use-case for Get. One of the main advantages of Get is how small and focused the API is, so I'd prefer to avoid adding new APIs unless it's something that can't be implemented "outside" of the framework.

grdsdev commented 2 years ago

I created a new repository for holding some common extensions to Get library, and added this type to it https://github.com/binaryscraping/GetExtensions

Will close this, thanks.