ReactiveSwift:如何订阅 SignalProducer?
ReactiveSwift: How to subscribe to SignalProducer?
我正在尝试学习 ReactiveSwift 和 ReactiveCocoa。我可以很好地使用 Signal
和 Property
,但我在使用 SignalProducer
.
时遇到了问题
据我了解,SignalProducer
非常适合网络请求之类的事情。我设置了我的 API 层来创建和 return 一个信号提供者,调用者可以启动它。
class ApiLayer {
func prepareRequest(withInfo info: RequestInfo) -> SignalProducer<ModelType, ErrorType> {
return SignalProducer<ModelType, ErrorType> { (observer, lifetime) in
// Send API Request ...
// In Completion Handler:
let result = parseJson(json)
observer.send(value: result)
observer.sendCompleted()
}
}
}
但是我应该如何听取结果呢?
我试过类似的方法,但出现错误,所以我一定是做错了/想错了。
apiLayer.prepareRequest(withInfo: info)
.startWithValues { (resultModel) in
// Do Stuff with result ...
}
这是我得到的错误:
Ambiguous reference to member 'startWithValues'
- Found this candidate (ReactiveSwift.SignalProducer< Value, NoError >)
- Found this candidate (ReactiveSwift.SignalProducer< Never, NoError >)
编辑
我试着更明确地帮助编译器识别正确的方法,就像这样。但是错误仍然存在。
apiLayer.prepareRequest(withInfo: info)
.startWithValues { (resultModel: ModelType) in // Tried adding type. Error remained.
// Do Stuff with result ...
}
编辑 2
在 GitHub 支持页面获得帮助并考虑此处提供的答案后,这就是我的最终结果。
与我之前尝试的一个主要区别是调用者不会手动启动 returned SignalProducer
。相反,通过创建它 inside/in 对另一个信号的响应,它在链中隐式启动。
我之前(错误地)认为有必要提取并明确订阅 Signal
SignalProducer
"produced".
相反,我现在将 SignalProducer
s 简单地视为响应刺激而启动的延迟工作。我可以手动订阅 SignalProvider
或者我可以让另一个 Signal
提供刺激。 (后者在我下面的更新示例中使用。它看起来相当干净,比手动启动它更像 FRP,这是我从命令式思维中继承下来的。)
enum ErrorType: Error {
case network
case parse
}
class ApiLayer {
func prepareRequest(withInfo info: RequestInfo) -> SignalProducer<ModelType, ErrorType> {
let producer = SignalProducer<ResultType, NoError> { (observer, lifetime) in
sendRequest(withInfo: info) { result in
observer.send(value: result)
observer.sendCompleted()
}
}
return producer
.attemptMap { result throws -> ResultType in
let networkError: Bool = checkResult(result)
if (networkError) {
throw ErrorType.network
}
}
.retry(upTo: 2)
.attemptMap { result throws -> ModelType in
// Convert result
guard let model: ModelType = convertResult(result) else {
throw ErrorType.parse
}
return model
}
// Swift infers AnyError as some kind of error wrapper.
// I don't fully understand this part yet, but to match the method's type signature, I needed to map it.
.mapError { [=13=].error as! ErrorType}
}
}
// In other class/method
// let apiLayer = ApiLayer(with: ...)
// let infoSignal: Signal<RequestInfo, NoError> = ...
infoSignal
.flatMap(.latest) { (info) in
apiLayer.prepareRequest(withInfo: info)
}
.flatMapError { error -> SignalProducer<ModelType, NoError> in
// Handle error
// As suggested by the ReactiveSwift documentation,
// return empty SignalProducer to map/remove the error type
return SignalProducer<ModelType, NoError>.empty
}
.observeValues { model in
// Do stuff with result ...
}
ReactiveSwift
的理念是让用户忽略错误并不容易。因此 startWithValues
仅在生产者的错误类型为 NoError
时可用,这可确保永远不会发送任何错误。如果您的生产者 可以 发送错误,您需要使用像 startWithResult
这样的函数来处理它:
apiLayer.prepareRequest(withInfo: info).startWithResult { result in
switch result {
case let .success(model):
// Do stuff with model
case let .failure(error):
// Handle error
}
}
忽略错误不是一个好主意,但在某些情况下,可以使用这样的扩展将它们视为 nil 值:
public extension SignalProducer {
func skipErrors() -> SignalProducer<Value?, NoError> {
return self
.flatMap(.latest, { SignalProducer<Value?, NoError>(value: [=10=]) })
.flatMapError { _ in SignalProducer<Value?, NoError>(value: nil) }
}
}
我正在尝试学习 ReactiveSwift 和 ReactiveCocoa。我可以很好地使用 Signal
和 Property
,但我在使用 SignalProducer
.
据我了解,SignalProducer
非常适合网络请求之类的事情。我设置了我的 API 层来创建和 return 一个信号提供者,调用者可以启动它。
class ApiLayer {
func prepareRequest(withInfo info: RequestInfo) -> SignalProducer<ModelType, ErrorType> {
return SignalProducer<ModelType, ErrorType> { (observer, lifetime) in
// Send API Request ...
// In Completion Handler:
let result = parseJson(json)
observer.send(value: result)
observer.sendCompleted()
}
}
}
但是我应该如何听取结果呢?
我试过类似的方法,但出现错误,所以我一定是做错了/想错了。
apiLayer.prepareRequest(withInfo: info)
.startWithValues { (resultModel) in
// Do Stuff with result ...
}
这是我得到的错误:
Ambiguous reference to member 'startWithValues'
- Found this candidate (ReactiveSwift.SignalProducer< Value, NoError >)
- Found this candidate (ReactiveSwift.SignalProducer< Never, NoError >)
编辑
我试着更明确地帮助编译器识别正确的方法,就像这样。但是错误仍然存在。
apiLayer.prepareRequest(withInfo: info)
.startWithValues { (resultModel: ModelType) in // Tried adding type. Error remained.
// Do Stuff with result ...
}
编辑 2
在 GitHub 支持页面获得帮助并考虑此处提供的答案后,这就是我的最终结果。
与我之前尝试的一个主要区别是调用者不会手动启动 returned SignalProducer
。相反,通过创建它 inside/in 对另一个信号的响应,它在链中隐式启动。
我之前(错误地)认为有必要提取并明确订阅 Signal
SignalProducer
"produced".
相反,我现在将 SignalProducer
s 简单地视为响应刺激而启动的延迟工作。我可以手动订阅 SignalProvider
或者我可以让另一个 Signal
提供刺激。 (后者在我下面的更新示例中使用。它看起来相当干净,比手动启动它更像 FRP,这是我从命令式思维中继承下来的。)
enum ErrorType: Error {
case network
case parse
}
class ApiLayer {
func prepareRequest(withInfo info: RequestInfo) -> SignalProducer<ModelType, ErrorType> {
let producer = SignalProducer<ResultType, NoError> { (observer, lifetime) in
sendRequest(withInfo: info) { result in
observer.send(value: result)
observer.sendCompleted()
}
}
return producer
.attemptMap { result throws -> ResultType in
let networkError: Bool = checkResult(result)
if (networkError) {
throw ErrorType.network
}
}
.retry(upTo: 2)
.attemptMap { result throws -> ModelType in
// Convert result
guard let model: ModelType = convertResult(result) else {
throw ErrorType.parse
}
return model
}
// Swift infers AnyError as some kind of error wrapper.
// I don't fully understand this part yet, but to match the method's type signature, I needed to map it.
.mapError { [=13=].error as! ErrorType}
}
}
// In other class/method
// let apiLayer = ApiLayer(with: ...)
// let infoSignal: Signal<RequestInfo, NoError> = ...
infoSignal
.flatMap(.latest) { (info) in
apiLayer.prepareRequest(withInfo: info)
}
.flatMapError { error -> SignalProducer<ModelType, NoError> in
// Handle error
// As suggested by the ReactiveSwift documentation,
// return empty SignalProducer to map/remove the error type
return SignalProducer<ModelType, NoError>.empty
}
.observeValues { model in
// Do stuff with result ...
}
ReactiveSwift
的理念是让用户忽略错误并不容易。因此 startWithValues
仅在生产者的错误类型为 NoError
时可用,这可确保永远不会发送任何错误。如果您的生产者 可以 发送错误,您需要使用像 startWithResult
这样的函数来处理它:
apiLayer.prepareRequest(withInfo: info).startWithResult { result in
switch result {
case let .success(model):
// Do stuff with model
case let .failure(error):
// Handle error
}
}
忽略错误不是一个好主意,但在某些情况下,可以使用这样的扩展将它们视为 nil 值:
public extension SignalProducer {
func skipErrors() -> SignalProducer<Value?, NoError> {
return self
.flatMap(.latest, { SignalProducer<Value?, NoError>(value: [=10=]) })
.flatMapError { _ in SignalProducer<Value?, NoError>(value: nil) }
}
}