我有一个ViewModelType来绑定UIViewController&ViewModel。
import Foundation
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
HomeViewModel符合ViewModelType并定义所需的输入和输出,然后它根据输入返回输出。
为了简单起见,我删除了存储库,并总是为syncData任务返回失败。
import Foundation
import RxSwift
import RxCocoa
class HomeViewModel: ViewModelType {
struct Input {
let syncData: Driver<Void>
}
struct Output {
let message: Driver<String>
}
func transform(input: Input) -> Output {
let fetching = input.syncData.flatMapLatest { _ -> Driver<String> in
return Observable<String>.from(optional: "Choose below options to proceed") // This message will be returned by server.
.delay(.seconds(1), scheduler: MainScheduler.instance)
.asDriverOnErrorJustComplete()
}
return Output(message: fetching)
}
}
我有一个带字符串的提醒活页夹。
UIAlertController
在单击重试按钮时有一个重试按钮。我想从HomeViewModel
的Input
调用syncData
。我该怎么做?
import UIKit
import RxSwift
import RxCocoa
class HomeViewController: UIViewController {
private let disposeBag = DisposeBag()
var viewModel = HomeViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let viewDidAppear = rx.sentMessage(#selector(UIViewController.viewDidAppear(_:)))
.mapToVoid()
.asDriverOnErrorJustComplete()
// How to merge viewWillAppear & alert for callback of retry button?
let input = HomeViewModel.Input(syncData: viewDidAppear)
let output = viewModel.transform(input: input)
output.message.drive(alert)
.disposed(by: disposeBag)
}
var alert: Binder<String> {
return Binder(self) { (vc, message) in
let alert = UIAlertController(title: "Sync failed!",
message: message,
preferredStyle: .alert)
let okay = UIAlertAction(title: "Retry", style: .default, handler: { _ in
// how to call syncData of Input?
})
let dismiss = UIAlertAction(title: "Dismiss",
style: UIAlertAction.Style.cancel,
handler: nil)
alert.addAction(okay)
alert.addAction(dismiss)
vc.present(alert, animated: true, completion: nil)
}
}
}
当您将非RxCode转换为RxCode时(如使UIAlertAction成为反应式(,以及当您必须创建一个循环时(如将视图模型的输出反馈回其自己的输入(,有两种情况应该使用Subject
如果你发现自己经常这样做,那么你可能需要考虑制作多视图模型。
您还会注意到,我将警报的创建作为自己的、独立的协调器功能。这样它就可以在多个地方使用。如果您愿意,您可以对传递到flatMapFirst
中的闭包执行相同的操作。
class HomeViewModel {
struct Input {
let syncData: Observable<Void>
let retry: Observable<Void>
}
struct Output {
let message: Observable<String>
}
func transform(input: Input) -> Output {
let fetching = Observable.merge(input.syncData, input.retry)
.flatMapLatest { _ -> Observable<String> in
return Observable<String>.from(optional: "Choose below options to proceed") // This message will be returned by server.
.delay(.seconds(1), scheduler: MainScheduler.instance)
}
return Output(message: fetching)
}
}
class HomeViewController: UIViewController {
private let disposeBag = DisposeBag()
var viewModel = HomeViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let viewDidAppear = rx.sentMessage(#selector(UIViewController.viewDidAppear(_:)))
.mapToVoid()
let retry = PublishSubject<Void>()
let input = HomeViewModel.Input(syncData: viewDidAppear, retry: retry.asObservable())
let output = viewModel.transform(input: input)
output.message
.flatMapFirst { [weak self] (message) -> Observable<Void> in
let (alert, trigger) = createAlert(message: message)
self?.present(alert, animated: true)
return trigger
}
.subscribe(retry)
.disposed(by: disposeBag)
}
}
func createAlert(message: String) -> (UIViewController, Observable<Void>) {
let trigger = PublishSubject<Void>()
let alert = UIAlertController(title: "Sync failed!",
message: message,
preferredStyle: .alert)
let okay = UIAlertAction(title: "Retry", style: .default, handler: { _ in
trigger.onNext(())
trigger.onCompleted()
})
let dismiss = UIAlertAction(title: "Dismiss",
style: UIAlertAction.Style.cancel,
handler: nil)
alert.addAction(okay)
alert.addAction(dismiss)
return (alert, trigger)
}
首先,您应该使用一些协调器来调用推送/呈现控制器。并制作代表Alert的函数。
例如:
class Router {
private let rootViewController: UIViewController
let retryAction = PublishSubject<Void>()
func showGalleryAlert() {
let alert = UIAlertController(title: "Your Title", message: "Your Message", preferredStyle: .alert)
let settings = UIAlertAction(title: "Name Action", style: .default) { _ in
// send here your action to PublishSubject
self.retryAction.send()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel)
alert.addAction(settings)
rootViewController.present(alert, animated: true)
}}
然后你需要将这个路由器插入到你的ViewModel中,并监听这个PublishSubject。
或者你可以使用Single/Maybe函数,这里有一个如何使用它的小例子:
public func openList() -> Maybe<Void> {
return .create { observer -> Disposable in
let alert = UIAlertController(title: "Your Title", message: "YourMessage", preferredStyle: .alert)
let settings = UIAlertAction(title: "Name Action", style: .default) { _ in
// send here your action
observer.send()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel)
alert.addAction(settings)
rootViewController.present(alert, animated: true)
return Disposables.create {
DispatchQueue.main.async {
vc.dismiss(animated: true, completion: nil)
}
}
}
}
并通过ViewModel进行处理。
附言:您应该在ViewModel中的输入上使用Subject,而不是Drivers。驱动程序必须仅用于输出等视图