我是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
转换为具有Failure
的Publisher
。
在我的案例中缺失的一行:
Just(payload)
.setFailureType(to: APIError.self)
您只有几个编译时错误,Swift类型推理系统无法确定这些错误何时发生在臭名昭著的古怪flatMap
Combine运算符中。
- 首先,在创建
Response
对象时,使用了错误的参数顺序和URLResponse
类型。将其更正为:
return Response(
response: response.response as! HTTPURLResponse,
value: value
)
- 其次,您的
flatMap
实际上并没有返回AnyPublisher<Data, Error>
——您在其闭包中指定的返回类型。返回类型为AnyPublisher<Response<T>, APIError>
。所以,你可以改变这一点,但随后你会遇到另一个问题,那就是flatMap
的Error
类型必须与其上游相同,而上游目前不是APIError
,所以我建议将mapError
从flatMap
中移出。它看起来是这样的:
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>)
。我已经更新了我的问题以反映这个解决方案。