하나의 모델을 의미하며, 크게 View와 Reactor가 있습니다.
View에서 사용자가 일으키는 Action을 Reactor에 전달하고,
Reactor에서 State의 변화를 View에 전달하는 방식으로 단방향 반응형 아케틱쳐의 형태를 띄고 있습니다.
저는 많이들 처음 구현 해보는 Counter 앱을 통해 ReactorKit을 사용해보겠습니다.
'-'와 '+'를 통해 값을 올리고 내릴 수 있는 기능 구현
import Foundation
import ReactorKit
class CounterReactor: Reactor {
let initialState = State()
//View의 Action 정의
enum Action {
case increase
case decrease
}
//Action을 받을 Mutation 정의
enum Mutation {
case increaseValue
case decreaseValue
}
struct State {
var value : Int = 0
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .increase:
return Observable.just(Mutation.increaseValue)
case .decrease:
return Observable.just(Mutation.decreaseValue)
}
}
func reduce(state: State, mutation: Mutation) -> State {
var newState = state
switch mutation {
case .increaseValue:
newState.value += 1
case .decreaseValue:
newState.value -= 1
}
return newState
}
}
import UIKit
import ReactorKit
import SnapKit
import RxSwift
import RxCocoa
class ViewController: UIViewController,View {
//reactorkit에서 View 레이어는 항상 View Protocol을 채택해야함, Disposebag과 bind(reactor:)를 정의해야 함
var disposeBag = DisposeBag()
init(reactor: CounterReactor) {
super.init(nibName: nil, bundle: nil)
self.reactor = reactor
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private lazy var increaseButton : UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "plus"), for: .normal)
return button
}()
private lazy var decreaseButton : UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "minus"), for: .normal)
return button
}()
private lazy var valueLabel : UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 18.0, weight: .semibold)
return label
}()
private lazy var stackView : UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.alignment = .fill
stackView.distribution = .equalSpacing
[decreaseButton,valueLabel,increaseButton]
.forEach{stackView.addArrangedSubview($0)}
return stackView
}()
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
view.addSubview(stackView)
stackView.snp.makeConstraints{
$0.leading.trailing.equalToSuperview().inset(24.0)
$0.centerY.centerX.equalToSuperview()
}
}
func bind(reactor: CounterReactor) {
//decrease increase 버튼 tap시 action 발생
decreaseButton.rx.tap
.map{Reactor.Action.decrease}
.bind(to:reactor.action)
.disposed(by: disposeBag)
increaseButton.rx.tap
.map{Reactor.Action.increase}
.bind(to:reactor.action)
.disposed(by: disposeBag)
reactor.state
.map{$0.value}
.distinctUntilChanged()
.map{"\($0)"}
.bind(to: valueLabel.rx.text)
.disposed(by: disposeBag)
}
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
let reactor = CounterReactor()
let rootViewController = ViewController(reactor: reactor)
window?.backgroundColor = .systemBackground
window?.rootViewController = UINavigationController(rootViewController: rootViewController)
window?.makeKeyAndVisible()
}
위와 같이 Reactor를 정의해주고 ViewController에 저장해주는 작업을 해주어야 실행됩니다!
오늘 이렇게 ReactorKit에 대해 간단하게 알아보았는데, 아직 간단한 수준이라 더 공부할 필요가 있어보입니다.
개인적으로는 MVVM보다 눈에 더 잘 들어오는 느낌이 들고, 깔끔한 느낌이라 마음에 들었습니다 !
다음에 또 ReacotrKit 예제를 들고 오겠습니다 ! 감사합니다!