【问题标题】:ReactiveSwift: How to subscribe to SignalProducer?ReactiveSwift:如何订阅 SignalProducer?
【发布时间】:2017-12-13 06:42:09
【问题描述】:

我正在尝试学习 ReactiveSwift 和 ReactiveCocoa。我可以很好地使用SignalProperty,但我在使用SignalProducer 时遇到了问题。

据我了解,SignalProducer 非常适合网络请求之类的事情。我设置了我的 API 层来创建并返回一个信号提供者,调用者可以启动它。

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 ...
}

这是我得到的错误:

对成员“startWithValues”的模糊引用

  1. 找到了这个候选(ReactiveSwift.SignalProducer)
  2. 找到了这个候选者 (ReactiveSwift.SignalProducer)

编辑

我尝试更明确地帮助编译器识别正确的方法,就像这样。但错误仍然存​​在。

apiLayer.prepareRequest(withInfo: info)
    .startWithValues { (resultModel: ModelType) in // Tried adding type. Error remained.
        // Do Stuff with result ...
}

编辑 2

在 GitHub 支持页面获得帮助并考虑此处提供的答案后,这就是我的最终结果。

与我之前尝试的一个关键区别是调用者不会手动启动返回的SignalProducer。相反,通过在内部/响应另一个信号创建它,它在链内部隐式启动。

我之前(错误地)假设有必要提取并显式订阅 Signal,这是 SignalProducer “产生”的。

相反,我现在认为SignalProducers 只是为了响应刺激而启动的延迟工作。我可以手动订阅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 { $0.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 ...
    }

【问题讨论】:

    标签: swift reactive-swift reactive-cocoa-5


    【解决方案1】:

    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
        }
    }
    

    【讨论】:

    • 我也在 GitHub 支持页面here 上问过这个问题:andersio 正确指出 startWithResult 由于错误类型不可用。现在我在源代码中看到了 extension ... where 子句。但是感谢您指出明确处理和转换错误的理念。在返回并处理错误案例之前,我试图处理成功案例,所以我不知道为什么它是这样设置的。
    • 另外,我实际上并不了解如何使用信号提供者。感谢您和 andersio 的 cmets,我开始理解思考 Signals、SignalProviders 和 FRP 的正确方法。
    【解决方案2】:

    忽略错误不是一个好主意,但在某些情况下,可以通过这样的扩展将它们视为 nil 值:

    public extension SignalProducer {
        func skipErrors() -> SignalProducer<Value?, NoError> {
            return self
                .flatMap(.latest, { SignalProducer<Value?, NoError>(value: $0) })
                .flatMapError { _ in SignalProducer<Value?, NoError>(value: nil) }
        }
    }
    

    【讨论】:

    • 这不是一个坏主意。这种类型的扩展可能很有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 2021-05-06
    • 2012-03-14
    • 2015-08-24
    • 2020-02-22
    • 2021-06-08
    • 2019-12-05
    相关资源
    最近更新 更多