Bibi's DevLog ๐Ÿค“๐ŸŽ

[iOS] Coordinator (2) - ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด, ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ๊ตฌํ˜„ํ•˜๊ธฐ ๋ณธ๋ฌธ

๐Ÿ“ฑ๐ŸŽ iOS

[iOS] Coordinator (2) - ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด, ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ๊ตฌํ˜„ํ•˜๊ธฐ

๋น„๋น„ bibi 2022. 11. 25. 01:13

โœ”๏ธ ์ด ๊ธ€์˜ ์‹œ๋ฆฌ์ฆˆ๋Š” ์•„๋ž˜์˜ ์„ธ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Coordinator Pattern

How to use the coordinator pattern in iOS apps

Advanced coordinators in iOS

โœ”๏ธ ์•„๋ž˜์˜ ๋‘ ๋ฌธ์„œ๋Š” ์กฐ์•…ํ•˜๊ฒŒ๋‚˜๋งˆ ๋ฒˆ์—ญ์„ ํ•ด ๋‘์—ˆ์œผ๋‹ˆ ํ˜น์‹œ ํ•„์š”ํ•˜์‹  ๋ถ„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

โœ”๏ธ ์ด์ „ ํŽธ์ธ (1) - ์ฝ”๋””๋„ค์ดํ„ฐ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ๋Š” ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค.

โœ”๏ธ ๋…ธ์…˜์ด ๋” ํŽธํ•˜์‹œ๋‹ค๋ฉด ์—ฌ๊ธฐ์˜ 1,2ํŽธ ํ†ตํ•ฉ๋ณธ ๋ฅผ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.


Coordinator ๊ตฌํ˜„

์ด์ œ ์„ค๋ช…์€ ๋งˆ์น˜๊ณ  ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉฐ ๊ตฌํ˜„ํ•ด ๋ด…์‹œ๋‹ค.

์˜ˆ์ œ ํ”„๋กœ์ ํŠธ์ธ CoordinatorPractice๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

https://user-images.githubusercontent.com/67407678/203829373-74d4472e-2bc7-4652-afea-db372af523aa.png

CoordinatorPractice์˜ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

https://user-images.githubusercontent.com/67407678/203829410-e5662008-04ef-424f-ad5f-354725df426c.png

1. Coordinator ํ”„๋กœํ† ์ฝœ ๋งŒ๋“ค๊ธฐ

๊ฐ€์žฅ ๋จผ์ €, ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ(AppCoordinator)์™€ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์ค€์ˆ˜ํ•  ํ”„๋กœํ† ์ฝœ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

Coordinator ํ”„๋กœํ† ์ฝœ

import UIKit

protocol Coordinator: AnyObject {
    var childCoordinators: [Coordinator] { get set }
    var navigationController: UINavigationController { get set }

    func start()
}
  • childCoordinators : ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์ €์žฅ
    • ๋ชจ๋“  ์ฝ”๋””๋„ค์ดํ„ฐ๋Š” ์ž์‹ ์˜ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • navigationController : ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ณด์—ฌ์ค„ ๋•Œ ์‚ฌ์šฉ๋  ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ €์žฅ.
    • โ˜‘๏ธ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ”๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š๋”๋ผ๋„, ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์ด๋‹ค.
  • start(): ํ•ด๋‹น ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์ œ์–ด๊ถŒ์„ ๊ฐ–๋„๋ก ํ•˜๋Š” ๋ฉ”์„œ๋“œ. ์™„์ „ํžˆ ๋งŒ๋“ค๊ณ  ์ค€๋น„๋˜์—ˆ์„ ๋•Œ๋งŒ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ํ™œ์„ฑํ™”ํ•จ.
  • AnyObject : ํด๋ž˜์Šค์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ”„๋กœํ† ์ฝœ์ž„์„ ๋ช…์‹œ

2. AppCoordinator(๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ) ๋งŒ๋“ค๊ธฐ

AppCoordinator๋Š” ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ๋กœ,

์•ฑ ์‹คํ–‰๊ณผ ๋™์‹œ์— ์•ฑ์— ๋Œ€ํ•œ ์ œ์–ด๊ถŒ์„ ๊ฐ–๋Š” ์ฒซ ๋ฒˆ์งธ ์ฝ”๋””๋„ค์ดํ„ฐ์ด๋‹ค.

์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๊นŒ์ง€ ์žˆ๋Š” ๊ฒฝ์šฐ, AppCoordinator๋ผ๊ณ  ์ด๋ฆ„์ง“๊ธฐ๋„ ํ•œ๋‹ค.

AppCoordinator

import Foundation
import UIKit

class AppCoordinator: NSObject, Coordinator, UINavigationControllerDelegate {

    var childCoordinators: [Coordinator] = [] // child coordinator๊ฐ€ ํ• ๋‹น ํ•ด์ œ๋˜๋Š”๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
    var navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() {
        navigationController.delegate = self
        showLoginViewController()
    }

    private func showHomeViewController() {
        // ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ณ  ์ œ์–ด๊ถŒ์„ ๊ฐ–๊ฒŒ ํ•จ(start ํ˜ธ์ถœ)
        let child = HomeCoordinator(navigationController: navigationController)
        childCoordinators.append(child)
        child.start()
        child.parentCoordinator = self
    }

    private func showLoginViewController() {
        // ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ณ  ์ œ์–ด๊ถŒ์„ ๊ฐ–๊ฒŒ ํ•จ(start ํ˜ธ์ถœ)
        let child = LoginCoordinator(navigationController: navigationController)
        childCoordinators.append(child)
        child.start()
        child.parentCoordinator = self
    }

    func didLoggedIn() {
        navigationController.popViewController(animated: true)
        showHomeViewController()
        print("๋กœ๊ทธ์ธ -> ํ™ˆ")
    }

    func didLoggedOut() {
        navigationController.popViewController(animated: true)
        print("ํ™ˆ -> ๋กœ๊ทธ์ธ")
    }

}
  • ํ•ด๋‹น ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ๋‹ค๋ฅธ ๋ทฐ์ปจ๋“ค์— ์˜ํ•ด ๊ณต์œ ๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, struct๋ณด๋‹ค class๋กœ ์„ ์–ธ
  • Coordinator ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜
  • start() : ViewController ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , navigationController์— pushํ•œ๋‹ค.
  • didLoggedIn(), didLoggedOut() : ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋“ค์ด ํ™”๋ฉด ์ „ํ™˜์„ ์œ„ํ•ด AppCoordinator๋กœ๋ถ€ํ„ฐ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
    • ํ™”๋ฉด ์ „ํ™˜ ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด ์ „ํ™˜ํ•  ํ™”๋ฉด์„ ๋‹ด๋‹นํ•˜๋Š” ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋“ค์„ ๋จผ์ € ์ƒ์„ฑํ•˜๊ณ , ๊ทธ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์‹คํ–‰์‹œํ‚ต๋‹ˆ๋‹ค - showHomeViewController(), showLoginViewController() ์ฐธ์กฐ

โ“ AppCoordinator๊ฐ€ ์™œ NSObject๋ฅผ ์ƒ์†ํ•˜๊ณ  UINavigationControllerDelegate๋ฅผ ์ค€์ˆ˜ํ•˜๋‚˜์š”?

โ—๏ธ ์ด ๋ถ€๋ถ„์€ ์กฐ๊ธˆ ์–ด๋ ค์šด๋ฐ, ๋ฐ”๋กœ ์•„๋ž˜ ๋‹จ๋ฝ์— ๋‚˜๋ˆ„์–ด ์„ค๋ช…ํ•ด ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

3. AppCoordinator์— UINavigationControllerDelegate ์ฑ„ํƒ ํ›„ didShow() ๋ฉ”์„œ๋“œ ๊ตฌํ˜„ํ•˜๊ธฐ

์ด ์ž‘์—…์€ AppCoordinator๊ฐ€ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์ง์ ‘์ ์œผ๋กœ ๊ฐ์ง€ํ•˜๋„๋ก ํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค.

