본문 바로가기
iOS/ReactorKit

[ReactorKit] ReatorKit 살펴보기

by 0inn 2022. 11. 10.

 

새로운 프로젝트를 시작하면서 ReactorKit을 도입하여 개발중에 있는데 . .

이 시점에서 좀 더 꼼꼼히 정리해보고자 합니다 . . ! (최근에 기록의 중요성을 뼈저리게 느껴요 ㅜ)

 

ReactorKit 이란 ?

ReactorKit은 View에서 Action이 발생하면 Reactor가 이를 처리하여 State를 업데이트합니다.

View에서는 필요한 State를 binding하여 State가 업데이트됨에 따라 View를 업데이트하는 방식입니다.

 

 

View

View에는 어떠한 비즈니스 로직도 존재하지 않습니다.

Action을 Reactor에게 넘기고, Reactor의 State들을 구독하고 있는 형태로 이해하면 좋을 것 같습니다.

 

사용 예시를 코드로 살짝 보도록 하겠습니다.

class HomeViewController: NavigationBarViewController, View {
    
    typealias Reactor = HomeReactor
    
    //...
    
    func bind(reactor: HomeReactor) {
    	// Action (View -> Reactor)
       todayButton.rx.tap
            .map { .tapHomeViewTypeButton(.today) }
            .bind(to: reactor.action)
            .disposed(by: disposeBag)
         
        // State (Reactor -> View)
        reactor.state
            .map(\.homeViewType)
            .distinctUntilChanged()
            .withUnretained(self)
            .bind { (this, type)
            	// code
            }
            .disposed(by: disposeBag)

    }
}

todayButton이 클릭됨에 따라 뷰가 바뀌어야하는 상황인데요.

bind(reactor:)를 통해 바인딩을 수행합니다.

todayButton.rx.tap ~ 을 통해 View에서 Reactor로 버튼 클릭 이벤트를 방출합니다.

그리고 reactor.state ~ 을 통해 Reactor에서 바뀐 State인 homeViewType을 View에서 구독하고 있습니다.

 

 

Reactor

Reactor 프로토콜은 반드시 구현해야하는 Action, Mutation, State가 있습니다.

 

Action

Action은 유저 인터렉션을 나타냅니다.

Mutation

Action과 State 사이의 다리역할을 합니다.

State

View에서 사용할 정보들을 저장합니다.

 

mutate()

: Action을 받고, Observable<Mutation>을 방출합니다.

 

reduce()

: 이전 State와 Mutation을 받아 새로운 State를 생성합니다.

 

transform() 요 아이는 사용을 안해봤음 . .

func transform(action: Observable<Action>) -> Observable<Action>
func transform(mutation: Observable<Mutation>) -> Observable<Mutation>
func transform(state: Observable<State>) -> Observable<State>

이 함수들을 구현하여 다른 observable stream과 결합할 수 있습니다.

global states에서 많이 사용된다고 합니다. 이 부분에 관련해선 나중에 기회가 되면 다루겠습니다 . .

func transform(action: Observable<Action>) -> Observable<Action> {
  return action.debug("action") // Use RxSwift's debug() operator
}

이런 식으로 디버깅할 때도 사용할 수 있다고 하네요 . .

 

그렇다면 아까 View에 대한 Reactor 예시도 빼놓을 수 없겠죠.

import UIKit

import ReactorKit

class HomeReactor: Reactor {
    enum HomeViewType {
        case today
        case month
    }
    
    // 버튼을 클릭하는 유저 액션
    enum Action {
        case tapHomeViewTypeButton(HomeViewType)
    }
    
    // 액션에 대한 상태 변화
    enum Mutation {
        case showHomeView(HomeViewType)
    }
    
    // 현재 상태
    struct State {
        var homeViewType: HomeViewType = .month
    }
    
    var initialState: State
    
    init() {
        self.initialState = State()
    }
    
    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
        case .tapHomeViewTypeButton(let type): // 버튼 클릭 이벤트 받으면
            return .just(.showHomeView(type)) // Mutation으로 바꿔줌
        }
    }
    
    func reduce(state: State, mutation: Mutation) -> State {
        var newState = state
        
        switch mutation {
        case .showHomeView(let type):
            newState.homeViewType = type // 상태 반영
        }
        
        return newState
    }
}

버튼을 클릭하면 homeViewType을 .today으로 바꿔야하는 상황입니다.

 

유저가 버튼을 클릭하는 Action을 정의해주고, 해당 Action에 대해 어떤 뷰를 보여줄 것인지를 Mutation에 정의합니다.

State에서 homeViewType의 state를 먼저 .month으로 정의합니다.

 

mutate()를 통해 버튼 클릭 이벤트에 대해 어떤 뷰를 보여줄 것인지를 Observable로 방출합니다.

state(현재 상태)와 mutation을 받아 최종 상태를 반환합니다.

그렇다면, View에서 해당 homeViewType의 state를 구독하는 곳의 구현부가 실행되면서 원하는 작업을 할 수 있습니다 !

 

 

프로젝트를 진행하면 할수록 너무 편하고 . . 좋은 것 같습니다 . .

ReactorKit 최고 . .


참고

https://github.com/ReactorKit/ReactorKit