使用Combine发布API请求时出现问题



我是Combine游戏的新手,正在尝试了解如何概括HTTPPOST请求。

我创建了以下APIService类来扩展来自的各个资源服务

import Foundation
import Combine
class APIService {
let decoder: JSONDecoder
let session: URLSession

init(session: URLSession = URLSession.shared, decoder: JSONDecoder = JSONDecoder()) {
self.decoder = decoder
self.session = session
}
}
// MARK: JSON API
extension APIService {
struct Response<T> {
let response: HTTPURLResponse
let value: T
}

func post<T: Codable>(
payload: T,
url: URL
) -> AnyPublisher<Response<T>, APIError> {
return Just(payload)
.setFailureType(to: APIError.self) // <<< THIS WAS MISSING!
.encode(encoder: JSONEncoder())
.flatMap({ [weak self] payload -> AnyPublisher<Data, Error> in
guard let self = self else {
return Fail(error: .default("Failing to establish self.")).eraseToAnyPublisher()
}
var request = URLRequest(url: url)
request.httpMethod = Methods.post
request.setValue(
Mimetypes.json,
forHTTPHeaderField: Headers.contentType
)
request.httpBody = payload
return self.session
.dataTaskPublisher(
for: request
)
.tryMap { response -> Response<T> in
let value = try self.decoder.decode(T.self, from: response.data)
return Response(
value: value,
response: response.response
)
}
.mapError { error in
return APIError.default(error.localizedDescription)
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
})
.eraseToAnyPublisher()
}
}

但是,此类不会在post函数处编译,并出现以下错误。

Type of expression is ambiguous without more context

作为Swift的新手,尤其是Combine,不幸的是,我对如何继续没有想法。

非常感谢您的帮助

我自己想好了:解决方案

Failure类型添加到Just,因此输入和输出Failure类型到flatMap是相等的。换句话说:flatMap无法将Never失败的Just转换为具有FailurePublisher

在我的案例中缺失的一行:

Just(payload)
.setFailureType(to: APIError.self)

您只有几个编译时错误,Swift类型推理系统无法确定这些错误何时发生在臭名昭著的古怪flatMapCombine运算符中。

  1. 首先,在创建Response对象时,使用了错误的参数顺序和URLResponse类型。将其更正为:
return Response(
response: response.response as! HTTPURLResponse,
value: value
)
  1. 其次,您的flatMap实际上并没有返回AnyPublisher<Data, Error>——您在其闭包中指定的返回类型。返回类型为AnyPublisher<Response<T>, APIError>。所以,你可以改变这一点,但随后你会遇到另一个问题,那就是flatMapError类型必须与其上游相同,而上游目前不是APIError,所以我建议将mapErrorflatMap中移出。它看起来是这样的:
return Just(payload)
.encode(encoder: JSONEncoder())
.flatMap({ [weak self] payload -> AnyPublisher<Response<T>, Error> in
guard let self = self else {
return Fail(error: APIError.default("...")).eraseToAnyPublisher()
}
var request = URLRequest(url: url)
request.httpMethod = "Methods.post"
request.setValue(
Mimetypes.json,
forHTTPHeaderField: Headers.contentType
)
request.httpBody = payload
return self.session
.dataTaskPublisher(
for: request
)
.tryMap { response -> Response<T> in
let value = try self.decoder.decode(T.self, from: response.data)
return Response(
response: response.response as! HTTPURLResponse,
value: value
)
}
.eraseToAnyPublisher()

})
.mapError { error in
return APIError.default(error.localizedDescription)
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()

多亏了这本关于调试发布服务器的另一个问题的可靠指南,才明白了这一点。

Just需要用Failure来扩充,因为flatMap需要输入流和输出流的Failure相同。

我们可以使用setFailureType(to: <T>)。我已经更新了我的问题以反映这个解决方案。

最新更新