AppCoordinator์˜ ์œ„ ์ฝ”๋“œ์— ์•„๋ž˜ ๋ฉ”์„œ๋“œ๋“ค์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โ“ ์™œ ์ด ์ž‘์—…์„ ํ•˜๋‚˜์š”?

โ—๏ธ โ€œ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๋กœ๋ถ€ํ„ฐ ๋’ค๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์„ AppCoordinator๊ฐ€ ์ฒ˜๋ฆฌํ•ด ์ฃผ๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹คโ€

  • ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ”์—์„œ ๋’ค๋กœ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ pop๋ฉ๋‹ˆ๋‹ค.
  • ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์‚ฌ๋ผ์ง€๋ฉด ๊ทธ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ด€๋ฆฌํ•˜๋˜ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋„ ๋” ์ด์ƒ ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ, childCoordinators ์—์„œ ์ง€์›Œ ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ๋’ค๋กœ ๊ฐ€๋Š” ์ž‘์—…์€ Coordinator์— ์˜ํ•ด ๋ฐœ๋™๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค.

โ—๏ธ ๋‹คํ–‰ํžˆ UINavigationControllerDelegate ํ”„๋กœํ† ์ฝœ๋กœ ์ด๋Ÿฌํ•œ ์ด๋ฒคํŠธ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ทธ๋Ÿผ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ง์ „ ํ™”๋ฉด์ด ๋ญ์˜€๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ๋‚˜์š”??
    • ๊ทธ๊ฑธ ์•Œ๊ธฐ ์œ„ํ•ด 1. UINavigationControllerDelegate ๋ฅผ ์ฑ„ํƒํ•ด 2. ์•„๋ž˜์˜ navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค - ๊ณต์‹๋ฌธ์„œ
    • ์ด ๋ฉ”์„œ๋“œ๋Š” ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ทฐ์™€ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ ํ”„๋กœํผํ‹ฐ๋ฅผ ํ‘œ์‹œํ•œ โ€œ์ดํ›„โ€์— ๋Œ€๋ฆฌ์ž(delegate)์—๊ฒŒ ์•Œ๋ฆฌ๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
    • ํ™”๋ฉด์ „ํ™˜์ด ์žˆ์—ˆ๋‹ค๋ฉด, push๋“  pop์ด๋“  ๊ด€๊ณ„์—†์ด ํ™”๋ฉด ์ „ํ™˜ ์ดํ›„์— ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
  • NSObject ๋ฅผ ์ƒ์†ํ•˜๋Š” ์ด์œ ๋Š”, ์ด๋ฅผ ์ƒ์†ํ•ด์•ผ๋งŒ UINavigationControllerDelegate ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด์ œ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ณด์—ฌ์งˆ ๋•Œ๋งˆ๋‹ค ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ AppCoordinator์—๊ฒŒ ์•Œ๋ ค ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค!

// AppCoordinator.swift

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {

        print("viewControllers : \(navigationController.viewControllers)")
        print("childCoordinators : \(childCoordinators)")

        // ์–ด๋–ค ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋กœ๋ถ€ํ„ฐ ์ด๋™ํ•ด ์™”๋Š”์ง€ ์กฐํšŒ
        guard let fromViewController = navigationController.transitionCoordinator?.viewController(forKey: .from) else {
            return
        }

        // ํ•ด๋‹น ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ ๋ฐฐ์—ด์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ. ๋งŒ์•ฝ ์กด์žฌํ•œ๋‹ค๋ฉด, pop์ด ์•„๋‹Œ push๋ฅผ ํ•œ ๊ฒƒ์ด๋ฏ€๋กœ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•  ์ž‘์—…์€ ์—†๋‹ค.
        if navigationController.viewControllers.contains(fromViewController) {
            return
        }

        // ์—ฌ๊ธฐ๊นŒ์ง€ ๋„๋‹ฌํ–ˆ๋‹ค๋ฉด ํ•ด๋‹น ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ ๋ฐฐ์—ด์— ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋ฉฐ, ์ด๋Š” pop์ด ๋˜์—ˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค.
        // ๋”ฐ๋ผ์„œ ์–ด๋–ค ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ pop๋˜์—ˆ๋Š”์ง€ ํ™•์ธ ํ›„, ๊ทธ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ด€๋ฆฌํ•˜๋˜ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ childCoordinators๋กœ๋ถ€ํ„ฐ ์ง€์›Œ ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
        if let homeViewController = fromViewController as? HomeViewController {
            print("HomeVC์˜ ์ฝ”๋””๋„ค์ดํ„ฐ ์‚ญ์ œ")
            childDidFinish(homeViewController.coordinator)
        }
        if let loginViewController = fromViewController as? LoginViewController {
            print("LoginVC์˜ ์ฝ”๋””๋„ค์ดํ„ฐ ์‚ญ์ œ")
            childDidFinish(loginViewController.coordinator)
        }
        print("viewControllers : \(navigationController.viewControllers)")
        print("childCoordinators : \(childCoordinators)")
    }

    func childDidFinish(_ child: Coordinator?) {
        for (index, coordinator) in childCoordinators.enumerated() {
            if coordinator === child {
                childCoordinators.remove(at: index)
                print("\(child) ์‚ญ์ œํ•จ")
                break
            }
        }
    }

