
꼭 1편과 2편을 보고 글을 읽어주시면 좋겠습니다.


이전 시간에 배운 방법으로 똑같이 템플릿을 통해서
Login Ribs를 세팅해주시면 됩니다.
View가 존재하는 Rib이기 때문에 첫번째를 체크하고 진행 하셔야 합니다.
빠른 진행을 위해 간단하게 설계 했습니다.
import UIKit
final class LoginView: UIView {
// 시작 버튼
let button = UIButton().after { // after 는 Then 라이브러리 같이 설계되어 있습니다.
var config = UIButton.Configuration.filled()
config.title = "로그인"
config.contentInsets = NSDirectionalEdgeInsets(top: 12, leading: 20, bottom: 12, trailing: 20)
$0.configuration = config
$0.translatesAutoresizingMaskIntoConstraints = false
}
override init(frame: CGRect) {
super.init(frame: frame)
setUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: UI Layer
extension LoginView {
private func setUI() {
self.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: self.centerXAnchor),
button.centerYAnchor.constraint(equalTo: self.centerYAnchor),
])
}
}
UI 를 구성했으니 위와같은 버튼을 Interactor 에게 알려주어야 하겠죠?
RIBs 내부에 RxSwift가 있어서 RxSwift 를 사용해 보겠습니다.
protocol LoginPresentableListener: AnyObject {
// TODO: Declare properties and methods that the view controller can invoke to perform
// business logic, such as signIn(). This protocol is implemented by the corresponding
// interactor class.
// 로그인 버튼 클릭
func touchToLogin(name: String)
}
final class LoginViewController: UIViewController, LoginPresentable, LoginViewControllable {
private let baseView = LoginView()
weak var listener: LoginPresentableListener?
override func loadView() {
super.loadView()
self.view = baseView
subscribe()
}
}
extension LoginViewController {
private func subscribe() {
baseView.button.rx.tap
.bind(with: self) { owner, _ in
owner.listener?.touchToLogin(name: "테스트를 위함")
}
.disposed(by: rx.disposeBag)
}
}
자 프로토콜을 통해 저희는 저런 이벤트를 구성하겠다고 약속을 한거죠?
그것을 구현해야 합니다.
로그인이 완료 되면 상위에게 뷰를 옮겨달라 라는 메시지를 전달하고자 합니다.
일단은요. -> 다음편에서는 구조가 바뀔 예정입니다.
protocol LoginListener: AnyObject {
// TODO: Declare methods the interactor can invoke to communicate with other RIBs.
func didLogin(name: String)
}
final class LoginInteractor: PresentableInteractor<LoginPresentable>, LoginInteractable, LoginPresentableListener {
weak var router: LoginRouting?
weak var listener: LoginListener?
// TODO: Add additional dependencies to constructor. Do not perform any logic
// in constructor.
override init(presenter: LoginPresentable) {
super.init(presenter: presenter)
presenter.listener = self
}
override func didBecomeActive() {
super.didBecomeActive()
// TODO: Implement business logic here.
}
override func willResignActive() {
super.willResignActive()
// TODO: Pause any business logic.
}
// 로그인 버튼 클릭시
func touchToLogin(name: String) {
let name = name
// 비즈니스 로직 거친후 (생략) -> Root 에게 Home으로 가라고 명령
listener?.didLogin(name: name)
}
}
자 이제 구성한 이벤트들이나, 의존성등을 구성하는 친구를 구현을 해야합니다.
그래야 Init() 을 해서 사용할 수 있겠죠?
protocol LoginBuildable: Buildable {
func build(withListener listener: LoginListener) -> LoginRouting
}
final class LoginBuilder: Builder<LoginDependency>, LoginBuildable {
override init(dependency: LoginDependency) {
super.init(dependency: dependency)
}
func build(withListener listener: LoginListener) -> LoginRouting {
let component = LoginComponent(dependency: dependency)
let viewController = LoginViewController()
let interactor = LoginInteractor(presenter: viewController)
interactor.listener = listener
return LoginRouter(interactor: interactor, viewController: viewController)
}
}
자 위에서 말했듯이 상위에게 이벤트를 전달하고자
Listener를 세팅했죠?
상위가 바로 루트이기 때문에 다음과 같이 이벤트를 감지 하고자 합니다.
protocol RootInteractable: Interactable, LoginListener { // <- 이부분
var router: RootRouting? { get set }
var listener: RootListener? { get set }
}
final class RootInteractor: PresentableInteractor<RootPresentable>, RootInteractable, RootPresentableListener {
weak var router: RootRouting?
weak var listener: RootListener?
// TODO: Add additional dependencies to constructor. Do not perform any logic
// in constructor.
override init(presenter: RootPresentable) {
super.init(presenter: presenter)
presenter.listener = self
}
...
func didLogin(name: String) { // <- 이부분
router?.routToHome(name: name)
}
}
protocol RootRouting: ViewableRouting {
// TODO: Declare methods the interactor can invoke to manage sub-tree via the router.
func routToLogin()
}
final class RootRouter: LaunchRouter<RootInteractable, RootViewControllable>, RootRouting {
private let loginBuilder: LoginBuildable
private let homeBuilder: HomeBuildable
private var currentChild: ViewableRouting?
private var loginRouter: LoginRouting?
private var homeRouter: HomeRouting?
init(interactor: RootInteractable,
viewController: RootViewControllable,
loginBuilder: LoginBuildable,
homeBuilder: HomeBuildable) {
self.loginBuilder = loginBuilder
self.homeBuilder = homeBuilder
super.init(interactor: interactor, viewController: viewController)
interactor.router = self
}
override func didLoad() {
super.didLoad()
routToLogin()
}
func routToLogin() {
if let child = currentChild {
detachChild(child)
}
let login = loginBuilder.build(withListener: interactor)
attachChild(login)
currentChild = login
loginRouter = login
viewController.setRoot(login.viewControllable, animated: true)
}
func routToHome(name: String) { // 다음 시간에
if let child = currentChild {
detachChild(child)
}
let home = homeBuilder.build(withListener: interactor, name: name)
attachChild(home)
currentChild = home
homeRouter = home
viewController.setRoot(home.viewControllable, animated: true)
}
}
final class RootViewController: UIViewController, RootPresentable, RootViewControllable {
private let container = UIView()
private weak var current: UINavigationController?
weak var listener: RootPresentableListener?
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
view = container
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
}
func setRoot(_ vc: ViewControllable, animated: Bool) {
let rootVC = vc.uiviewController
let nav = UINavigationController()
nav.setViewControllers([rootVC], animated: false) // 초기 세팅은 false 권장
// 기존 제거
if let current {
current.willMove(toParent: nil)
current.view.removeFromSuperview()
current.removeFromParent()
}
addChild(nav)
container.addSubview(nav.view)
nav.view.frame = container.bounds
nav.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
nav.didMove(toParent: self)
current = nav
// 페이드 인
if animated {
container.alpha = 0
UIView.animate(withDuration: 0.2) { [weak self] in
self?.container.alpha = 1
}
}
}
}
이번편은 자식 RIBs가 하나 생겨서 연결하는 작업을 했습니다.
사실 Home뷰도 만들어서 왔다갔다 하는거 까지를 이번편으로 하려 했는데
사실 Home같은 경우는 Home 안에서도 뷰가 많겠죠? 이동하는 뷰들이
그래서 HomeFlow 라고 해서 View가 없는 RIB을 생성해서 Home의 뷰들을 통제하는
중계소? 같은 것으로 구성을 또 하게 되서 루즈해지니
Login만 설명을 했구요 다음편은 HomeView 빠르게 뷰와 라우터 만 설명을 한후
FlowRIB 설명을 끝으로 RIBs 편을 마무리 할까 고민중입니다.
궁금하신 사항 있으시면 꼭 말씀 부탁드립니다.
감사합니다.