티스토리 뷰
이전 시간까지는 MainViewController를 화면에 표현하여 주고 Button Action을 받아올수 있도록 만들었습니다.
2020/10/04 - [Mobile/iOS] - RIBs를 이용한 개발 - 3. Main RIBs
이번에는 만들어진 이것을 이용하여서 Click을 하게 되면 ViewController를 present하고 Component를 전달하도록 하겠습니다.
MainRIB를 고칩니다.
MainViewController
아래의 두가지 부분을 이렇게 고쳐줍시다.
// MARK: - ViewControllable
protocol MainViewControllable: ViewControllable {
func present(_ viewController: ViewControllable, animated: Bool)
func dismiss(_ viewController: ViewControllable, animated: Bool)
}
extension MainViewController: MainViewControllable {
func present(_ viewController: ViewControllable, animated: Bool) {
present(viewController.uiviewController, animated: animated)
}
func dismiss(_ viewController: ViewControllable, animated: Bool) {
guard !viewController.uiviewController.isBeingDismissed else { return }
viewController.uiviewController.dismiss(animated: animated)
}
}
MainInteractor
이곳은 이전글에서 만든 function 이름이 마음에 안들어서 수정한 부분도 있으니 이것도 수정합니다.
// MARK: - Listener
protocol MainListener: class {
}
// MARK: - Routing
protocol MainRouting: ViewableRouting {
func attachNext()
func detachNext()
}
// MARK: - Interactor
final class MainInteractor: PresentableInteractor<MainPresentable>,
MainInteractable {
private let buttonTextRelay: BehaviorRelay<String>
var router: MainRouting?
var listener: MainListener?
init(title: String,
presenter: MainPresentable) {
buttonTextRelay = .init(value: title)
super.init(presenter: presenter)
presenter.handler = self
}
override func didBecomeActive() {
super.didBecomeActive()
setup()
}
private func setup() {
guard let action = presenter.action else { return }
action.didClickButton
.bind { [weak self] in
self?.router?.attachNext()
}
.disposeOnDeactivate(interactor: self)
}
}
extension MainInteractor: MainPresentableHandler {
var buttonText: Observable<String> {
return buttonTextRelay.asObservable()
}
}
extension MainInteractor: MainListener {
func detachNext() {
router?.detachNext()
}
}
MainRouter
라우터도 수정을 하여 주어야 하는데요.
NextBuilder를 가지고 사용하는 방법으로 만들어 줍니다.
import RIBs
final class MainRouter: LaunchRouter<MainInteractable, MainViewControllable> {
private let nextBuilder: NextBuilable
private var nextRouter: NextRouting?
init(nextBuilder: NextBuilable,
interactor: MainInteractable,
viewController: MainViewControllable) {
self.nextBuilder = nextBuilder
self.nextRouter = nil
super.init(interactor: interactor, viewController: viewController)
interactor.router = self
}
}
extension MainRouter: MainRouting {
func attachNext() {
let time = "Now is \(Date().description)"
let router = nextBuilder.build(with: interactor, title: time)
nextRouter = router
attachChild(router)
viewController.present(router.viewControllable, animated: true)
}
func detachNext() {
guard let router = nextRouter else { return }
viewController.dismiss(router.viewControllable, animated: true)
nextRouter = nil
detachChild(router)
}
}
MainBuilder
이곳에서 Builder를 이용하여서 Router를 만들어 주도록 할게요
import RIBs
// MARK: - Component
protocol MainDependency: Dependency {}
final class MainComponent: Component<MainDependency>, NextDependency {
var title: String = ""
}
// MARK: - Builder
protocol MainBuilable: Buildable {
func build() -> LaunchRouting
}
final class MainBuilder: Builder<MainDependency>, MainBuilable {
func build() -> LaunchRouting {
let component = MainComponent(dependency: dependency)
let viewController = MainViewController()
let interactor = MainInteractor(title: "Hello, RIBs\nClick Button!",
presenter: viewController)
let nextBuilder = NextBuilder(dependency: component)
return MainRouter(nextBuilder: nextBuilder,
interactor: interactor,
viewController: viewController)
}
}
Group을 만들어 줍시다.
저는 NextRIB를 만들었는데 자신의 상황에 맞도록 만들어 주세요.
NextViewController
이전의 MainViewController와 거의 흡사합니다.
import RIBs
import SnapKit
import RxSwift
import RxCocoa
// MARK: - Presenter
protocol NextPresentableAction: class {
var didClickButton: Observable<Void> { get }
}
protocol NextPresentableHandler: class {
var timeText: Observable<String> { get }
}
protocol NextPresentable: Presentable {
var action: NextPresentableAction? { get set }
var handler: NextPresentableHandler? { get set }
}
// MARK: - ViewControllable
protocol NextViewControllable: ViewControllable {
}
// MARK: - ViewController
final class NextViewController: UIViewController, NextPresentable {
// MARK: - UI Components
private lazy var button: UIButton = {
let button = UIButton(type: .system)
button.setTitleColor(.blue, for: .normal)
button.titleLabel?.numberOfLines = 1
button.titleLabel?.textAlignment = .center
button.setTitle("Close", for: .normal)
return button
}()
private lazy var label: UILabel = {
let label = UILabel()
label.textColor = .black
label.numberOfLines = 0
return label
}()
// MARK: - Properties
private let disposeBag = DisposeBag()
// MARK: - Presentable
var action: NextPresentableAction?
var handler: NextPresentableHandler?
init() {
super.init(nibName: nil, bundle: nil)
action = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
setupView()
bindPresentable()
}
private func setupView() {
view.backgroundColor = .white
view.addSubview(button)
button.snp.makeConstraints { maker in
maker.center.equalToSuperview()
}
view.addSubview(label)
label.snp.makeConstraints { maker in
maker.centerX.equalToSuperview()
maker.bottom.equalTo(button.snp.top).offset(-30)
}
}
private func bindPresentable() {
guard let handler = handler else { return }
handler.timeText
.bind(to: label.rx.text)
.disposed(by: disposeBag)
}
}
extension NextViewController: NextPresentableAction {
var didClickButton: Observable<Void> {
return button.rx.tap.asObservable()
}
}
extension NextViewController: NextViewControllable {
}
NextInteractor
Main과 다른점은 listener를 통해서 detachNext를 호출하는 것이 다릅니다.
import RIBs
import RxSwift
import RxCocoa
// MARK: - Listener
protocol NextListener: class {
func detachNext()
}
// MARK: - Routing
protocol NextRouting: ViewableRouting {
}
// MARK: - Interactable
protocol NextInteractable: Interactable {
var router: NextRouting? { get set }
var listener: NextListener? { get set }
}
// MARK: - Interactor
final class NextInteractor: PresentableInteractor<NextPresentable>,
NextInteractable {
private let timeTextRelay: BehaviorRelay<String>
var router: NextRouting?
var listener: NextListener?
init(component: NextComponent,
presenter: NextPresentable) {
timeTextRelay = .init(value: component.dependency.title)
super.init(presenter: presenter)
presenter.handler = self
}
override func didBecomeActive() {
super.didBecomeActive()
setup()
}
private func setup() {
presenter.action?.didClickButton
.bind { [weak self] in
self?.listener?.detachNext()
}
.disposeOnDeactivate(interactor: self)
}
}
extension NextInteractor: NextPresentableHandler {
var timeText: Observable<String> {
return timeTextRelay.asObservable()
}
}
NextRouter
다음 화면 정의같은 부분들이 없어서 별다른 동작이 없습니다.
import RIBs
final class NextRouter: ViewableRouter<NextInteractable, NextViewControllable> {
override init(interactor: NextInteractable,
viewController: NextViewControllable) {
super.init(interactor: interactor, viewController: viewController)
interactor.router = self
}
}
extension NextRouter: NextRouting {
}
NextBuilder
import RIBs
// MARK: - Component
protocol NextDependency: Dependency {
var title: String { get set }
}
final class NextComponent: Component<NextDependency> {
}
// MARK: - Builder
protocol NextBuilable: Buildable {
func build(with listener: NextListener, title: String) -> NextRouting
}
final class NextBuilder: Builder<NextDependency>, NextBuilable {
func build(with listener: NextListener, title: String) -> NextRouting {
dependency.title = title
let component = NextComponent(dependency: dependency)
let viewController = NextViewController()
let interactor = NextInteractor(component: component,
presenter: viewController)
interactor.listener = listener
return NextRouter(interactor: interactor, viewController: viewController)
}
}
다른분들과 특이하게 만들어진 부분은 NextBuilder를 build할때 title을 받아서 이전 MainRIB에서 전달하는 값을 dependency로 전달하여서 사용하도록 하였습니다.
종합?
저는 MVVM을 하기 때문에 다소 불편한 부분이 많습니다.
결론은 저는 아마도 쓰지 않을거 같긴 합니다만
하지만 이정도만 알아둬도 나중에 이 패턴을 이용하여서 더 좋은 패턴을 만들수 있지 않을까요?
Dependency 부분을 어떻게 해야지 주입하기 좋을까에 대한 부분은 조금 생각을 해봐야할거 같습니다.
protocol에 set까지 같이 주면 외부에서 값이 변경이 되기 때문에 좋지 않은것 같기 때문이죠.
소스는 git에 올려두었습니다.
github.com/outofcode-example/iOS-RIBsExample/tree/NextPage
'Mobile > iOS' 카테고리의 다른 글
SwiftUI 처럼 Code로 View를 만들고 Preview를 바로 바로 보여주면서 개발하기 (0) | 2020.12.19 |
---|---|
UIWindowSceneDelegate란 무엇인가? 어떨때 필요한가? (0) | 2020.12.19 |
RIBs를 이용한 개발 - 3. Main RIBs (0) | 2020.10.04 |
RIBs를 이용한 개발 - 2. RIBs Setting. (0) | 2020.10.03 |
RIBs를 이용한 개발 - 1. RIBs란? (0) | 2020.10.03 |
- Total
- Today
- Yesterday
- windows10
- Windows
- android
- centos8
- ios
- enum
- Linux
- Spring
- MySQL
- war
- php
- SWIFT
- tomcat
- go
- Java
- git
- Kotlin
- ubuntu
- golang
- intellij
- Codable
- cocoapods
- Python
- Xcode
- CentOS
- rxswift
- Gradle
- docker
- nodejs
- github
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |