
protocol InputOutputType {
associatedtype Input
associatedtype Output
var disposeBag: DisposeBag { get set }
func transform(input: Input) -> Output
}import RxSwift
import RxCocoa
final class ViewModel {
private let disposeBag: DisposeBag()
struct Input {
// 구독할 이벤트별로 프로퍼티를 지정해준다.
}
struct Output {
// 이벤트 처리 결과에 따라 만들어진 Observable을 보통 반환한다.
}
func transform(input: Input) -> Output {
..
return Output
}
}import RxSwift
import RxCocoa
final class SomeViewController: UIViewController {
private let textField = UITextField()
..
private func bindView() {
..
let input = SomeViewModel.Input(
// text 입력에 대한 이벤트를 ViewModel의 Input 인스턴스에 반영
textFieldChange: textField.rx.text
)
}
}final class SomeViewModel {
..
struct Input {
let textFieldChange: ControlProperty<String?>
}
struct Output {
let emailValidationText: PublishRelay<String>
}
func transform(for input: Input) -> Output {
..
let emailValidationText = PublishRelay<String>()
// 전달받은 textfield의 이벤트를 여기서 구독하여 특정 값 처리를 한다.
input.textFieldChange.orEmpty
.map { self.emailValidator(for: $0) && $0.count >= 3 }
.subsribe(self) { owner, valid in
emailValidationText.accept(valid ? "검증 성공" : "검증 실패")
}
.disposed(by: disposeBag)
..
return Output(
emailValidationText: emailValidationText
)
}
}// SomeViewController
private func bindView() {
..
let input = .. // 위에 작성한 코드 참고
let output = viewModel.transform(for: input)
// email 검증에 따른 결과 값(여기서는 PublishRelay<String>한 Observable)을
// emailValidationLabel 이라고 하는 Observer에 구독한다.
output.emailValidationText
.bind(to: emailValidationLabel.rx.text) // view에 바인딩
.disposed(by: disposedBag)
..
}// SomeViewController
private func bindView() {
..
let cellButtonTouch = PublishRelay<Int>()
let input = SomeViewModel.Input(
..
cellButtonIndex: cellButtonTouch
)
let output = .. // 위에 작성한 코드 참고
output.collectionViewDatalist
.bind(to: collectionView.rx.items(
cellIdentifier: Cell.id,
cellType: Cell.self
)) { row, item, cell in
// 이 클로저 안에서 cell instance에 대한 ui 작업 호출
cell.bindView(for: item)
cell.someButton.rx.tap
.bind(with: self) { _, _ in
cellButtonTouch.accept(row)
}
.disposed(by: cell.disposeBag) // ⭐️
}
.disposed(by: disposeBag)
..
}// SomeViewController
private func bindView() {
..
let cellSelected = PublishRelay<(Int, SomeData)>()
Observable.zip(
collectionView.rx.itemSelected,
collectionView.rx.modelSelected(SomeData.self)
)
.bind(with: self) { vc, cellTuple in
// itemSelected에서 확인되는 IndexPath 값과
// modelSelected에서 확인되는 cell의 Data 값을
// Tuple 형태로 반환해준다.
cellSelected.accept((cellTuple.0.row, cellTuple.1))
}
.disposed(by: disposeBag)
..
}collection.rx.itemSelected
.bind(with: Object, onNext: (Object, IndexPath) -> Void)collection.rx.modelSelected(SomeData.self)