apple/DesignPattern, Architecture

Swift 디자인패턴 Coodinator Pattern (코디네이터 패턴)

lgvv 2022. 7. 7. 19:40

Swift 디자인패턴 Coodinator Pattern (코디네이터 패턴)

 

Coordinator 패턴은 화면 전환 및 네비게이션 로직을 분리하여 ViewController의 역할을 단순화하고, 앱의 화면 흐름을 관리하기 위한 아키텍처 패턴.

 

히스토리

  • 2022-07-07: 디자인 패턴 스터디 정리
  • 2024-11-29: 포스팅 글 재정리 및 조금 더 실용적인 예제 코드로 변경

 

Coordinator Pattern

 

 

Coordinator Pattern

코디네이터 패턴은 일반적으로 아래의 구조로 구성

  • Coordinator는 모든 구체적인 Coordinator들이 반드시 구현해야 하는 메서드와 속성을 정의하는 인터페이스
    • children과 router를 정의하며, 화면 전환 메서드인 present와 dismiss도 정의
    • Concrete Coordinator 대신 Coordinator 인터페이스를 사용하여, 부모 Coordinator와 자식 Coordinator 간의 결합도 낮춤
    • 부모 Coordinator는 하나의 children coodinator 프로퍼티를 통해 여러 자식 Coordinator를 관리할 수 있음
    • Concrete Router 대신 Router 인터페이스를 사용해, Coordinator와 Router 간의 결합을 줄임
  • Concrete Coordinator는 Coordinator 프로토콜을 구현한 객체
    • 해당 객체에서 ViewController의 생성 방법 및 push 된 순서를 알고 있음
  • Router는 모든 구체적인 Router들이 반드시 구현해야 하는 메서드를 정의하는 인터페이스
    • ViewContoller의 presentdismiss 메서드를 포함
  • Concrete Router는 ViewContoller를 present하는건 알지만 구체적인 객체에 대해서는 모름
    • Coordinator가 Router에게 어떤 ViewController를 사용할지 알려줌
  • Concrete View Controller는 MVC에서 일반적으로 사용되는 UIViewController의 서브클래스.
    • 다른 뷰 컨트롤러에 대한 정보를 가지지 않으며 화면 전환이 필요한 경우에 Coordinator에 위임

 

코드 예제

코디네이터 패턴을 가장 간단하게 사용하는 예제

//
//  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()
    }
}

 

(참고)
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.kodeco.com:443