Android, iOS 앱을 만들면서 많은 디자인 패턴들이 존재한다. 그중에 가장 많이 사용하는것이 MVVM 패턴이다.
여기서 MVVM 패턴이란?
MVVM은 Model - View - ViewModel의 약자로 소프트웨어 아키텍처 패턴입니다.
간단하게.. 화면단을 보여주는 뷰 와 비즈니스 로직을 담당하는 뷰 모델 을 분리하여 프로젝트가 간결해지는 특징을 가지고 있습니다.
ReactorKit 같은 경우 해당 MVVM 을 좀더 규칙적 이게 사용할수 있게 도와주는 라이브러리 입니다.
실제 많은 기업에서 현재 적용중인 디자인 패턴 입니다.
1. View는 Action(사용자 입력 등)을 Reactor에게 전달한다
2. Reactor는 전달받은 Action에 따라 비즈니스 로직을 수행한다.
3. 그 후 Reactor는 상태를 변경하여 View에게 전달한다.
좀~더 자세히 보면
1. Reactor - mutate() 함수
- View 에서 던져준 Action 스트림을 Mutation 스트림으로 변환
- 해당 프로세스에서 비즈니스 로직 처리후 Mutation 을 reduce() 함수로 던져줍니다.
2. Reactor - reduce() 함수
- Mutation을 받은후 다음상태를 반환합니다.
- 반환 한값을 View 에서 구독을 받은 다음 UI 변경을 처리합니다.
해당 예제는 ReactorKit 상에 있는 예제이며 더욱 심화된 내용은 추가로 포스팅 하도록 하겠습니다.
1. View
1-1. plusButton 클릭 했을경우 Reactor.Action(plus) 액션을 전달 합니다.
1-2. minusButton 클릭 했을경우 Reactor.Action(minus) 액션을 전달 합니다.
plusButton.rx.tap
.map{ ViewReactor.Action.plus }
.bind(to: reactor.action)
.disposed(by: disposeBag)
minusButton.rx.tap
.map{ ViewReactor.Action.minus }
.bind(to: reactor.action)
.disposed(by: disposeBag)
2. Action
2-1. 뷰에서 액션을 요청 받은 plus, minus에 대해 Mutation 에 전달 합니다.
enum Action {
case plus
case minus
}
enum Mutation {
case setPlus
case setMinus
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .plus:
return Observable.just(Mutation.setPlus)
case .minus:
return Observable.just(Mutation.setMinus)
}
}
3. Action
3-1. mutate 에서 던져준 setPlus, setMinus 에 대해 처리 한후 값을 그대로 반환한다.
struct State {
var value : Int = 0
}
func reduce(state: State, mutation: Mutation) -> State {
var state = state
switch mutation {
case .setPlus:
state.value+=1
case .setMinus:
state.value-=1
}
return state
}
4. View
4-1. state 에서 던져준 각자의 값들을 구독을 받아 UI 변경 처리를 진행 합니다.
reactor.state.map{ $0.value }
.subscribe(onNext : {
value in
self.valueLabel.text = "\(value)"
}).disposed(by: disposeBag)
전체소스 (View)
import UIKit
import RxSwift
import RxCocoa
import RxGesture
import ReactorKit
class ViewController: UIViewController {
let reactor = ViewReactor()
let disposeBag = DisposeBag()
@IBOutlet weak var plusButton: UIButton!
@IBOutlet weak var minusButton: UIButton!
@IBOutlet weak var valueLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
bind(reactor: reactor)
}
func bind(reactor : ViewReactor) {
plusButton.rx.tap
.map{ ViewReactor.Action.plus }
.bind(to: reactor.action)
.disposed(by: disposeBag)
minusButton.rx.tap
.map{ ViewReactor.Action.minus }
.bind(to: reactor.action)
.disposed(by: disposeBag)
reactor.state.map{ $0.value }
.subscribe(onNext : {
value in
self.valueLabel.text = "\(value)"
}).disposed(by: disposeBag)
}
}
전체소스 (Reactor)
import Foundation
import RxSwift
import RxCocoa
import RxGesture
import ReactorKit
class ViewReactor: Reactor {
let initialState = State()
enum Action {
case plus
case minus
}
struct State {
var value : Int = 0
}
enum Mutation {
case setPlus
case setMinus
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .plus:
return Observable.just(Mutation.setPlus)
case .minus:
return Observable.just(Mutation.setMinus)
}
}
func reduce(state: State, mutation: Mutation) -> State {
var state = state
switch mutation {
case .setPlus:
state.value+=1
case .setMinus:
state.value-=1
}
return state
}
}
UI, 비즈니스 로직을 완전히 분리 함으로써 추후 유지보수 할경우에 문제가 생기더라도 찾기가 쉽습니다.
해당 라이브러리는 iOS 뿐만 아니라 Android 에서도 사용 가능합니다.
두가지 플랫폼에서 사용하면 서로의 언어를 몰라도 로직을 읽는데 도움이 될꺼라고 생각합니다.
참고 : https://github.com/ReactorKit/ReactorKit
https://medium.com/styleshare/reactorkit-시작하기-c7b52fbb131a
'IOS' 카테고리의 다른 글
Swift - SceneDelegate (0) | 2021.07.10 |
---|---|
Swift - ReactorKit2 (0) | 2021.07.06 |
Swift - Photos + CollectionView + Drag and Drops (0) | 2021.05.30 |
Swift - Photos + CollectionView + Gesture Multiple Select (0) | 2021.03.07 |
프로젝트에 SwiftLint를 달아보자 (0) | 2021.03.01 |