func navigationController - didShow()

  • ์ด์ „์— ์–ด๋–ค ๋ทฐ์ปจ์œผ๋กœ๋ถ€ํ„ฐ ์ด๋™ํ•˜๋Š”์ง€ ์ฝ์–ด๋‚ธ๋‹ค
  • ์ด์ „ ๋ทฐ์ปจ์ด ์•„์ง navigationStack์— ์กด์žฌํ•œ๋‹ค๋ฉด : pop์ด ์•„๋‹Œ push๋™์ž‘์œผ๋กœ ์ธ์‹๋œ ๊ฒƒ์ž„
    • ์•„๋ฌด ์กฐ์น˜๋„ ํ•  ํ•„์š” ์—†๋‹ค (return)
  • ์ด์ „ ๋ทฐ์ปจ์ด navigationStack์— ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด : pop๋œ ๊ฒƒ์ด ๋งž์Œ
    • pop๋œ ๋ทฐ์ปจ์˜ coordinator๋ฅผ ์ฝ์–ด์™€์„œ childCoordinators์—์„œ ์‚ญ์ œํ•ด ์ค˜์•ผ ํ•จ
  • ๋”ฐ๋ผ์„œ push์˜€๋‹ค๋ฉด ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์ง€๋งŒ, pop์ด์—ˆ๋‹ค๋ฉด ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ด ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (์•„๋ž˜ ์ฝ”๋“œ ์ฐธ๊ณ )
  • if let โ€ฆ ์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋Š” ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ ํ•˜๋‚˜๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ๋งˆ๋‹ค ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค.

func childDidFinish(_ child: Coordinator?)

  • AppCoordinator์˜ childCoordinators ๋ชฉ๋ก์—์„œ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์ฐพ์•„ ์‚ญ์ œํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.

4. AppDelegate์— AppCoordinator ์ถ”๊ฐ€ํ•˜๊ธฐ

์•ฑ์ด ์‹œ์ž‘ํ•  ๋•Œ AppCoordinator๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

AppDelegate

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var coordinator: AppCoordinator?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        // ์•ฑ์—์„œ ์‚ฌ์šฉ๋  ๋ฉ”์ธ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ ์ƒ์„ฑ
        let navigationController = UINavigationController()

        // ์•ฑ ์ฝ”๋””๋„ค์ดํ„ฐ์— ๋„ค๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋„ฃ์Œ - ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด
        coordinator = AppCoordinator(navigationController: navigationController)

        // ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์ œ์–ด๊ถŒ์„ ๊ฐ–๋„๋ก ์ง€์‹œ
        coordinator?.start()

        // ๊ธฐ๋ณธ UIWindow๋ฅผ ๋งŒ๋“ค๊ณ  ํ™œ์„ฑํ™”ํ•˜๊ธฐ
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = navigationController
        window?.makeKeyAndVisible()

        return true
    }
}
  • var coordinator: AppCoordinator?
    • ์•ฑ์˜ ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•จ
  • func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
    • MainCoordinator๋ฅผ ๊ตฌ์„ฑ ๋ฐ ์‹คํ–‰
    • ์•ฑ์˜ ๊ธฐ๋ณธ window๋ฅผ ์„ค์ •
    • (์›๋ž˜ ๊ธฐ๋ณธ window๋Š” ์Šคํ† ๋ฆฌ๋ณด๋“œ์— ์˜ํ•ด ์„ค์ •๋˜์ง€๋งŒ, ์ด์ œ๋Š” ์šฐ๋ฆฌ์˜ ์ฑ…์ž„์ด๋ฏ€๋กœ ์šฐ๋ฆฌ๊ฐ€ ์„ค์ •ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค)

5. ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ ๋งŒ๋“ค๊ธฐ

์ฒซ ํ™”๋ฉด์ธ ๋กœ๊ทธ์ธ ํ™”๋ฉด๊ณผ ํ™ˆ ํ™”๋ฉด์„ ๋งŒ๋“ค์–ด ๋ด…๋‹ˆ๋‹ค.

LoginViewController

import Foundation
import UIKit

class LoginViewController: UIViewController {

    weak var coordinator: LoginCoordinator?

    override func viewDidLoad() {
        super.viewDidLoad()
        let loginItem = UIBarButtonItem(title: "๋กœ๊ทธ์ธ", style: .plain, target: self, action: #selector(loginButtonDidTap))
        self.navigationItem.rightBarButtonItem = loginItem
    }

    deinit {
        print("\(type(of: self)) deinit")
    }

    @objc
    func loginButtonDidTap() {
        coordinator?.login()
    }
}

HomeViewController

import Foundation
import UIKit

class HomeViewController: UIViewController {

    weak var coordinator: HomeCoordinator?

    override func viewDidLoad() {
        super.viewDidLoad()
        let item = UIBarButtonItem(title: "๋กœ๊ทธ์•„์›ƒ", style: .plain, target: self, action: #selector(logoutButtonDidTap))
        self.navigationItem.rightBarButtonItem = item
    }

    deinit {
        print("\(type(of: self)) deinit")
    }

    @objc
    func logoutButtonDidTap() {
        coordinator?.logout()
    }
}
  • weak var coordinator: LoginCoordinator?
    • ๋ชจ๋“  ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋“ค์€ ์ž์‹ ์˜ ์ฝ”๋””๋„ค์ดํ„ฐ์™€ ๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ๋”ฐ๋ผ์„œ ์•ฝํ•œ์ฐธ์กฐ๋กœ ์ž์‹ ์˜ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์†Œ์œ ํ•ฉ๋‹ˆ๋‹ค.
    • โœ… ์•ฑ์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋ฉด ์ฝ”๋””๋„ค์ดํ„ฐ ๋Œ€์‹  ๊ฐœ๋ณ„ ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - ๋งํฌ ์ฐธ์กฐ

6. ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ด€๋ฆฌํ•  ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ ๋งŒ๋“ค๊ธฐ

  • ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ์—์„œ ํ™”๋ฉด์„ ์ „ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” start() ๊ฐ€ ์•„๋‹ˆ์–ด๋„ ๋ฉ๋‹ˆ๋‹ค

LoginCoordiniator

import Foundation
import UIKit

class LoginCoordinator: Coordinator {

    var childCoordinators: [Coordinator] = []
    var navigationController: UINavigationController
    weak var parentCoordinator: AppCoordinator?

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() { // VC ์„ค์ •์„ ์‹œ์ž‘ํ•˜๋Š” ์—ญํ• 
        // loginVC ์…‹ํŒ…
        let viewController = LoginViewController()
        viewController.coordinator = self
        viewController.view.backgroundColor = .systemBrown
        viewController.title = "Login"
        navigationController.pushViewController(viewController, animated: true)
        print("LoginViewController๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค")
    }

    func login() {
        parentCoordinator?.didLoggedIn()
    }

}

HomeCoordinator

import Foundation
import UIKit

class HomeCoordinator: Coordinator {

