Cannot convert return expression of type 'AnyPublisher<T, any Error> in Combine

2.1k Views Asked by At

I am new to combine . I created an generic function which return AnyPublisher with result (T) and Error(Service error) it the custom error message . I have the pass the urlSession with dataTaskPublisher and return the request. I am getting following error ..

Cannot convert return expression of type 'AnyPublisher<T, any Error>' to return type 'AnyPublisher<T, ServiceError>'

Here is the code for URL request.

extension URLRequest {
    static func getRequest(client: ServiceClient) ->URLRequest? {
        guard var urlComponents = URLComponents(string:client.baseUrl + client.path) else {
            return nil
        }
        urlComponents.query = "\(client.params)"
        guard let url = urlComponents.url else {
            return nil
        }
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = client.method
        return urlRequest
    }
}

Here is the code ..

class ServiceImpl: Service {

    let urlSesson = URLSession(configuration: .default)
    var dataTask: URLSessionDataTask?

    func fetchData<T:Codable>(client: ServiceClient, type:T.Type) -> AnyPublisher<T,ServiceError> {

        guard let request = URLRequest.getRequest(client: client) else {
            return Fail(outputType: type, failure: ServiceError.requestNotCreated(message: Constants.requestNotCreated)).eraseToAnyPublisher()
        }
        return urlSesson.dataTaskPublisher(for: request)
            .tryMap {
                guard let response = $0.response as? HTTPURLResponse, response.statusCode == 200 else {
                    throw ServiceError.errorWith(message: Constants.noResponse)
                }
                return $0.data
            }
            .decode(type: T.self, decoder: JSONDecoder())
            .receive(on: RunLoop.main)
            .eraseToAnyPublisher()
    }
}

Here is the screenshot of the error ..

enter image description here

1

There are 1 best solutions below

0
Dávid Pásztor On

The problem is that you are trying to return a Publisher whose Failure type is ServiceError, but your Combine pipeline can throw other error types as well. tryMap's Failure type is any Error, same as decode's.

To resolve the problem, you need to call mapError to ensure that you are always emitting a ServiceError in case of Failure.

  ...
  .receive(on: RunLoop.main)
  .mapError { error -> ServiceError in
    switch error {
    case let serviceError as ServiceError:
      return serviceError
    default:
      return .generic(error)
    }
  }
  .eraseToAnyPublisher()

Above code assumes that your ServiceError has a generic(Error) case. You can return any other case as well, I've just used this as an example since you haven't actually included your ServiceError definition in your question.

enum ServiceError: Error {
  case errorWith(message: String)
  case generic(Error)
}