您如何在BehaviorRelay中编辑当前值的属性?

问题描述 投票:1回答:1

我对RxSwift和RxCocoa还是陌生的,最近我大量使用了Variable,因为通过其值将突变推入Variable是多么方便。现在已经弃用了,我试图了解如何最好地使用BehaviorRelay。有一种Rx-y方式可以做我想做的事,但是我很难下定决心。

[我想要的是将基于结构的模型的实例放在ViewModel后面,观察它的变化并绑定UI元素,这样我就可以通过BehaviorRelay对该模型进行修改。

模型很简单:

struct Pizza {
    var name: String
    var price: Float
}

视图模型也是:

final class PizzaViewModel {
    let pizzaRelay =  BehaviorRelay<Pizza>(value: Pizza(name: "Sausage", price: 5.00))

    init(pizza: Pizza) {
        pizzaRelay.accept(pizza)

        // I feel like I'm missing something Rx-like here... 
    }
}

然后您可能会在某个地方像这样将UITextField绑定到BehaviorRelay:

viewModel
    .pizzaRelay
    .asObservable()
    .map { $0.name }
    .bind(to: nameTextField.rx.text)
    .disposed(by: disposeBag)

问题就变成了:如果您需要将文本字段中的值推回到BehaviorRelay中,那应该如何工作?

nameTextField
    .rx
    .controlEvent([.editingChanged])
    .asObservable()
    .subscribe(onNext: { [weak self] in
        guard let self = self else { return }
        if let text = self.nameTextField.text {
            self.viewModel.pizzaRelay.value.name = text  // does not compile because value is a let
        }
    }).disposed(by: disposeBag)

我可能在这里没有使用正确的类型,或者我没有考虑正确的输入/输出流的Rx时尚术语,但是我很好奇其他人如何解决这个问题?

我考虑过的其他事项:

  • 只需使用Pizza中的当前值在.subscribe中重新构造一个新的BehaviorRelay,更改名称,然后.accept将其返回到继电器中。但是,那感觉并不完全正确。
  • 我想在BehaviorRelay上为每个属性创建单个Pizza,然后为每个属性.accept输入值,然后在所有这些中继上使用combineLatest并返回Observable<Pizza>。但这也感觉很笨拙。

在理想的世界中应该如何工作?我在想这个吗?救命!我的头很痛。

swift reactive-programming rx-swift behaviorrelay
1个回答
0
投票

在理想的世界中,您不会使用中继甚至主题来编写此类代码。与其从结构开始,不如从流程开始。数据应如何在您的系统中移动?

作为示例,这是一个具有视图模型的视图控制器,可以将华氏温度转换为摄氏温度并返回:

struct TempInOut {
    let fahrenheit: Observable<String>
    let celsius: Observable<String>
}

func tempViewModel(input: TempInOut) -> TempInOut {
    let celsius = input.fahrenheit
        .compactMap { Double($0) }
        .map { ($0 - 32) * 5.0/9.0 }
        .map { "\($0)" }

    let fahrenheit = input.celsius
        .compactMap { Double($0) }
        .map { $0 * 5.0/9.0 + 32 }
        .map { "\($0)" }

    return TempInOut(fahrenheit: fahrenheit, celsius: celsius)
}

主要要了解的是数据如何从input.fahrenheit流到output.celsius,以及如何从input.celsius流到output.fahrenheit。

这是对您的程序的另一种思考方式……我最近听说“时间设计”的概念,我认为这是一个很好的术语。

这里是将使用上述视图模型的视图控制器。

class ViewController: UIViewController {

    @IBOutlet weak var fahrenheitField: UITextField!
    @IBOutlet weak var celsiusField: UITextField!

    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        let input = TempInOut(
            fahrenheit: fahrenheitField.rx.text.orEmpty.asObservable(),
            celsius: celsiusField.rx.text.orEmpty.asObservable()
        )

        let output = tempViewModel(input: input)

        disposeBag.insert(
            output.fahrenheit.bind(to: fahrenheitField.rx.text),
            output.celsius.bind(to: celsiusField.rx.text)
        )
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.