Home > Mobile >  Using flatMap in RestManager with generic function not working
Using flatMap in RestManager with generic function not working

Time:03-12

I am new to Combine, so I wanted to create class RestManager for networking with generic fetchData function. Function is returning AnyPublisher<Result<T, ErrorType>, Never> where ErrorType is enum with .noInternetConnection, .empty and .general cases.

I tried to use URLSession with dataTaskPublisher and flatMap

func fetchData<T: Decodable>(url: URL) -> AnyPublisher<Result<T, ErrorType>, Never> {
    URLSession
        .shared
        .dataTaskPublisher(for: url)
        .flatMap { (data, response) -> AnyPublisher<Result<T, ErrorType>, Never> in
            switch response.result {
            case .success(let data):
                if let data = try? JSONDecoder().decode(T.self, from: data){
                    return Just(data).eraseToAnyPublisher()
                }
            case .failure(let error):
                if let error = error as? URLError {
                    switch error.code {
                    case .notConnectedToInternet, .networkConnectionLost, .timedOut:
                        return Fail(ErrorType.noInternetConnection).eraseToAnyPublisher()
                    case .cannotDecodeRawData, .cannotDecodeContentData:
                        return Fail(ErrorType.empty).eraseToAnyPublisher()
                    default:
                        return Fail(ErrorType.general).eraseToAnyPublisher()
                    }
                }
            }
        }
        .eraseToAnyPublisher()
}

But I am getting

Cannot convert return expression of type 'AnyPublisher<AnyPublisher<Result<T, ErrorType>, Never>.Output, URLSession.DataTaskPublisher.Failure>' (aka 'AnyPublisher<AnyPublisher<Result<T, ErrorType>, Never>.Output, URLError>') to return type 'AnyPublisher<Result<T, ErrorType>, Never>' error.

CodePudding user response:

There are several major flows in your implementation.

Firstly, you shouldn't be using Result as the Output type of the Publisher and Never as its Failure type. You should be using T as the Output and ErrorType as Failure.

Second, you need tryMap and mapError, not flatMap.

Lastly, you are handling the result of dataTaskPublisher completely wrong. When dataTaskPublisher fails, it emits an error, so you need to handle that in mapError. When it succeeds, it emits its result as data, so you need to be decoding that, not response.

func fetchData<T: Decodable>(url: URL) -> AnyPublisher<T, ErrorType> {
    URLSession
        .shared
        .dataTaskPublisher(for: url)
        .tryMap { data, _ in
            return try JSONDecoder().decode(T.self, from: data)
        }
        .mapError { error -> ErrorType in
            switch error {
            case let urlError as URLError:
                switch urlError.code {
                case .notConnectedToInternet, .networkConnectionLost, .timedOut:
                    return .noInternetConnection
                case .cannotDecodeRawData, .cannotDecodeContentData:
                    return .empty
                default:
                    return .general
                }
            default:
                return .general
            }
        }
        .eraseToAnyPublisher()
}
  • Related