Controller 에서 Inputs 을 받아서 ViewModel 에서 이 Inputs 값을 바탕으로 처리를 하여 Controller 에 Outputs 해주는 방식입니다.
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
class ViewModel: ViewModelType {
struct Input {
let name: Observable<String>
let validate: Observable<Void>
}
struct Output {
let greeting: Driver<String>
}
func transform(input: Input) -> Output {
let greeting = input.validate
.withLatestFrom(input.name)
.map { name in
return "hello \(name)!"
}
.startWith("")
.asDriver(onErrorJustReturn: ":-(")
return Output(greeting: greeting)
}
}
class ViewController: UIViewController {
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var greetingLabel: UILabel!
@IBOutlet weak var validateButton: UIButton!
private let viewModel = ViewModel()
private var bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bind()
}
private func bind() {
let inputs = ViewModel.Input(
name: nameTextField.rx.text.orEmpty.asObservable(),
validate: validateButton.rx.tap.asObservable()
)
let outputs = viewModel.transform(input: inputs)
outputs.greeting
.drive(greetingLabel.rx.text)
.disposed(by: bag)
}
}
class ViewModel {
let input: Input
let output: Output
struct Input {
let name: AnyObserver<String>
let validate: AnyObserver<Void>
}
struct Output {
let greeting: Driver<String>
}
private let nameSubject = ReplaySubject<String>.create(bufferSize: 1)
private let validateSubject = PublishSubject<Void>()
init() {
let greeting = validateSubject
.withLatestFrom(nameSubject)
.map { name in
return "Hello \(name)!"
}
.asDriver(onErrorJustReturn: ":-(")
self.output = Output(greeting: greeting)
self.input = Input(name: nameSubject.asObserver(),
validate: validateSubject.asObserver())
}
}
class ViewController: UIViewController {
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var greetingLabel: UILabel!
@IBOutlet weak var validateButton: UIButton!
private let viewModel = ViewModel()
private var bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bind()
}
private func bind() {
nameTextField.rx
.text
.orEmpty
.bind(to: viewModel.input.name)
.disposed(by: bag)
validateButton.rx
.tap
.bind(to: viewModel.input.validate)
.disposed(by: bag)
viewModel.output.greeting
.drive(greetingLabel.rx.text)
.disposed(by: bag)
}
}
class ViewModel {
enum Input {
case TextInput(String)
}
enum Output {
case validate(String)
}
private let outPut: PassthroughSubject<Output, Never> = .init()
private var cancellables = Set<AnyCancellable>()
func transform(input: AnyPublisher<Input, Never>) -> AnyPublisher<Output, Never> {
input.sink { [weak self] event in
switch event {
case .TextInput(let text):
self?.outPut.send(.validate(text))
}
}
.store(in: &cancellables)
return outPut.eraseToAnyPublisher()
}
}
class ViewController: UIViewController {
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var greetingLabel: UILabel!
@IBOutlet weak var validateButton: UIButton!
private let viewModel = ViewModel()
private var cancellables = Set<AnyCancellable>()
private let input: PassthroughSubject<ViewModel.Input, Never> = .init()
override func viewDidLoad() {
super.viewDidLoad()
bind()
setupButton()
}
private func bind() {
let output = viewModel.transform(input: input.eraseToAnyPublisher())
output.receive(on: DispatchQueue.main)
.sink { [weak self] event in
switch event {
case .validate(let text):
self?.greetingLabel.text = "Hello \(text)"
}
}
.store(in: &cancellables)
}
private func setupButton() {
validateButton.addAction(UIAction(handler: { _ in
self.input.send(.TextInput(self.nameTextField.text ?? ""))
}), for: .touchUpInside)
}
}
https://medium.com/blablacar/rxswift-mvvm-66827b8b3f10
https://www.youtube.com/watch?v=KK6ryBmTKHg&t=1880s