apple/DesignPattern & Architecture

[Swift] Coodinator Pattern

lgvv 2022. 7. 7. 19:40

Coodinator Pattern

✅ Coodinator Pattern

 

아래의 문서를 구입하여 영어 문서를 번역하고 이해한 것을 바탕으로 글을 작성하고 있습니다.

https://www.raywenderlich.com/books/design-patterns-by-tutorials/v3.0/chapters/23-coordinator-pattern

 

Design Patterns by Tutorials, Chapter 23: Coordinator Pattern

The coordinator pattern is a structural design pattern for organizing flow logic between view controllers. This pattern can be adopted for only part of an app, or it can be used as an “architectural pattern” to define the structure of an entire app. Yo

www.raywenderlich.com

 

이 패턴은 뷰 컨트롤러 간의 흐름 로직을 구성하기 위해 구조적 디자인 패턴이다. 여기에 아래의 이미가 포함된다.

Coordinator 패턴

 

 

1. coordinator는 모든 concrete coordinator가 구현해야 하는 메소드와 속성을 정의하는 프로토콜입니다.

2. concrete coordinator는 coordinator protocol을 구현합니다.

3. router는 모든 concrete router가 구현해야 하는 방법을 정의하는 프로토콜입니다. 특히, 뷰의 present와 dismiss를 정의합니다.

4. concrete router는 어떤 viewController를 표시하는지 알고 있지만, 내용이나 view에 대한 정보는 알지 못합니다. 그냥 어떤 ViewController러 호출할 지 알려줍니다.

5. concrete ViewController는 MVC에서 흔하게 볼 수 있는 하위 클래스 대신 화면전환을 수행할 때 Coordinator에 위임합니다.

 

이 패턴은 앱의 일부에만 적용하거나 전체 앱의 구조를 정의하는 "아키텍쳐 패턴"으로 사용할 수 있습니다.

 

When should you use it?

이 패턴을 활용하여 ViewController를 서로 분리할 수 있습니다. ViewController를 직접알고 있는 유일한 구성 요소는 Coordinator입니다.

 

결과적으로 ViewController는 훨씬 더 재사용이 가능하며, 앱 내에서 새로운 흐름을 만들고 싶다면 새로운 Coordinator를 만들기만 하면 됩니다.

 

 

 

Example

✅ SceneDelegate.swift

//
//  SceneDelegate.swift
//  CoordinatorPattern
//
//  Created by Hamlit Jason on 2022/08/18.
//

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    var mainCoordinator: MainCoordinator?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
       
        guard let windowScene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: windowScene)
        window?.backgroundColor = .systemBackground
        
        let navigationController = UINavigationController()
        mainCoordinator = MainCoordinator(navigationController: navigationController)
        mainCoordinator?.start()
        
        window?.rootViewController = navigationController
        window?.makeKeyAndVisible()
    }


}

 

✅ 구현부

import UIKit
import SnapKit

protocol Coordinator: AnyObject {
    var childCoordinators: [Coordinator] { get set }
    var navigationController: UINavigationController { get set }
    
    func start()
}

class MainCoordinator: Coordinator {
    var childCoordinators: [Coordinator] = []
    var navigationController: UINavigationController
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() {
        let mainViewContoller = MainViewController()
        mainViewContoller.coordinator = self
        navigationController.pushViewController(mainViewContoller, animated: true)
    }
    
    func pushToSecondViewController() {
        let secondViewCoordinator = SecondViewCoordinator(navigationController: navigationController)
        secondViewCoordinator.parentCoordinator = self
        childCoordinators.append(secondViewCoordinator) // 메모리에서 제거되지 않도록
        secondViewCoordinator.start()
    }
}

class MainViewController: UIViewController {
    weak var coordinator: MainCoordinator?
    
    let button = UIButton()
    override func viewDidLoad() {
        super.viewDidLoad()
        
        [button].forEach { view.addSubview($0) }
        button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
        button.setTitle("프레젠트 버튼", for: .normal)
        button.backgroundColor = .black
        button.snp.makeConstraints {
            $0.center.equalToSuperview()
        }
    }
    
    @objc
    func didTapButton(_ sender: UIButton) {
        print("\(#function)")
        self.coordinator?.pushToSecondViewController()
        
    }
}

class SecondViewCoordinator: Coordinator {
    var childCoordinators: [Coordinator] = []
    var navigationController: UINavigationController
    weak var parentCoordinator: MainCoordinator?
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() {
        let secondViewController = SecondViewController()
        secondViewController.coordinator = self
        navigationController.pushViewController(secondViewController, animated: true)
    }
}

class SecondViewController: UIViewController {
    weak var coordinator: Coordinator?
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}