ReactiveSwift中的Action和BindingTarget

哈Ha!

我叫Igor,我是AGIMA移动部门的负责人。并不是每个人都从ReactiveSwift / Rxswift切换到Combine吗?那么今天我就谈谈使用等概念ReactiveSwift的经验ActionBindingTarget什么任务都可以在他们的帮助来解决。我立即注意到,对于RxSwift,相同的概念还存在于RxAction的形式中Binder在本文中,我们将考虑ReactiveSwift上的示例,最后,我将展示RxSwift上的一切看起来如何。

我希望您已经知道什么是反应式编程,并具有ReactiveSwift或RxSwift的经验。

假设我们有一个产品页面和一个“添加到收藏夹”按钮。当我们按下它时,加载器开始旋转而不是旋转,结果,按钮变为填充或不填充。最有可能的是,我们将在ViewController中使用MVVM体系结构获得类似的内容。

let favoriteButton = UIButton()
let favoriteLoader = UIActivityIndicatorView()
let viewModel: ProductViewModel

func viewDidLoad() {
  ...

  favoriteButton.reactive.image <~ viewModel.isFavorite.map(mapToImage)

  favoriteLoader.reactive.isAnimating <~ viewModel.isLoading
  //      
  favoriteButton.reactive.isHidden <~ viewModel.isLoading

  favoriteButton.reactive.controlEvents(.touchUpInside)
     .take(duringLifetimeOf: self)
     .observeValues { [viewModel] _ in
         viewModel.toggleFavorite()
     }
}

并在viewModel中:

lazy var isFavorite = Property(_isFavorite)
private let _isFavorite: MutableProperty<Bool>

lazy var isLoading = Property(_isLoading)
private let _isLoading: MutableProperty<Bool>

func toggleFavorite() {
  _isLoading.value = true
	service.toggleFavorite(product).startWithResult { [weak self] result in
    self._isLoading.value = false
    switch result {
      case .success(let isFav):
         self?.isFavorite.value = isFav
      case .failure(let error):
         // do somtething with error
    }
  }
}

, MutableProperty «» , . Action . «» . Action 2- : SignalProducer apply BindingTarget( ). , viewModel :

let isFavorite: Property<Bool>
let isLoading: Property<Bool>
private let toggleAction: Action<Void, Bool, Error>

init(product: Product, service: FavoritesService = FavoriteServiceImpl()) {
    toggleAction = Action<Void, Bool, Error> {
        service.toggleFavorite(productId: product.id)
            .map { $0.isFavorite }
     }

     isFavorite = Property(initial: product.isFavorite, then: toggleAction.values)
     isLoading = toggleAction.isExecuting
}

func toggleFavorite() {
  favoriteAction.apply().start()
}

? , . , Action

Action SignalProducer ( RxSwift: SignalProducer — , Signal — ). Action , execute , SignalProducer.

( !) .

final class Action<Input, Output, Error> {
	let values: Signal<Output, Never>
	let errors: Signal<Error, Never>
	let isExecuting: Property<Bool>
  let isEnabled: Property<Bool>
  
	var bindingTarget: BindingTarget<Input>
 
	func apply(_ input: Input) -> SignalProducer<Output, Error> {...}

  init(execute: @escaping (T, Input) -> SignalProducer<Output, Error>)
}

? values Action errors— . isExecuting , ( ). , values errors Never «», .  isEnabled- Action / , . , 10 . , «» Action , , , , :)

1:  apply SignalProducer   values , errors, isExecuting , Action

2: Action . Action , . , , Action ( RxSwift).

SignalProducer, favoriteAction.values , favoriteAction.errors

2- Action BindingTarget viewModel toggleFavorite :

let toggleFavorite: BindingTarget<Void> = favoriteAction.bindingTarget

viewModel.toggleFavorite <~ button.reactive.controlEvents(.touchUpInside)

. . BindingTarget.

E, , : SignalProducer, , - . , SignalProducer Signal Disposable dispose(). input , SignalProducer Action disposable .

BindingTarget? BindingTarget ,

, Lifetime(, ). , Observer MutableProperty BindingTarget.

. , BindingTarget— , «» :

isLoadingSignal
    .take(duringLifetimeOf: self)
    .observe { [weak self] isLoading in 
        isLoading ? self?.showLoadingView() : self?.hideLoadingView()
    }

:

self.reactive.isLoading <~ isLoadingSignal

— , .

isLoading ( ):

extension Reactive where Base: ViewController {
    var isLoading: BindingTarget<Bool> {
        makeBindingTarget { (vc, isLoading) in
            isLoading ? vc.showLoadingView() : vc.hideLoadingView()
        }
    }
}

, makeBindingTarget , .  KeyPath ( ):

var isLoading = false

...

reactive[\.isLoading] <~ isLoadingSignal

BindingTarget ReactiveCocoa , , , , 99% .

Action «» ViewModel .  BindingTarget , , , , :)

RxSwift

ViewController:

viewModel.isFavorite
    .map(mapToImage)
    .drive(favoriteButton.rx.image())
    .disposed(by: disposeBag)

viewModel.isLoading
    .drive(favoriteLoader.rx.isAnimating)
    .disposed(by: disposeBag)

viewModel.isLoading
   .drive(favoriteButton.rx.isHidden)
   .disposed(by: disposeBag)

favoriteButton.rx.tap
   .bind(to: viewModel.toggleFavorite)
   .disposed(by: disposeBag)

ViewModel

let isFavorite: Driver<Bool>
let isLoading: Driver<Bool>
let toggleFavorite: AnyObserver<Void>
private let toggleAction = Action<Void, Bool>
    
init(product: Product, service: FavoritesService = FavoriteServiceImpl()) {
    toggleAction = Action<Void, Bool> {
         service.toggleFavorite(productId: product.id)
            .map { $0.isFavorite }
    }
        
    isFavorite = toggleAction.elements.asDriver(onErrorJustReturn: false)
    isLoading = toggleAction.executing.asDriver(onErrorJustReturn: false)
    toggleFavorite = toggleAction.inputs
}

Binder

extension Reactive where Base: UIViewController {
    var isLoading: Binder<Bool> {
        Binder(self.base) { vc, value in
            value ? vc.showLoadingView() : vc.hideLoadingView()
        }
    }
}

:

Action

RxSwiftCommunity/Action




All Articles