在具有通用功能的 RestManager 中使用 flatMap 不起作用

Using flatMap in RestManager with generic function not working

我是 Combine 的新手,所以我想创建 class RestManager 用于与通用网络 获取数据 函数。函数是 returning AnyPublisher, Never> 其中 ErrorType 的枚举。 noInternetConnection、.empty 和 .general 例。

我尝试将 URLSessiondataTaskPublisherflatMap

一起使用
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()
}

但我得到了

无法将类型 'AnyPublisher<AnyPublisher<Result<T, ErrorType>, Never>.Output, URLSession.DataTaskPublisher.Failure>'(又名 'AnyPublisher<AnyPublisher<Result<T, ErrorType>, Never>.Output, URLError>')的 return 表达式转换为 return 类型 'AnyPublisher<Result<T, ErrorType>, Never>'错误。

您的实施中有几个主要流程。

首先,您不应该使用 Result 作为 PublisherOutput 类型和 Never 作为其 Failure 类型。您应该使用 T 作为 OutputErrorType 作为 Failure.

其次,你需要tryMapmapError,而不是flatMap

最后,您处理 dataTaskPublisher 的结果完全错误。当 dataTaskPublisher 失败时,它会发出错误,因此您需要在 mapError 中处理它。当它成功时,它会以 data 的形式发出结果,因此您需要对其进行解码,而不是 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()
}