본문 바로가기
iOS/ReactorKit

[ReactorKit] ReactorKit의 transform() 사용하기

by 0inn 2022. 12. 6.

ReactorKit에서 View에서 action을 전달하고,

action에 따른 처리를 mutate()를 통해 Mutation으로 방출하여

reduce()에서 State 값을 변경시켰었죠.

 

View에서는 state값을 바인딩하여 값 변경 시 UI를 업데이트 시켜줍니다.

 

 

그렇다면 . . transform은 언제 쓰는데 . .?

 

Github를 보면 transform은 각각의 stream을 transform()한다 . .라고 나와있는데요.

Global States에서 많이 사용된다고 합니다.

처음에 ReactorKit을 접하고 읽었을 때는 뭔소린가 싶었지만 실제로 사용하고 나니까 이해가 수월하더라고요 !

 

먼저 Global States란 ?

 

예를 들어, currentUser가 바뀔 때마다 다른 View의 화면 또한 바뀌어하는 상황이 있다고 해볼게요.

그럼 이 currentUser는 Global State로 해당 state가 변할 때마다 Observable<Mutation>이 방출되어야겠죠.

 

그렇다면 transform()의 역할은 ?

 

ReactorKit에서는 Action→ Mutation → State 의 흐름으로 Global State가 없는데요.

이 때, transform(mutation:)을 통해 Global State를 Mutation으로 방출할 수 있습니다.

 

실제로 구현해보자 !

 

제가 구현해야하는 상황에 대해 설명해볼게요.

정보입력 화면 -> 생년월일 화면 전환하여 생일을 선택한 후, 완료 버튼을 누르면 해당 생년월일 화면이 dismiss됩니다.

그러면 정보입력 화면의 생년월일에 해당 생일로 업데이트 되어야 합니다.

 

자 그렇다면, 여기서 Global State는 뭘까요 ?

생일입니다 ! (2022년 12월 6일)

생일의 변경 사항에 따라 정보 입력 화면의 UI가 업데이트 되어야 하기 때문입니다.

 

코드를 살펴볼게요 !

 

InfoService

import RxSwift

enum InfoType {
    case birth
}

enum InfoEvent {
    case updateInfo(type: InfoType, content: String)
}

protocol InfoServiceProtocol {
    var event: PublishSubject<InfoEvent> { get }
    
    func updateBirth(to birth: String) -> Observable<String>
}

class InfoService: InfoServiceProtocol {
    let event = PublishSubject<InfoEvent>()
    
    func updateBirth(to birth: String) -> Observable<String> {
        event.onNext(.updateInfo(type: .birth, content: birth))
        return .just(birth)
    }
}

 

Global State가 여러 개여서 InfoService를 생성해서 구현했습니다 !

updateBirth()를 통해 Observable을 방출하고 있습니다.

 

BirthReactor

var initialState: State
var service: InfoServiceProtocol

init(service: InfoServiceProtocol) {
    self.initialState = State()
    self.service = service
}

func mutate(action: Action) -> Observable<Mutation> {
    switch action {
    case .tapDoneButton(let birth):
        return service.updateBirth(to: birth).map { _ in .dismiss }
    }
}

 

birth를 넘겨줘야하는 BirthReactor 먼저 볼게요.

여기서 완료 버튼과 함께 아까 생성한 InfoService에서 updateBirth()를 호출하여 Observable을 방출하고 있습니다.

 

InfoReactor

var service: InfoServiceProtocol

init(service: InfoServiceProtocol) {
    self.initialState = State()
    self.service = service
}

func transform(mutation: Observable<Mutation>) -> Observable<Mutation> {
    let event = service.event.flatMap { event -> Observable<Mutation> in
        switch event {
        case .updateInfo(type: let type, content: let birth):
            if type == .birth {
                return .just(.updateBirth(birth))
            } else {
                return .never()
            }
        }
    }
    
    return Observable.merge(mutation, event)
}

 

그렇다면 InfoReactor에서는 transform()을 통해 해당 Global State를 Mutation으로 변형하여 UI를 업데이트 해줘야겠죠 !

birth 변경사항을 받아 .updateBirth()를 방출하여 UI 업데이트를 할 수 있습니다.

 

가장 효율적인 방법인지는 모르겠지만 . . 이렇게 transform()을 사용해서 화면 간의 데이터 전달을 구현했습니다.

추후에 더 나은 방법이나 오류를 찾게 된다면 바로 업데이트하도록 하겠습니다 !

'iOS > ReactorKit' 카테고리의 다른 글

[ReactorKit] RxDataSources로 CollectionView 구현하기  (0) 2022.11.17
[ReactorKit] ReatorKit 살펴보기  (0) 2022.11.10