๊ฐ๋จํ ๊ตฌ์กฐ
์ผ๋จ RIB์ ๊ตฌ์กฐ๋ ์์ ๊ฐ๋ค.
- ๋ ธ๋์ ์ง์ฌ๊ฐํ: RIB ๋ด๋ถ์์ ์ฌ์ฉํ๋ Protocol
- ๋นจ๊ฐ์ ์ง์ฌ๊ฐํ: RIB ์ธ๋ถ์ ํต์ ํ๊ธฐ ์ํด ์ฌ์ฉํ๋ Protocol
- ๋ฅ๊ทผ ์ง์ฌ๊ฐํ: Class
๋ฅ๊ทผ ์ง์ฌ๊ฐํ (Router, Builder, Interactor, View) ๊ทผ์ฒ์ ์๋ Protocol์ ํด๋น ํ์ผ ๋ด์ ์์ฑ๋๊ฒ ๋์ด ๊ทผ์ฒ์ ๋ฐฐ์นํ์๋ค. ์ด์ ์์ ํ๋ํ๋์ ๋ํด ๊ฐ๋จํ๊ฒ ์ค๋ช ํด๋ณด๊ฒ ๋ค.
Builder
ํด๋น RIB์ด ๊ฐ์ง๋ ์์กด์ฑ์ ๋ฐ๊ณ , build()
method๋ฅผ ํตํด ๋์ ์ผ๋ก ์ ์ฉ๋์ด์ผ ํ๋ ์์กด์ฑ์ ๋ฐ์ํ๊ณ ๋ด๋ถ Component๋ฅผ ์์ฑํ๊ณ ์์กด์ฑ์ ์ฃผ์
ํ๋ ์ญํ ์ ๋ด๋นํ๋ค. ์ฐ๋ฆฌ๊ฐ ์ฑ์ ๋ง๋ค ๋, View tree๋ฅผ ๋ฐ๋ผ์ ํด๋น ๋ก์ง๋ค์ด ์์นํ์๋ค. ์ต์ข
์ ์ผ๋ก ๋ณด์ด๋ ํ๋ฉด์ ์ด์ ์ ๋ฐฉ๋ฌธํ๋ View๋ค์ ๋ํด ์์กด์ฑ์ ๊ฐ๊ณ ์ ๋ง๋ค์ด์ง๊ฒ ๋๋ค. ์ด๋ฐ ์์กด์ฑ์ ๋ด๋นํ๋ ๊ฒ์ด Builder์ด๋ค.
๊ทธ๋ฐ๋ฐ ์์กด์ฑ์๋ ์ ์ ์์กด์ฑ, ๋์ ์์กด์ฑ ๋๊ฐ์ง ๋ถ๋ฅ๊ฐ ์กด์ฌํ๋ค. ์ ์ ์์กด์ฑ์ ๋จ์ํ ํน์ component๋ฅผ ์์ฑํ๋๋ฐ ์์ด ์ธ๋ถ ์์๋ ๊ณ์ฐ๋ ๊ฒฐ๊ณผ๊ฐ ํ์์์ด๋ ๋ง๋ค์ด์ง ์ ์๋ ๊ฒฝ์ฐ๋ค. ์๋ฅผ ๋ค์ด, Aํ๋ฉด์์ Bํ๋ฉด์ผ๋ก ์ด๋ํ๋๋ฐ ์์ด A๊ฐ ๊ฐ์ง๊ณ ์๋ ์ ๋ณด ๊ทธ๋๋ก๋ฅผ ๋ฃ์ด์ฃผ๋ ๊ฒ์ผ๋ก ๋๋๋ค๋ฉด ์ด ๊ฒฝ์ฐ ์ ์ ์์กด์ฑ์ ๊ฐ์ง๋ค ํ ์ ์์ ๊ฒ์ด๋ค. ๊ทธ๋ผ ๋์ ์์กด์ฑ์ ๋ฌด์์ผ๊น? ํน์ action์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ค๋ฅธ component๊ฐ ์์ฑ๋์ด์ผ ํ๋ ๊ฒฝ์ฐ๋ฅผ ๋งํ๋ค. ์๋ฅผ ๋ค์ด, A ํ๋ฉด์์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ๊ณ ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก Bํ๋ฉด์ด ๋ง๋ค์ด์ ธ์ผ ํ๋ค๋ฉด, ์ด๋ Aํ๋ฉด์ submit์ด ๋ ์ดํ์ ํด๋น ๊ฐ์ ๊ฐ์ด ๋ฃ์ด์ฃผ์ด B๋ฅผ ๋ง๋ค์ด์ผ ํ ๊ฒ์ด๋ค. ์ด๋ฐ ๊ฒฝ์ฐ๋ฅผ ๋์ ์์กด์ฑ์ ๊ฐ์ง๋ค๋ผ ํ ์ ์์ ๊ฒ์ด๋ค.
Builder์์๋ ์ ์ ์์กด์ฑ์ ๊ฒฝ์ฐ component
๋ฅผ ํตํด ๋ฃ์ด์ฃผ๊ณ , ๋์ ์์กด์ฑ์ ๊ฒฝ์ฐ build()
ํจ์์ ์ธ์๋ก ๋ฃ์ด์ค์ผ๋ก์ ์ด๋ฅผ ๊ฐ๋ฅ์ผํ๋ค. build()
ํจ์์์ ์ค์ ๋ก RIB์ ๊ตฌ์ฑ์์๊ฐ ๋ชจ๋ ์์ฑ๋๊ณ ์์กด์ฑ์ด ์ฃผ์
๋๊ธฐ ๋๋ฌธ์, ์ด ์์ ์ ๋ฃ์ด์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
Builder instance ์์ฒด๋ ์ถํ ์ค๋ช
ํ Router๊ฐ ๊ฐ์ง๊ณ ์๋ค. ์ ๊น ์ค๋ช
ํ๋ฉด, Router๋ ์ ์ ์์กด์ฑ์ด ์ฃผ์
๋ Builder instance๋ฅผ ์์ฑ์ ๊ฐ์ง๊ณ ์๋ค๊ฐ, Interactor๊ฐ ํ์ RIB์ ์์ฑํ๋ผ๋ ๋ช
๋ น์ ๋ฐ๋ ์์ ์ ํ์ Builder์ build()
ํจ์๋ฅผ ํธ์ถํ๋ค.
Component
Builder์ ๋์ ์์กด์ฑ์ ๋ฃ์ด์ฃผ๋ ์์์ด๋ค. ํ์ RIB์ Builder๋ self.dependency
๋ฅผ ํตํด ์์ RIB์ dependency์ ์ ๊ทผํ ์ ์๋ค. (์ ์ ์์กด์ฑ) ํ์ง๋ง ํด๋น Type์ ํ์ RIB์์ ์ฌ์ฉํ๋ ChildDependency
๋ผ๋ Protocol๋ก interface๋ฅผ ๋ถ๋ฆฌํด์ ๊ด๋ฆฌํ๊ณ ์๊ธฐ ๋๋ฌธ์, ์์ dependency ๊ตฌํ์ฒด์ ๋ง ์ ๊ทผํด์ ์ฌ์ฉ์ ๋ถ๊ฐ๋ฅํ๋ค. ๋ง์ฝ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ChildDependency
์ ๋ด๊ฐ ์ฌ์ฉํ๊ณ ์ถ์ ๋ณ์๋ฅผ ์ ์ํด์ผ ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
// ์์ RIB
protocol RootBuildable: Buildable {
func build() -> LaunchRouting
}
final class RootBuilder: Builder<RootDependency>, RootBuildable {
override init(dependency: RootDependency) {
super.init(dependency: dependency)
}
func build() -> LaunchRouting {
let viewController = RootViewController()
let component = RootComponent(dependency: dependency,
rootViewController: viewController)
let interactor = RootInteractor(presenter: viewController)
// ํ์ RIB์ Builder๋ฅผ ์์ฑํ ๋, Builder๊ฐ ๊ฐ์ง๊ณ ์๋ dependency๋ฅผ ๋ฃ์ด์ค๋ค.
let loggedInBuilder = LoggedInBuilder(dependency: component)
// ์์ RIB์ Router์์ ํ์ RIB์ Builder๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
let router = RootRouter(interactor: interactor,
viewController: viewController,
loggedInBuilder: loggedInBuilder)
return (router, interactor)
}
}
// ํ์ RIB: ์ดํด๋ฅผ ๋๊ธฐ ์ํ Protocol๊ณผ ์์ฑ์๋ง ๊ฐ์ ธ์ด, ์์ธํ ์ฌํญ์ Tutorial ์งํ
protocol LoggedInDependency: Dependency {
var loggedInViewController: LoggedInViewControllable { get }
}
final class LoggedInBuilder: Builder<LoggedInDependency>, LoggedInBuildable {
// ์์ฑ๋ ๋ ์์ RIB์ dependency๋ฅผ ๋ฐ์์ผ๋, interface๊ฐ LoggedInDependency์ด๊ธฐ ๋๋ฌธ์ ์ ๊ทผ์ด ์ ํ๋๋ค.
override init(dependency: LoggedInDependency) {
super.init(dependency: dependency)
}
}
์ด๋ ๊ฒ ์์ RIB์ dependency๋ฅผ Builder๊ฐ ๊ฐ์ง๊ณ ์์์๋ ๋ถ๊ตฌํ๊ณ , ๋ง์ฝ ๋์ ์์กด์ฑ์ด ํ์ํ๋ค๋ฉด ์ด ๋ฐฉ๋ฒ์ผ๋ก๋ ๋ถ์กฑํ๋ค. ์ด๋ฅผ ์ํด ๋ง๋ค์ด์ง ๊ฒ์ด Component์ด๋ค. ํ์ RIB์ด ๋ง๋ค์ด์ง ๋, ์์ RIB์ Router์์ instance๋ก ๊ฐ์ง๊ณ ์๋ ํ์ RIB์ build()
ํจ์๋ฅผ ํธ์ถํ๊ฒ ๋๋๋ฐ, ์ด ๋, ๋์ ์ผ๋ก ๋ฐ์ํ ๊ฐ(์์กด์ฑ)์ build(player1, player2)
์ ๊ฐ์ ํ์์ผ๋ก ๋ฃ์ด์ค๋ค. ๊ทธ๋ฆฌ๊ณ ํ์ RIB์์๋ ํด๋น ๊ฐ์ ๋ฐ์ Component๋ฅผ ์์ฑํ์ฌ ์์กด์ฑ์ ํด๊ฒฐํ RIB ๋ด๋ถ ์์๋ฅผ ๋ง๋ ๋ค.
// ์ดํด๋ฅผ ์ํด ๊ตฌ์ฒด ์ฌํญ์ ์ ์ธํ๊ณ ํต์ฌ๋ง ๊ฐ์ ธ์๋ค.
// ์์ RIB์ Interactor์ ์์ฒญ์ ๋ฐ๋ผ Router์์ ํ์ RIB์ buildํ๋ค.
final class RootRouter: LaunchRouter<RootInteractable, RootViewControllable>, RootRouting {
func routeToLoggedIn(withPlayer1Name player1Name: String, player2Name: String) {
// build์ ๋์ ์ผ๋ก ๋ฐ์๋์ด์ผ ํ๋ ๊ฐ์ด ํ์ํ ๊ฒฝ์ฐ ๋ฃ์ด ๋ณด๋ธ๋ค.
let loggedIn = loggedInBuilder.build(withListener: interactor,
player1Name: player1Name,
player2Name: player2Name)
attachChild(loggedIn.router)
}
}
// MARK: - Builder
protocol LoggedInBuildable: Buildable {
func build(withListener listener: LoggedInListener, player1Name: String, player2Name: String) -> (router: LoggedInRouting, actionableItem: LoggedInActionableItem)
}
final class LoggedInBuilder: Builder<LoggedInDependency>, LoggedInBuildable {
func build(withListener listener: LoggedInListener, player1Name: String, player2Name: String) -> LoggedInRouting {
// build ํจ์ ์์์ Component๋ฅผ ์์ฑํ์ฌ ์๋ก์ด ์์กด์ฑ ์์๋ฅผ ๋ง๋ ๋ค.
let component = LoggedInComponent(dependency: dependency,
player1Name: player1Name,
player2Name: player2Name)
let interactor = LoggedInInteractor(games: component.games)
interactor.listener = listener
let offGameBuilder = OffGameBuilder(dependency: component)
let router = LoggedInRouter(interactor: interactor,
viewController: component.loggedInViewController,
offGameBuilder: offGameBuilder)
return (router, interactor)
}
}
์ค์ Component๋ ์ด๋ ๊ฒ ์๊ฒผ๋ค.
final class LoggedInComponent: Component<LoggedInDependency> {
fileprivate var loggedInViewController: LoggedInViewControllable {
return dependency.loggedInViewController
}
fileprivate var games: [Game] {
return shared {
return [RandomWinAdapter(dependency: self), TicTacToeAdapter(dependency: self)]
}
}
internal let player1Name: String
internal let player2Name: String
init(dependency: LoggedInDependency, player1Name: String, player2Name: String) {
self.player1Name = player1Name
self.player2Name = player2Name
super.init(dependency: dependency)
}
}
์ฌ๊ธฐ์ ์ฃผ๋ชฉํ ์ ์, component์ ๋ณ์๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ด๋ค. ์ค์ ๋ก ์ฌ์ฉํ๋ ๋ณ์๋ง ์ ๊ณ , ์ ๊ทผ ์ ์ด์๋ฅผ ํตํด ๋ช ์์ ์ผ๋ก ๋ณ์ ์ฌ์ฉ์ ๊ด๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
Interactor
Interactor๋ RIB์ Business logic์ด ๋ด๊ธฐ๋ ๊ณณ์ด๋ค. ์ธ๊ฐ์ Protocol์ ๊ธฐ๋ณธ์ผ๋ก ๊ฐ๋๋ค.
- Listener์ ๊ฒฝ์ฐ ์์ RIB๊ณผ ํต์ ํ๊ธฐ ์ํ Interface์ด๋ค. ํด๋น Listener๋ ์์ RIB์ Interactor๊ฐ ์ฑํํ๊ณ ์๋ค.
- Routing์ ๊ฒฝ์ฐ Router์ ์์ฒญํ๊ธฐ ์ํ ๊ฒ๋ค์ด ๋์ด๋์ด ์๋ค.
- Presentable์ ๊ฒฝ์ฐ View์ ์์ฒญํ๊ธฐ ์ํ ๊ฒ๋ค์ด ๋์ด๋์ด ์๋ค.
View
View๋ View์ View Controller ๋ชจ๋๋ฅผ ๊ฐ๋ฆฌํจ๋ค. ๋ค๋ฅธ ์ํคํ
์ณ ํจํด์์ ์ฒ๋ผ View๋ ๋ฉ์ฒญํ๋ค. ๋จ์ํ ๊ทธ๋ฆฌ๋ ์ฉ๋๋ก๋ง ์ฌ์ฉํ๋ค. View์์ ๋ณ๊ฒฝ๋ ์ฌํญ์ ์๋ฆฌ๊ธฐ ์ํด์ PresentableListner
๋ฅผ ์ฑํํ ๊ณณ์ผ๋ก ๋ฉ์์ง๋ฅผ ๋ณด๋ธ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก๋ Interactor๊ฐ ๊ทธ ์ญํ ์ ํ๋ค.
Router
Router๋ Interactor์ ์์ฒญ์ ๋ฐ์ RIB์ Attach, Detachํ๋ ์ญํ ์ ๋ด๋นํ๋ค. Interactable
์ Interactor๊ฐ ์ฑํํ๋ Protocol๋ก, ํ์ RIB์ Interactor์ Listener, Router๊ฐ Interactor์ ์์ฒญํด์ผ ํ๋ ์์๋ค์ ๋ชจ๋ ์ฑํํ๊ณ ์๋ค.
ViewControllable
์ View ์์์ transition์ ๊ด๋ จ๋ ๊ฒ๋ค์ ์ ์ํ Protocol์ด๋ค. RIB Attach, Detach์ ๋ฐ์ํ๋ transition์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ํด๋น Protocol์ View๊ฐ ์ค์ํ๊ณ ์๋ค.
protocol LoggedInInteractable: Interactable, OffGameListener, GameListener {
var router: LoggedInRouting? { get set }
var listener: LoggedInListener? { get set }
}
protocol LoggedInViewControllable: ViewControllable {
func replaceModal(viewController: ViewControllable?)
}
final class LoggedInRouter: Router<LoggedInInteractable>, LoggedInRouting {
// MARK: - LoggedInRouting
func cleanupViews() {
if currentChild != nil {
viewController.replaceModal(viewController: nil)
}
}
func routeToOffGame(with games: [Game]) {
detachCurrentChild()
attachOffGame(with: games)
}
func routeToGame(with gameBuilder: GameBuildable) {
detachCurrentChild()
let game = gameBuilder.build(withListener: interactor)
self.currentChild = game
attachChild(game)
viewController.replaceModal(viewController: game.viewControllable)
}
...
}