-
[iOS] RIBs 로 Routing 처리 #1👻 iOS 2021. 1. 15. 23:51
2020/12/29 - [👻 iOS] - [iOS] Child RIBs를 추가해보자 에서 이어집니다.
어... 지난 글을 쓰고 해가 바뀌었네요 ㅎ
보름이나 지난 것은 제가 그간 좀 바빴기 때문에.. (횡설수설)
다시 RIBs 공부에 매진해 봅니다.
지난번엔 Child RIBs만 추가하고 끝났는데 그 Child는 LoggedOut 이란 이름이었어요.
- 이유는 없고 RIBs 샘플에 따르면 제일 먼저 추가하게 되는 뷰 -
공부하는 거니까 그냥 여기부턴 각자 노선을 가는 것으로 하고 uber의 sample은 덮어 버립니다 ㅋㅋ
(제가 하고싶은대로 할 거예요..)
의미 없이 LoggedOut이 추가되었지만 나중에 다시 의미 있게 해 주고
여기에서 버튼을 눌러 LoggedOut이 닫히면
새로운 Child로 전환하는 것을 구현해 보겠습니다.
식상하게도 그 이름은 Todo로 할 거예요.
우선 Todo의 RIBs template을 만들어주고요
Todo와 sibling 관계는 LoggedOut이고 parent는 Root가 됩니다.
그래서 Root에서 Todo의 builder를 연결해 주는 것이 시작이라고 봐요.
아래와 같이 todoBuildable을 만들어주고 RootRouter에 넣겠다고 선언합니다.
그럼 당연히 오류가 날 거예요. POP(=Protocol-Oriented Programming)를 믿고 가봅니다.
final class RootBuilder: Builder<RootDependency>, RootBuildable { override init(dependency: RootDependency) { super.init(dependency: dependency) } func build() -> LaunchRouting { let component = RootComponent(dependency: dependency) let viewController = RootViewController() let interactor = RootInteractor(presenter: viewController) let loggedOutBuildable = LoggedOutBuilder(dependency: component) let todoBuildable = TodoBuilder(dependency: component) return RootRouter(interactor: interactor, viewController: viewController, loggedOutBuildable: loggedOutBuildable, todoBuildable: todoBuildable) } }
오류 내용 확인해보니 RootComponent에 호환성 문제네요.
TodoDependency 프로토콜을 상속받아 줍니다. 그러면 위 오류는 해결.
final class RootComponent: Component<RootDependency>, LoggedOutDependency, TodoDependency { // TODO: Declare 'fileprivate' dependencies that are only used by this RIB. }
RootRouter의 초기화 값에 todoBuildable은 지금 없으니 구현해주러 갑니다.
init에 todoBuildable을 받아주고 private으로 해당 값을 할당해주는 것 까지 할 거예요.
// 필요한 부분만 삽입했어요. final class RootRouter: LaunchRouter<RootInteractable, RootViewControllable>, RootRouting { // TODO: Constructor inject child builder protocols to allow building children. init(interactor: RootInteractable, viewController: RootViewControllable, loggedOutBuildable: LoggedOutBuilder, todoBuildable: TodoBuilder) { self.loggedOutBuildable = loggedOutBuildable self.todoBuildable = todoBuildable super.init(interactor: interactor, viewController: viewController) interactor.router = self } .... // MARK: - private private var loggedOutBuildable: LoggedOutBuilder private var todoBuildable: TodoBuilder .... }
그리고 이제 RootRouter에서 child RIB을 실제로 바꿔치는 구현을 할 시간입니다.
현재 RootRouter에는 private fun attachLoggedOut() 함수가 있고,
고정적으로 LoggedOut만 생성해서 붙여주고 있어요.
그렇지만 이제는 다른 child가 생길 수 있으니
조금 체계적으로 바꿔보겠습니다.
임의의 child가 attach 된다면 해당 child를 기억해두고 다른 child로 바뀌게 되었을 때 detach 시킵니다.
기존 attachLoggedOut은 private 였으니 public 으로 route 시켜주는 두 녀석을 만들거예요.
아래처럼 선언부터 하고 봅니다.
{ .... override func didLoad() { super.didLoad() routeToLoggedOut() } func routeToTodo() { detachCurrentChild() attachTodo() } func routeToLoggedOut() { detachCurrentChild() attachLoggedOut() } .... }
그리고 이제 detachCurrentChild 와 attachTodo 를 구현해요.
detach 시키는 것은 detachChild 로 RIB 을 떼어내주고,
화면에서의 제거는 viewController의 메소드를 호출해서 정리해 주어야 합니다.
(저는 첨에 detachChild 하면 다 되는줄 알았..){ .... private var currentChild: ViewableRouting? private func attachLoggedOut() { let loggedOut = loggedOutBuildable.build(withListener: interactor) self.currentChild = loggedOut attachChild(loggedOut) viewController.present(viewControllable: loggedOut.viewControllable) } private func attachTodo() { let todo = todoBuildable.build(withListener: interactor) // 지금은 오류가 난다. self.currentChild = todo attachChild(todo) viewController.present(viewControllable: todo.viewControllable) } private func detachCurrentChild() { if let currentChild = currentChild { detachChild(currentChild) // RIB에서 떼어내기 viewController.dimiss(viewController: currentChild.viewControllable) // 화면에서 닫아주기. 지금은 오류가 난다. } } .... }
이러면 RootRouter 의 구조는 대충 된거 같네요.
오류가 나는 부분을 잡아봅니다.
RootInteractable 에 TodoListener 가 안 맞으니까 프로토콜을 상속해서 오류를 제거해 줍니다.
protocol RootInteractable: Interactable, LoggedOutListener, TodoListener { var router: RootRouting? { get set } var listener: RootListener? { get set } }
이번엔 기존에 없던 dismiss를 구현해 줄꺼에요.
저걸 보면 RootViewController 에서 dismiss 라는걸 호출하는 모양이니까
RootViewControllable 에 dismiss를 쓰겠다고 선언해 줍니다.
protocol을 미리 예상 할 수 있다면 아래 선언을 초기에 맞춰 주는게 POP스러운 모습일듯 하네요.
담엔 그렇게 해야지...(반성)
protocol RootViewControllable: ViewControllable { // TODO: Declare methods the router invokes to manipulate the view hierarchy. func present(viewControllable: ViewControllable) func dismiss(viewControllable: ViewControllable) }
protocol 이 바뀌었으니 구현체에도 오류가 날꺼에요
빌드를 눌러서 확인해 봅니다.
dismiss 로 어떻게 화면을 닫을지 구현해 줍니다.
단, presentingViewController 가 실제 닫아야 할 viewController 인지 확인하고 닫는 것으로 합니다.
{ .... func dismiss(viewControllable: ViewControllable) { if presentedViewController === viewControllable.uiviewController { dismiss(animated: true, completion: nil) } } }
그럼 이제 전환에 관련된 로직은 다 된것 같네요.
이제 LoggedOut 에서 버튼하나 추가해서
버튼 누르면 LoggedOut 닫고
Todo 를 붙여주는 구현을 하면 될텐데...
포스트 너무 길어져서 보기 힘드네요
이 포스트에 #1 달고
다음 포스트로 이어 가겠습니다.
git : github.com/maarteti/rib-test/tree/todo-step1