Ilkhom Khodjaev
Ilkhom Khodjaev

Reputation: 99

How to refactor converting Generic typed results to Solid struct in Combine

I have this ugly construction of returning generic type as solid model, does anyone have ideas on refactoring to be more declarative?

extension NetworkingService {
    
    func login(email: String, password: String) -> AnyPublisher<UserModel, APIError> {
        func convertToModel() -> AnyPublisher<ResponseObject<UserResponse>, APIError> {
            return request(with: AuthRequests.login(email: email, password: password)).eraseToAnyPublisher()
        }
        return convertToModel().map { UserModel(with: $0.data!) }.eraseToAnyPublisher()
    }
    
    func request<T>(with endpoint: Endpoint) -> AnyPublisher<T, APIError> where T : Decodable {
        //... returning any publisher with Decodable
    }
}

Upvotes: 0

Views: 69

Answers (1)

Daniel T.
Daniel T.

Reputation: 33979

Your request<T>(with:) function only uses the T in the return. Although this is doable, it means that the calling code must call out what T is which is what is making your calling code "ugly".

The impression I get from what you posted is that the only thing your Endpoint type does is create a URLRequest. If it also defined the shape of the expected return data, then the entire construct would be cleaner.

I suggesting making your Endpoint type look like this:

public struct Endpoint<Response> {
    public let request: URLRequest
    public let response: (Data) throws -> Response

    public init(request: URLRequest, response: @escaping (Data) throws -> Response) {
        self.request = request
        self.response = response
    }
}

Then, with suitable updates elsewhere, you could do something like this:

extension NetworkingService {

    func login(email: String, password: String) -> AnyPublisher<UserModel, APIError> {
        request(with: AuthRequests.login(email: email, password: password))
            .map { $0.response.model }
            .eraseToAnyPublisher()
    }

    func request<T>(with endpoint: Endpoint<T>) -> AnyPublisher<T, APIError> where T: Decodable {
        //... returning any publisher with Decodable
    }
}

Upvotes: 1

Related Questions