    var childCoordinators: [Coordinator] = []
    var navigationController: UINavigationController
    weak var parentCoordinator: AppCoordinator?

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() {
        // homeVC ์…‹ํŒ…
        let viewController = HomeViewController()
        viewController.coordinator = self
        viewController.view.backgroundColor = .systemPurple
        viewController.title = "Home"
        navigationController.pushViewController(viewController, animated: true)
        print("HomeViewController๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค")
    }

    func logout() {
        parentCoordinator?.didLoggedOut()
    }
}

์˜ˆ์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ์™„์„ฑํ•œ ์ƒํƒœ์—์„œ ํŒŒ์ผ๋ณ„ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์„ค๋ช…ํ•˜๋‹ค ๋ณด๋‹ˆ ์ˆœ์„œ๋‚˜ ๋™์ž‘ ๋ฐฉ์‹์ด ์ดํ•ด๊ฐ€ ์–ด๋ ค์šฐ์‹ค ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค(ใ… ใ… )

๊นƒํ—™ ์ €์žฅ์†Œ main ๋ธŒ๋žœ์น˜์— ์ „์ฒด ์ฝ”๋“œ๊ฐ€ ์žˆ์œผ๋‹ˆ ์‹คํ–‰ํ•ด๋ณด๋ฉฐ ์„ค๋ช…์„ ์ฝ์œผ์‹œ๋Š” ๊ฑธ ๊ฐ•๋ ฅํžˆ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

(๋ฒˆ์™ธ) delegate๋กœ ๋ถ€๋ชจ์ฝ”๋””๋„ค์ดํ„ฐ-์ž์‹์ฝ”๋””๋„ค์ดํ„ฐ-๋ทฐ์ปจํŠธ๋กค๋Ÿฌ ์†Œํ†ตํ•˜๊ธฐ

์ง€๊ธˆ๊นŒ์ง€ ์„ค๋ช…ํ•œ ๋ฐฉ์‹์€ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ํ”„๋กœํผํ‹ฐ๋กœ ์†Œ์œ ํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

parentCoordinator ํ”„๋กœํผํ‹ฐ๋ฅผ ํ†ตํ•ด ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ-์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ - ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋Œ€ํ™”ํ•˜๋Š” ๋ฒ„์ „.

์ด ๋ฐฉ์‹๊ณผ๋Š” ๋‹ฌ๋ฆฌ delegate ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ-์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ - ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋Œ€ํ™”ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ๋„ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๊ฐ™์€ ๊นƒํ—™ ์ €์žฅ์†Œ์˜ delegate ๋ธŒ๋žœ์น˜ ์— delegate ํ”„๋กœํ† ์ฝœ๋กœ ๋™์ž‘ํ•˜๋Š” ๋ฒ„์ „๋„ ์˜ฌ๋ ค ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ์ธ์ ์œผ๋กœ ์ €๋Š” ์ด ๋ฐฉ์‹๋ณด๋‹ค ํ”„๋กœํผํ‹ฐ๋ฅผ ์†Œ์œ ํ•˜๋Š” ๊ฒŒ ํ๋ฆ„์ด ๋œ ๋ณต์žกํ•˜๊ณ , ํ›จ์”ฌ ์ง๊ด€์ ์œผ๋กœ ๋™์ž‘ํ•ด์„œ ์ข‹์•˜์Šต๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์–ธ์  ๊ฐ€ ์ •๋ฆฌํ•ด์•ผ์ง€ ์ •๋ฆฌํ•ด์•ผ์ง€.. ํ–ˆ๋˜ ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ๋“œ๋””์–ด ์ •๋ฆฌํ–ˆ๋„ค์š”.

์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ๋ฐฐ์šฐ๋Š” ๋ถ„๋“ค๊ป˜ ์ด ๊ธ€์ด ์กฐ๊ธˆ์ด๋ผ๋„ ๋„์›€์ด ๋˜์…จ์œผ๋ฉด ํ•ฉ๋‹ˆ๋‹ค! ๐Ÿ™