مرحبا هبر!
اسمي إيغور ، أنا رئيس قسم الجوّال في AGIMA. لم يتحول الجميع من ReactiveSwift / Rxswift إلى Combine حتى الآن؟ ثم اليوم سأتحدث عن تجربة استخدام مفاهيم مثل ReactiveSwift Actionو BindingTargetوما المهام يمكن حلها مع مساعدتهم. ألاحظ على الفور أنه بالنسبة إلى 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()
}
}وفي المنظر النموذج:
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()
}
}
}: