Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 스위프트
- combine
- SWIFT
- Human interface guide
- clean architecture
- swift documentation
- rxswift
- Refactoring
- swiftUI
- Observable
- ribs
- ios
- 리팩토링
- collectionview
- RxCocoa
- Protocol
- Clean Code
- uitableview
- HIG
- UITextView
- uiscrollview
- map
- MVVM
- 애니메이션
- UICollectionView
- Xcode
- 리펙토링
- 리펙터링
- 클린 코드
- tableView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[Clean Architecture] 6. 코드로 알아보는 SOLID - Coordinator 패턴 화면전환 본문
Clean Architecture/Clean Architecture 코드
[Clean Architecture] 6. 코드로 알아보는 SOLID - Coordinator 패턴 화면전환
jake-kim 2021. 9. 21. 14:15
0. 코드로 알아보는 SOLID - 클래스 다이어그램 필수 표현
1. 코드로 알아보는 SOLID - SRP(Single Responsibility Principle) 단일 책임 원칙
2. 코드로 알아보는 SOLID - OCP(Open Close Principle) 개방 폐쇄 원칙
3. 코드로 알아보는 SOLID - LSP(Liskov Substitution Principle) 리스코프 치환 원칙
4. 코드로 알아보는 SOLID - ISP(Interface Segregation Principle) 인터페이스 분리 원칙
5. 코드로 알아보는 SOLID - DIP(Dependency Inversion Principle, testable) 의존성 역전 원칙
6. 코드로 알아보는 SOLID - Coordinator 패턴 화면전환
7. 코드로 알아보는 SOLID - Network, REST (URLSession, URLRequest, URLSessionDataTask)
8. 코드로 알아보는 SOLID - 캐싱 Disk Cache (UserDefeaults, CoreData)
Coordinator 패턴
- 화면전환하는 기능을 분리
- Coordinator를 사용하지 않은 경우 A -> B -> C 화면전환 시, A -> C로 화면전환 하려면, A에 코드가 새로 생성되어야 하지만 Coordinator를 사용하면 기존에 C로 이동하는 코드가 작성되어 있으므로 그대로 사용
Coordinator 사용
- ViewModel에서 dependencies로 가지고 있는 ViewModelAction을 통해 화면전화하는데, ViewModelAction은 Coordinator에서 구현하여 주입하는 형태
- ViewModel은 Action을 사용함으로써 화면전환 코드에 의존하지 않는 형태
- DIContainer - Coordinator - ViewModel 화면 전환을 구현부터 사용하기까지
코드에서 Coordinator 구현 방법
- 테크닉: 구현된 클로저를 파라미터로 넘겨서, 인수는 사용하는 곳에서 넣어줌으로서 `구현`하는 부분과 `인수`를 넣어주는 부분을 다르게 하는 방법
- 화면전환시 필요한 ViewController 생성은 DIContainer에서 정의, ViewModel의 Action에 따라 화면 전환 로직은 Coordinator에서 정의, Action은 ViewModel에서 호출
Coordinator와 Action
- Action은 ViewModel이 가지고 있다가 Coordinator를 호출할 때 사용
- ViewModel에서는 Coordinator에 의존하지 않게끔하여 화면전환 코드에 의존하지 않게끔 설계
- Coordinator와 ViewModel에서의 Action관련 코드
예제) Red, Blue 버튼을 누르면 해당 ViewController로 이동하는 앱
- Coordinator와 Action을 사용하여 구현
- 구조 설계
- ButtonViewModel에 존재하는 ButtonsViewModelActions
struct ButtonsViewModelActions {
let showRedButton: (Int) -> Void
let showBlueButton: (Int) -> Void
}
...
final class ButtonsViewModelImpl: ButtonsViewModel {
private let actions: ButtonsViewModelActions
...
init(actions: ButtonsViewModelActions, buttonUseCase: ButtonsUseCase) {
self.actions = actions
self.buttonsUseCase = buttonUseCase
}
...
// Input
func didTapRedButton() {
buttonsUseCase.redButtonCnt += 1
actions.showRedButton(buttonsUseCase.redButtonCnt)
}
func didTapBlueButton() {
buttonsUseCase.blueButtonCnt += 1
actions.showBlueButton(buttonsUseCase.blueButtonCnt)
}
}
- Action은 Coordinator에서 구현
protocol ButtonsCoordinatorDependencies {
func makeButtonViewController(actions: ButtonsViewModelActions) -> ButtonsViewController
func makeBlueViewController(tapCount: Int) -> UIViewController
func makeRedViewController(tapCount: Int) -> UIViewController
}
final class ButtonsCoordinator {
private weak var navigationController: UINavigationController?
private let dependencies: ButtonsCoordinatorDependencies
init(navigationController: UINavigationController,
dependencies: ButtonsCoordinatorDependencies) {
self.navigationController = navigationController
self.dependencies = dependencies
}
func start() {
let actions = ButtonsViewModelActions(showRedButton: showRed(tapCount:),
showBlueButton: showBlue(tapCount:))
let vc = dependencies.makeButtonViewController(actions: actions)
navigationController?.pushViewController(vc, animated: false)
}
// Private
private func showRed(tapCount: Int) {
let vc = dependencies.makeRedViewController(tapCount: tapCount)
navigationController?.pushViewController(vc, animated: true)
}
private func showBlue(tapCount: Int) {
let vc = dependencies.makeBlueViewController(tapCount: tapCount)
navigationController?.pushViewController(vc, animated: true)
}
}
- action을 통해 화면 전환이 되는데, 화면전활 할때 필요한 ViewController는 DIContainer에서 구현
- extension으로 구현 후, makeButtonCoordinator에서 dependencies: self로 주입
class ButtonsDIContainer {
func makeButtonCoordinator(navigationController: UINavigationController) -> ButtonsCoordinator {
return ButtonsCoordinator(navigationController: navigationController, dependencies: self)
}
// Private
private func makeButtonsUseCaseImpl() -> ButtonsUseCase {
return ButtonsUseCaseImpl()
}
private func makeButtonsViewModel(actions: ButtonsViewModelActions) -> ButtonsViewModel {
return ButtonsViewModelImpl(actions: actions, buttonUseCase: makeButtonsUseCaseImpl())
}
}
extension ButtonsDIContainer: ButtonsCoordinatorDependencies {
func makeButtonViewController(actions: ButtonsViewModelActions) -> ButtonsViewController {
return ButtonsViewController.create(with: makeButtonsViewModel(actions: actions))
}
func makeRedViewController(tapCount: Int) -> UIViewController {
return RedViewController.create(tapCount: tapCount)
}
func makeBlueViewController(tapCount: Int) -> UIViewController {
return BlueViewController.create(tapCount: tapCount)
}
}
- 첫 화면은 DI와 Coordinator를 이용하여 이동
let buttonsDIContainer = ButtonsDIContainer()
let coordinator = buttonsDIContainer.makeButtonCoordinator(navigationController: navigationController!)
coordinator.start()
* 전체 소스 코드 `Coordinator Pattern` 하위 그룹에 존재: https://github.com/JK0369/SOLID
'Clean Architecture > Clean Architecture 코드' 카테고리의 다른 글
Comments