본문 바로가기

iOS_RIBs

RIBs + ReactorKit을 활용한 RIBs 노드 中 ViewController

이전 포스팅인 " RIBs + ReactorKit을 활용한 RIBs 노드 中 Builder ",  " RIBs + ReactorKit을 활용한 RIBs 노드 中 Router ",

" RIBs + ReactorKit을 활용한 RIBs 노드 中 Interactor & Reactor "를 먼저 살펴보고 와야 이해가 됩니다.

 

5. ViewController

import RIBs
import RxSwift
import UIKit
import ReactorKit

protocol SamplePresentableListener: AnyObject {
  func requestToInteractor()

  // reactor
  var action: ActionSubject<SampleViewAction> { get }
  var currentState: SampleViewState { get }
  var state: Observable<SampleViewState> { get }
}

typealias SampleViewReactor = (
  state: Observable<SampleViewState>,
  action: ActionSubject<SampleViewAction>
)

class SampleViewController: BaseViewController, SamplePresentable, SampleViewControllable {
  weak var listener: SamplePresentableListener?
  
  private var apiService: ApiServiceType
  private let setting: Setting
  
  init(setting: Setting, apiService: ApiServiceType) {
    self.setting = setting
    self.apiService = apiService
    super.init()
  }
  
  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()

    guard let action = listener?.action else { return }
    guard let state = listener?.state else { return }
    let reactor = (state, action)
    bindAndRelay(reactor: reactor)
  }
  
  private func bindAndRelay(reactor: SampleViewReactor) {
    bindActions(to: reactor)
    relayState(from: reactor)
  }
  
  private func bindActions(to reactor: SampleViewReactor) {
    sampleBtn.rx.tap
      .asDriver()
      .drive(onNext: { [weak self] _ in
        self?.listener?.action.onNext(.requestBusinessLogic) // 이벤트 발생 시 비즈니스 로직 요청!
      }).disposed(by: disposeBag)
  }
  
  private func relayState(from reactor: SampleViewReactor) {
    reactor.state.map { $0.result }
      .distinctUntilChanged()
      .subscribe(onNext: { [weak self] in
        self?.listener?.requestToInteractor() // 비즈니스 로직 결과값 가지고 후속 프로세스 처리
      }).disposed(by: disposeBag)
  }
  
  @IBOutlet weak var sampleBtn: UIButton!
  
}

ViewController의 샘플 코드를 정리 하였다.

코드 분석 바로 들어가보겠습니다.

 

A. PresentableListener protocol 정의

이전 포스팅인 " RIBs + ReactorKit을 활용한 RIBs 노드 中 Interactor & Reactor " 에서 B. Presentable protocol 정의 부분을 살펴보시면 Interactor가 View의 비즈니스 로직 수행 요청을 듣고 있기 위해서 Presentable protocol을 정의하고, 해당 프로토콜은 ViewController가 준수하며, 이를 통해 Interactor는 PresentableListener를 Get해와서 View의 비즈니스 로직을 수행할 수 있게 되는 구조에 대해 설명드렸습니다.

 

이 구조에 대해 다시 이해를 도와드리자면

- Interactor는 Presentable 프로토콜을 Generic으로 받는다. (예제의 경우 SamplePresentable)

final class SampleInteractor: PresentableInteractor<SamplePresentable>, SampleInteractable {

}

SamplePresentable은 다음과 같았다.

protocol SamplePresentable: BasePresentable {
  var listener: SamplePresentableListener? { get set }
}

이후 Interactor를 init하는 과정에 다음과 같은 코드가 있었다.

presenter.listener = self

SamplePresentableListener는 ViewController에 정의되고, Interactor는 이걸 Set하고 Get 해가는 구조

 

이런 구조를 통해 ViewController와 Interactor가 동작하게 되고, SamplePresentableListener에 정의한 interface함수와

선언한 reactor의 action, state 변수들이 이해가 되실 겁니다. (이해가 되셨길 바랍니다.)

 

B. Reactor typealias 선언

사용자의 이벤트를 reactor에게 전달하고, view의 state가 바뀌었다는 것을 전달받을 reactor typealias 를 선언합니다.

추후 Interactor에서 Get해온 action 과 state를 Set 컬렉션 타입으로 만들어 사용자의 이벤트를 bind하고 state의 변화를 relay해오게 되는데 이를 처리하기 위함

 

C. ViewController class 구현

override func viewDidLoad() {
    super.viewDidLoad()

    guard let action = listener?.action else { return }
    guard let state = listener?.state else { return }
    let reactor = (state, action)
    bindAndRelay(reactor: reactor)
}
  
private func bindAndRelay(reactor: SampleViewReactor) {
    bindActions(to: reactor)
    relayState(from: reactor)
}

listener는 SamplePresentableListener이고, SamplePresentableListener에서 Get해온 Inteactor의 action 과 state를 가지고 reactor를 Set 컬렉션 타입으로 선언하여 typealias로 선언되었던 SampleViewReactor를 활용하여 bindAndRelay함수 실행

 

이후 사용자의 이벤트를 reactor에 bind하고 reactor의 state 변화를 감지하여, state가 변화하면 relay를 통해 받아오는 과정을 구현한 함수를 실행합니다.

private func bindActions(to reactor: SampleViewReactor) {
    sampleBtn.rx.tap
      .asDriver()
      .drive(onNext: { [weak self] _ in
        self?.listener?.action.onNext(.requestBusinessLogic) // 이벤트 발생 시 비즈니스 로직 요청!
      }).disposed(by: disposeBag)
}

private func relayState(from reactor: SampleViewReactor) {
    reactor.state.map { $0.result }
      .distinctUntilChanged()
      .subscribe(onNext: { [weak self] in
        self?.listener?.requestToInteractor() // 비즈니스 로직 결과값 가지고 후속 프로세스 처리
      }).disposed(by: disposeBag)
}

 

이로써 4개의 포스팅에 걸친 RIBs + ReactorKit를 활용하여 RIBs 노드를 만드는 과정에 대한 포스팅을 마칩니다.