본문 바로가기

iOS_RIBs

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

RIBs 와 ReactorKit 를 사용하며 단점이라면 단점인것은 Swift 파일의 갯수가 많아진다는 것이다.

Router, Interactor, Builder, Reactor 와 추가적으로 UIViewController와 UIVierController에 대한 xib 파일이 필요하다.

xib 파일을 제외한 Router, Interactor, Builder, Reactor 와 UIViewController를 정리해놓고자 한다!!

 

우선 제가 이해하고 있는 RIBs는 Uber에서 만든 앱 설계 아키텍처 패턴으로서,

RIBs는 Router, Interactor, Builder의 약자입니다.

하나의 RIBs는 Router, Interactor 및 Builder가 필수적으로 구성되고, 필요에 따라 Presenter와 View로 구성됩니다.

 

이해하기 쉽게 하자면

RIBs가 하나의 Node이고 RIBs Node의 트리 구조를 만들어 나가며(트리에 RIBs 노드를 attach, detach하면서), 앱을 설계해가는 프레임워크(?)라고 이해하면 이해가 쉽다.

물론, 내가 이해하고 싶은데로 이해하고 있는 수준이며ㅎㅎ.. 틀린 이해일 수 있으나 우선은 이렇게 이해하고 코드로 넘어가자..

 

1.  Builder

import RIBs
import SwinjectAutoregistration

protocol SampleDependency: Dependency {
  var setting: Setting { get } // protocol로 선언된 Setting 불러옴 (별도 정리예정)
}

final class SampleComponent: Component<SampleDependency> {
  fileprivate let setting: Setting
  fileprivate let apiService: ApiServiceType

  override init(dependency: SampleDependency) {
    self.setting = dependency.setting
    self.apiService = container ~> ApiServiceType.self
    super.init(dependency: dependency)
    // container 나 CocoaPods 를 이용한 SwinjectAutoregistration에 대한 샘플 코드 별도 정리 예정
  }
}

protocol SampleBuildable: Buildable {
  func build(withListener listener: SampleListener) -> SampleRouting
}

final class SampleBuilder: Builder<SampleDependency>, SampleBuildable {
  init(dependency: SampleDependency) {
    super.init(dependency: dependency)
  }
  
  func build(withListener listener: SampleListener) -> SampleRouting {
    let component = SampleComponent(dependency: dependency)
    let viewController = SampleViewController(
      setting: component.setting,
      apiService: component.apiService
    )
    let interactor = SampleInteractor(
      presenter: viewController,
      apiService: component.apiService,
      setting: component.setting
    )
    interactor.listener = listener
    return SampleRouter(interactor: interactor, viewController: viewController)
  }
}

Builder의 샘플코드를 정의하였다.

  • Builder : RIBs의 모든 구성요소를 생성하고 DI를 정의합니다. 즉, Router, Interactor, View, Component를 모두 생성합니다.

※ DI: Dependency Injection의 개념은 따로 정리할 예정이다. (Swinject 관련)

A. DI해줄 Dependency를 정의

→ 예시로서 추후 dependency injection 해줄 앱의 전체 세팅값을 저장하고 있는 Setting Protocol을 목록에 올려놓자.

dependency의 목록만 정의 한다고 이해하자.

 

B. Dependency를 가지고 Component를 구성(Component Build)

" container ~> ApiServiceType.self " → 이와 같은 문법에 대한 내용은 Swinject 관련 정리에서 다룰 예정이다.

우선 ApiServiceType: 서비스 api를 기술하는 ApiService Protocol 이라고 생각하자.

추후 Component를 통해서 Dependency를 Injection 해줄 것인데 Injection하기 위해서 Componentization 한다고 이해하자.

 

C. Buildable Protocol 을 정의

import Foundation

/// The base builder protocol that all builders should conform to.
public protocol Buildable: class {}

/// Utility that instantiates a RIB and sets up its internal wirings.
open class Builder<DependencyType>: Buildable {

    /// The dependency used for this builder to build the RIB.
    public let dependency: DependencyType

    /// Initializer.
    ///
    /// - parameter dependency: The dependency used for this builder to build the RIB.
    public init(dependency: DependencyType) {
        self.dependency = dependency
    }
}

Builder는 Generic 으로 dependencyType을 받고, Buildable Protocol을 준수합니다.

 

Buildable Protocol에 SampleListener라는 리스너를 가지는 build 함수의 interface를 정의합니다.

→ 이 예제의 경우 SampleBuildable

 

여기서 SampleListener 하나의 프로토콜로서 RIBs와는 관계없이, 해당 interface는 비즈니스 로직이 정의되어 있는 Interactor(이 예제의 경우 SampleInteractor)에 정의되어 있습니다.

 

아까 위에서 RIBs는 앱 설계 아키텍처 패턴이고 RIBs 노드들의 트리 구조를 만들어 나가면서 앱을 설계한다고 했는데,

현재 RIBs 노드에서 다른 RIBs 노드로의 routing 요청이 필요한 경우가 있습니다. (화면전환은 모두 다른 RIBs 노드로의 routing 요청이라고 보면 되겠죠?!)

 

routing 요청을 수행하는 방식에는 2가지가 있습니다.

ㄱ. 나를 트리에 attach 시킨 부모 RIBs 노드에 맡기는 방식 (부모 RIBs 노드에게 delegate)

ㄴ. 자기 자신이 자식 RIBs 노드를 attach 시키기 위해 router를 이용하는 방식

여기서 (ㄱ) 방식을 사용하기 위해선 부모 RIBs 노드가 SampleInteractor에 정의한 SampleListener protocol을 준수하여야 합니다.

 

즉, SampleBuildable Protocol에 정의된 build 함수의 interface 속 SampleListener 인자는 (ㄱ) 방식의 routing요청을 구현하기 위함입니다.

 

D. Builder 클래스 구현

a. Dependency 목록들을 Componentization한 SampleComponent 인스턴스 생성

b. SampleComponent 인스턴스를 통해 ViewController, Interactor에 Dependency Injection(DI)를 수행 및 build

c. 마지막으로 router를 리턴(이 예제의 경우 SampleRouter)

 

벌써 3번째 같은 말을 반복합니다ㅎㅎ RIBs는 앱 설계 아키텍처 패턴이고 RIBs 노드들의 트리 구조를 만들어 나가면서 앱을 설계!!

그렇다면, 현재 이 예제코드로 쓰고 있는 RIBs 노드 역시!! 부모 RIBs 노드가 자신의 자식으로 attach 한거겠죠?!

→ "C. Buildable Protocol 을 정의 (Buildable Protocol interface)"에 소개된 routing 방식들 중 (ㄴ) 방식을 통해서 즉, SampleBuildable이라는 Buildable 프로토콜을 준수하는 부모 RIBs 노드의 라우터에서 SampleBuilder.build() 함수를 호출함으로써~

return 된 Router(이 예제의 경우 SampleRouter)를 통해 SampleRouter, SampleInteractor, SampleBuilder로 이루어진 이 예제의 RIBs 노드를 트리구조에 attach 했다고 보시면 RIBs 노드의 트리를 구성한다는 개념이 이해가 가실겁니다! 👍

 

다음번엔 " RIBs + ReactorKit을 활용한 RIBs 노드 中 Router " 포스팅을 이어나갈 계획입니다! 😄