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

[iOS] Advanced coordinators in iOS ํ•ด์„ ๋ฐ ์ •๋ฆฌ ๋ณธ๋ฌธ

๐Ÿ“ฑ๐ŸŽ iOS

[iOS] Advanced coordinators in iOS ํ•ด์„ ๋ฐ ์ •๋ฆฌ

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

Advanced coordinators in iOS

iOS ์•ฑ์—์„œ ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ์ ์šฉํ•  ๋•Œ ๋งˆ์ฃผ์น˜๋Š” 6๊ฐ€์ง€ ๋ฌธ์ œ๋“ค์— ๋Œ€ํ•œ ํ•ด๋‹ต์„ ์ฐพ๋Š”๋‹ค.

1. child coordinators๋ฅผ ์–ธ์ œ, ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๊ฐ€?

์•ฑ์ด ์ปค์ง€๋ฉด ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ์™€ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๋‘˜ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

  • ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋Š” ๊ฐ๊ฐ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ(์ฑ…์ž„)์„ ๋งก๋Š”๋‹ค
  • ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ ํ•˜๋‚˜์˜ ํ๋ฆ„์ด ๋๋‚ฌ์„ ๋•Œ, ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ์—๊ฒŒ ์ด๋ฅผ ์•Œ๋ฆฌ๊ณ , ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ํ๋ฆ„์„ ์ด์–ด์ค€๋‹ค.
  • ๋ณต์žกํ•œ ์•ฑ์˜ ๊ธฐ๋Šฅ์„ ์ž‘๊ฒŒ ๋‚˜๋ˆ„์–ด ์žฌ์‚ฌ์šฉ์ด ์‰ฝ๊ฒŒ ํ•จ

child coordinator ์˜ˆ์‹œ

class BuyCoordinator: Coordinator {
    var childCoordinators = [Coordinator]()
    var navigationController: UINavigationController

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

    func start() {
        // we'll add code here
                let vc = BuyViewController.instantiate()
            vc.coordinator = self
            navigationController.pushViewController(vc, animated: true)
    }
}
  • ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์—†์—ˆ๋Š”๋ฐ ์ƒˆ๋กœ ์ƒ์„ฑํ•œ ๊ฒƒ์ด๋ฏ€๋กœ, start()๋ฉ”์„œ๋“œ ๋‚ด์šฉ์„ MainCoordinator์˜ ํ•ด๋‹น ๋ทฐ์ปจ ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์™€์„œ ์ฑ„์šด๋‹ค.

  • ๊ทธ๋‹ค์Œ MainCoordinator์—์„œ ๋ทฐ์ปจ์„ ์ง์ ‘ ๋งŒ๋“ค๋˜ ๋ถ€๋ถ„์„ BuyCoordinator๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ฐ”๊พผ๋‹ค.

      let child = BuyCoordinator(navigationController: navigationController)
      childCoordinators.append(child)
      child.start()
      child.parentCoordinator = self
  • ๋˜ํ•œ ๋ทฐ์ปจ์˜ coordinator์˜ ํƒ€์ž…์„ MainCoordinator ์—์„œ BuyCoordinator๋กœ ๋ฐ”๊พผ๋‹ค.

  • ์ด์ œ MainCoordinator ์™€ BuyCoordinator ๋ฅผ ์—ฐ๊ฒฐํ•ด ์ค„ ๊ฒƒ์ด๋‹ค - ํ†ต์‹ ์ด ๋˜๋„๋ก

    • BuyCoordinator์— parentCoordinator๋กœ MainCoordinator ๋ฅผ ์—ฐ๊ฒฐํ•ด ์ค€๋‹ค
    • weak var parentCoordinator: MainCoordinator? - MainCoordinator๊ฐ€ ์ด๋ฏธ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๋ฏ€๋กœ weak๋กœ ์„ ์–ธ
    • child.parentCoordinator = self ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค€๋‹ค.
  • ๊ทธ๋‹ค์Œ, BuyViewController์˜ ์ž‘์—…์ด ๋๋‚˜๋ฉด ๋ทฐ์ปจ์—์„œ ์ฝ”๋””๋„ค์ดํ„ฐ๋กœ ์ด๋ฅผ ์•Œ๋ฆฌ๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

    • ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•(2.์—์„œ ๊ฐœ์„ ํ•  ๊ฒƒ์ž„) ์ด ์žˆ์ง€๋งŒ ์—ฌ๊ธฐ์„œ๋Š” viewDidDisappear() ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

      override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        coordinator?.didFinishBuying()
      }
    • ์œ„์—์„œ ํ˜ธ์ถœํ•˜๋Š” didFinishBuying() ์„ BuyCoordinator์— ๊ตฌํ˜„ํ•œ๋‹ค.

      • ์ฒ˜๋ฆฌ ๋ฐฉ์‹์€ ์•ฑ ํ๋ฆ„์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๊ฒ ์ง€๋งŒ ์—ฌ๊ธฐ์„œ๋Š” ํŠน๋ณ„ํ•œ ๋™์ž‘์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๋กœ ๊ฐ€์ •ํ•˜๊ณ  ๊ตฌํ˜„ํ•œ๋‹ค.

        func didFinishBuying() {
          parentCoordinator?.childDidFinish(self)
        }
    • ์œ„์—์„œ ํ˜ธ์ถœํ•˜๋Š” childDidFinish()๋ฅผ MainCoordinator์— ๊ตฌํ˜„ํ•œ๋‹ค.

      • === ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค - ๋ฐฐ์—ด ๋‚ด์— ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด. ํด๋ž˜์Šค์—๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

        func childDidFinish(_ child: Coordinator?) {
          for (index, coordinator) in childCoordinators.enumerated() {
              if coordinator === child {
                  childCoordinators.remove(at: index)
                  break
              }
          }
        }
    • ํด๋ž˜์Šค์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•จ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด Coordinator ํ”„๋กœํ† ์ฝœ์ด AnyObject๋ฅผ ์ค€์ˆ˜ํ•˜๋„๋ก ํ•˜๋ฉด ์ข‹๋‹ค.

        protocol Coordinator: AnyObject { ... }

2. navigation controller๋กœ๋ถ€ํ„ฐ ๋’ค๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฐ€?

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

    • ๊ธฐ์กด ์ฝ”๋“œ์—์„œ ๋ทฐ์ปจ์˜ viewDidDisappear() ์™€ ์ฝ”๋””๋„ค์ดํ„ฐ์˜ didFinishBuying() ์„ ์ฃผ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. - ๋” ๊ฐœ์„ ๋œ ๋ฐฉ์‹์œผ๋กœ ๋ฐ”๊ฟ€ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ
  • ๋ฉ”์ธ ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ NSObject๋ฅผ ์ƒ์†ํ•˜๋„๋ก ํ•œ ๋‹ค์Œ, UINavigationControllerDelegate ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•˜๋„๋ก ํ•œ๋‹ค. (NSObject๋ฅผ ์ƒ์†ํ•ด์•ผ๋งŒ UINavigationControllerDelegate ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ)

    • class MainCoordinator: NSObject, Coordinator, UINavigationControllerDelegate
  • ๊ทธ๋‹ค์Œ, ๋ฉ”์ธ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ์˜ delegate๋กœ ์ง€์ •ํ•˜์—ฌ

    • ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ณด์—ฌ์งˆ ๋•Œ๋งˆ๋‹ค ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ฉ”์ธ ์ฝ”๋””๋„ค์ดํ„ฐ์—๊ฒŒ ์•Œ๋ ค ์ฃผ๋„๋ก ํ•˜๋Š” ์ž‘์—….
    • start()์— navigationController.delegate = self ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค
  • ๋’ค๋กœ ์ด๋™ํ•˜๊ธฐ ์œ„ํ•œ ๋™์ž‘์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. - UINavigationControllerDelegate์˜ didShow()๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค

    • MainCoordinator์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ•œ๋‹ค:

    • ์ด์ „์— ์–ด๋–ค ๋ทฐ์ปจ์œผ๋กœ๋ถ€ํ„ฐ ์ด๋™ํ•˜๋Š”์ง€ ์ฝ์–ด๋‚ธ๋‹ค

    • ์ด์ „ ๋ทฐ์ปจ์ด ์•„์ง navigationStack์— ์กด์žฌํ•œ๋‹ค๋ฉด : pop์ด ์•„๋‹Œ push๋™์ž‘์œผ๋กœ ์ธ์‹๋œ ๊ฒƒ์ž„

      • ์•„๋ฌด ์กฐ์น˜๋„ ํ•  ํ•„์š” ์—†๋‹ค (return)
    • ์ด์ „ ๋ทฐ์ปจ์ด navigationStack์— ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด : pop๋œ ๊ฒƒ์ด ๋งž์Œ

      • pop๋œ ๋ทฐ์ปจ์˜ coordinator๋ฅผ ์ฝ์–ด์™€์„œ childCoordinators์—์„œ ์‚ญ์ œํ•ด ์ค˜์•ผ ํ•จ
      // MainCoordinator
      func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        // ์–ด๋–ค ๋ทฐ์ปจ์œผ๋กœ๋ถ€ํ„ฐ ์ด๋™ํ•˜๋Š” ๊ฒƒ์ธ์ง€ ์กฐํšŒ Read the view controller weโ€™re moving from.
        guard let fromViewController = navigationController.transitionCoordinator?.viewController(forKey: .from) else {
            return
        }
      
        // Check whether our view controller array already contains that view controller. 
            // ์šฐ๋ฆฌ์˜ VC ๋ฐฐ์—ด์— ํ•ด๋‹น ๋ทฐ์ปจ์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
        // If it does it means weโ€™re pushing a different view controller on top rather than popping it, so exit.
            // ๋งŒ์•ฝ ์กด์žฌํ•œ๋‹ค๋ฉด ๊ทธ๊ฒƒ์€ pop์ด ์•„๋‹Œ ์ตœ์ƒ์œ„์˜ ๋‹ค๋ฅธ ๋ทฐ์ปจ์„ pushํ•˜๋ ค๋Š” ๊ฒƒ์ž„์„ ์˜๋ฏธํ•˜๋ฏ€๋กœ, ์ข…๋ฃŒํ•จ
        if navigationController.viewControllers.contains(fromViewController) {
            return
        }
      
            // ๋ทฐ์ปจ์„ popํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋ฏ€๋กœ, ๊ทธ๊ฒƒ์ด BuyViewController์ธ์ง€ ํ™•์ธ
        // Weโ€™re still here โ€“ it means weโ€™re popping the view controller, so we can check whether itโ€™s a buy view controller 
        if let buyViewController = fromViewController as? BuyViewController {
            // BuyViewController๊ฐ€ ๋งž๋‹ค๋ฉด ๊ทธ๊ฒƒ์„ ์ง€์šด๋‹ค
            // We're popping a buy view controller; end its coordinator
            childDidFinish(buyViewController.coordinator)
        }
      }

๋’ค๋กœ ๊ฐ€๋Š” ์ž‘์—…์ด ๊นŒ๋‹ค๋กœ์šด ์ด์œ ๋Š” Coordinator์— ์˜ํ•ด ๋ฐœ๋™๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋‹คํ–‰ํžˆ UINavigationControllerDelegate ํ”„๋กœํ† ์ฝœ๋กœ ์ด๋Ÿฌํ•œ ์ด๋ฒคํŠธ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค.

3. view controller๋“ค ์‚ฌ์ด์— ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ์ „๋‹ฌํ•˜๋Š”๊ฐ€?

๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ ์‚ฌ์ด์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ ๋” ์–ด๋ ค์›Œ ๋ณด์ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ์ฝ”๋””๋„ค์ดํ„ฐ ์‚ฌ์šฉ์€ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ ์‚ฌ์ด์— hard link๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋ณด์žฅ๋˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ค.

๋…ธํŠธ : ์ดํ›„ ๊ณผ์ •์„ ๋”ฐ๋ผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์›๋ž˜์˜ Coordinators ํ”„๋กœ์ ํŠธ๋กœ ๋˜๋Œ๋ฆฌ์‹ญ์‹œ์˜ค.

๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์— ์ €์žฅํ•  ๋ฐ์ดํ„ฐ ์„ ์–ธ

// BuyViewController์— ์ถ”๊ฐ€
var selectedProduct = 0

์ด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ทฐ์ปจ์ด ์ƒ์„ฑ๋  ๋•Œ ์ œ๊ณต๋˜์–ด์•ผ ํ•œ๋‹ค๋ฉด, ์ด ๋ทฐ์ปจ์„ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋””๋„ค์ดํ„ฐ์—์„œ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

MainCoordinator์˜ ๋ทฐ์ปจ ์ƒ์„ฑ ๋ฉ”์„œ๋“œ ์ˆ˜์ •

func buySubscription(to productType: Int) {
    let vc = BuyViewController.instantiate()
    vc.selectedProduct = productType // ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์™€์„œ ์ง€์ •
    vc.coordinator = self
    navigationController.pushViewController(vc, animated: true)
}

๋ทฐ์ปจ์—์„œ ์ฝ”๋””๋„ค์ดํ„ฐ๋กœ ์ž‘์—…์„ ๋„˜๊ธธ ๋•Œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ

// BuyViewController์˜ ๋ฉ”์„œ๋“œ
@IBAction func buyTapped(_ sender: Any) {
    coordinator?.buySubscription(to: product.selectedSegmentIndex)
}

์ค‘์š”ํ•œ ์ ์€,

๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋‹ค๋ฅธ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ชฐ๋ผ์•ผ ํ•œ๋‹ค.

๋ทฐ์ปจ์€ ์ฝ”๋””๋„ค์ดํ„ฐ์—๊ฒŒ โ€˜์–ด๋””๋กœ๋“  ๊ฐˆ ์ˆ˜ ์žˆ๋Š”' ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค - ๊ทธ ๋ฐ์ดํ„ฐ๋Š” ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฑฐ๋‚˜, ๋‹ค๋ฅธ ๋ทฐ์ปจ์„ ๋ณด์—ฌ์ฃผ๊ฑฐ๋‚˜, ๋˜๋Š” ๋‹ค๋ฅธ ์–ด๋–ค ์ž‘์—…์„ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค.

์ฝ”๋””๋„ค์ดํ„ฐ๋Š” ๋ชฉ์ ์ง€๋ฅผ ํŒŒ์•…ํ•˜๊ฒŒ ํ•ด ์ค€๋‹ค - ๋ฐ›๊ณ  ์žˆ๋Š” ๊ฐ’(๋งค๊ฐœ๋ณ€์ˆ˜)์ด ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š”์ง€๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.

4. coordinator์— tab bar controller๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

์•ฑ์˜ ๊ธฐ๋Šฅ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋‚˜๋ˆ„์–ด ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ tab bar controller๋ฅผ ์ผ๋ฐ˜์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ์ฝ”๋””๋„ค์ดํ„ฐ๋Š” tab bar controller์—๋„ ์ž˜ ๋™์ž‘ํ•œ๋‹ค - ์ฝ”๋””๋„ค์ดํ„ฐ๋Š” ๊ทธ๊ฒƒ๋“ค์„ ํ•จ๊ป˜ ์ž˜ ๋ฌถ์–ด๋‘”๋‹ค(cinch).

์•ฑ ๋‚ด๋ถ€์˜ ๊ฐ ํƒญ๋“ค์ด ๊ฐ ์ฝ”๋””๋„ค์ดํ„ฐ์— ์˜ํ•ด ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๊ฐ ํƒญ๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ์˜ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๊ฐ€์ง„๋‹ค.

์•„๋ž˜๋Š” tab bar controller๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ช‡๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด๋‹ค.

MainTabBarController๋ฅผ ๋งŒ๋“ ๋‹ค

UITabBarController๋ฅผ ์ƒ์†ํ•˜๋Š” MainTabBarController๋ฅผ ๋งŒ๋“ ๋‹ค.

์ด ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๊ฐ ํƒญ์— ๋Œ€ํ•œ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ํ”„๋กœํผํ‹ฐ๋กœ ๊ฐ–๋Š”๋‹ค.

  • ํ•˜๋‚˜์˜ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ํ”„๋กœํผํ‹ฐ๋กœ ๋„ฃ์–ด๋ณด์ž.
  • viewDidLoad()์—์„œ ๊ฐ ์ฝ”๋””๋„ค์ดํ„ฐ์˜ start()๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค
    • ๊ฐ ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ๊ธฐ๋ณธ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ค€๋น„ํ•˜๋„๋ก ํ•œ๋‹ค
  • viewControllers ํ”„๋กœํผํ‹ฐ๋ฅผ ํƒญ๋ฐ”์ปจํŠธ๋กค๋Ÿฌ ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•œ๋‹ค.
    • ์ฝ”๋””๋„ค์ดํ„ฐ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ด์šฉํ•œ๋‹ค
// MainTabBarController์— ์ถ”๊ฐ€
let mainCoordinator = MainCoordinator(navigationController: UINavigationController())

override func viewDidLoad() {
    main.start()
    viewControllers = [main.navigationController]
}

๊ฐ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์— tab bar item ์ถ”๊ฐ€

์ถ”๊ฐ€ํ•˜์ง€ ์•Š์œผ๋ฉด ํƒญ๋ฐ”์—์„œ ๋ณผ ์ˆ˜๊ฐ€ ์—†๋‹ค.

coordinator์—์„œ start()ํ•  ๋•Œ ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

// MainCoordinator
vc.tabBarItem = UITabBarItem(tabBarSystemItem: .favorites, tag: 0)

AppDelegate์—์„œ rootViewController๋กœ MainTabBarController๋ฅผ ์ง€์ •ํ•œ๋‹ค

//AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    window = UIWindow(frame: UIScreen.main.bounds)
    window?.rootViewController = MainTabBarController()
    window?.makeKeyAndVisible()

    return true
}

ํƒญ๋ฐ”์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋˜‘๋˜‘ํ•œ ๋ฐฉ๋ฒ•์€ ํƒญ ํ•˜๋‚˜๋งˆ๋‹ค ํ•˜๋‚˜์˜ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๋‘๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋Š” ์•ฑ์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„๋“ค์ด ์ ์ ˆํžˆ ๋ถ„๋ฆฌ๋˜๋„๋ก ํ•ด์ค€๋‹ค.

5. segue๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฐ€?

segue๋Š” ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ ์‚ฌ์ด์— ์—ฐ๊ฒฐ์„ ์ƒ์„ฑํ•จ์œผ๋กœ์„œ ์Šคํ† ๋ฆฌ๋ณด๋“œ์— ์ถ”๊ฐ€๋œ๋‹ค.

segue๋Š” iOS์— ์˜ํ•ด ์ž๋™์œผ๋กœ, ๋˜๋Š” prepare(for segue:) ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์„œ ๋ฐœ๋™๋œ๋‹ค.

๋ฌธ์ œ๋Š” segue๊ฐ€ ์ฝ”๋””๋„ค์ดํ„ฐ์˜ ์ฃผ์š” ์žฅ์  ์ค‘ ํ•˜๋‚˜๋ฅผ ํ—›๋˜๊ฒŒ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค: segue๋Š” ๊ฐ•์ œ๋กœ ์ง€์ •๋œ ์•ฑ์˜ ํ๋ฆ„์„ ๋งŒ๋“ ๋‹ค. ์ด๋Š” ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์žฌ๋ฐฐ์น˜ํ•˜๋Š” ๊ฒƒ์„ ๋ง‰๋Š”๋‹ค.

๊ฒฐ๋ก  : ๊ฐ€๋Šฅํ•˜๋ฉด segue์‚ฌ์šฉ์„ ์ง€์–‘ํ•œ๋‹ค(์ ์–ด๋„ ๊ธ€์“ด์ด๋Š” ์“ฐ์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•จ).

6. ์–ด๋–ป๊ฒŒ ํ”„๋กœํ† ์ฝœ์ด๋‚˜ ํด๋กœ์ €๋ฅผ (์ฝ”๋””๋„ค์ดํ„ฐ) ๋Œ€์‹  ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

(์ฝ”๋””๋„ค์ดํ„ฐ ํƒ€์ž… ๋Œ€์‹  ํ”„๋กœํ† ์ฝœ์ด๋‚˜ ํด๋กœ์ € ์‚ฌ์šฉํ•˜๊ธฐ)

์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ˜ผ๋ž€์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์€ ์ฝ”๋””๋„ค์ดํ„ฐ์™€ delegate์˜ ์ฐจ์ด์ ์ด๋‹ค. ์‚ฌ์‹ค์€ ์ฝ”๋””๋„ค์ดํ„ฐ์™€ delegate๋Š” ๋‹ค๋ฅด์ง€ ์•Š๋‹ค - ์ฝ”๋””๋„ค์ดํ„ฐ๋Š” ๊ทธ์ € ํšจ๊ณผ์ ์œผ๋กœ ์ „๋ฌธํ™”๋œ delegate์ด๋‹ค. ์‚ฌ์‹ค, ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” coordinator ํ•˜๋‚˜์˜ ์ด๋ฆ„์„ delegate๋กœ ๋ฐ”๊พธ๋ฉด ๊ทธ๋Œ€๋กœ ์ž‘๋™ํ•œ๋‹ค.

coordinator๋ผ๋Š” ์ด๋ฆ„์„ ์“ฐ๋Š” ๊ฒƒ์˜ ์žฅ์ ์€, ๊ทธ๊ฒƒ์ด ์•ฑ ์ „๋ฐ˜์˜ ๋‚ด๋น„๊ฒŒ์ด์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ „๋ฌธํ™”๋œ delegate์ž„์„ ์˜๋ฏธํ•จ์ด ๋ช…ํ™•ํ•˜๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋‹น์‹ ์€ ํ•˜๋‚˜์˜ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์— ๋ช‡๋ช‡์˜ ์„œ๋กœ ๋‹ค๋ฅธ delegate๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ , ์•ฑ ๋‚ด๋ถ€์—์„œ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๊ฒ ์ง€๋งŒ, ๋‹น์‹ ์ด ๊ทธ ์ด๋ฆ„์„ ๊ทธ์ € delegate๋กœ ์ง“๋Š”๋‹ค๋ฉด ํ˜ผ๋ž€์ด ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ์ด๊ฒƒ์„ coordinator๋ผ๊ณ  ์ด๋ฆ„์ง€์Œ์œผ๋กœ์จ ์šฐ๋ฆฌ๋Š” ์—ญํ• ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค: ์ด๊ฒƒ์ด ์•ฑ์„ ๋ฐœ์ „์‹œํ‚ค๋Š” ์š”์†Œ์ด๋‹ค.

์ฝ”๋””๋„ค์ดํ„ฐ์˜ ์ด๋ฆ„์„ delegate๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๋Œ€์‹  ๋‘๊ฐ€์ง€ ๋Œ€์•ˆ์ด ์žˆ๋‹ค.

  • ํ”„๋กœํ† ์ฝœ ์‚ฌ์šฉํ•˜๊ธฐ
  • ํด๋กœ์ € ์‚ฌ์šฉํ•˜๊ธฐ

ํ”„๋กœํ† ์ฝœ ์‚ฌ์šฉํ•˜๊ธฐ

  • ํฐ ์•ฑ์ผ ๋•Œ ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„์„ ์ž์œ ๋กญ๊ฒŒ ๋‹ค๋ฅด๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ

๋จผ์ € ๋ฉ”์ธ ์ฝ”๋””๋„ค์ดํ„ฐ ๋‚ด์˜ VC ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๋ฅผ ๋ณด๊ณ  ๊ณตํ†ต ํ”„๋กœํ† ์ฝœ์„ ๋งŒ๋“ ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” buySubscription() ๊ณผ createAccount() ๊ฐ€ ์กด์žฌํ•˜๋ฏ€๋กœ, ์ด๊ฒƒ๋“ค์„ ์ƒˆ ํ”„๋กœํ† ์ฝœ๋กœ ๋ฌถ์„ ์ˆ˜ ์žˆ๋‹ค.

// Buying
protocol Buying: AnyObject {
    func buySubscription()
}
// AccountCreating
protocol AccountCreating: AnyObject {
    func createAccount()
}

์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์œ„์˜ ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋„๋ก ํ•œ๋‹ค

// MainCoordinator
class MainCoordinator: Coordinator, Buying, AccountCreating

ViewController๊ฐ€ ๊ตฌ์ฒด ํƒ€์ž… ๋Œ€์‹  ํ”„๋กœํ† ์ฝœ์„ ์ฐธ์กฐํ•˜๋„๋ก ํ•œ๋‹ค

// ViewController
weak var coordinator: (Buying & AccountCreating)?

์ด์ œ ๋‘ ํ”„๋กœํ† ์ฝœ์ด ๊ตฌํ˜„๋˜๋Š” ํ•œ, ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€๋Š” ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†๋‹ค - ๊ทธ๊ฒƒ์€ ์ฝ”๋””๋„ค์ดํ„ฐ์ผ์ˆ˜๋„ ์žˆ๊ณ , ์•„๋‹ ์ˆ˜๋„ ์žˆ๋‹ค.

ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋ฉดโ€ฆ

ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ํฐ ์•ฑ์„ ์ž‘์—…ํ•˜๋ฉด์„œ ๋” ๋งŽ์€ ์œ ์—ฐ์„ฑ์„ ํ•„์š”๋กœ ํ•  ๋•Œ ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค.

  • ์›ํ•  ๋•Œ ์ž์œ ๋กญ๊ฒŒ ์ƒˆ๋กœ์šด ํ”„๋กœํ† ์ฝœ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค
  • A/B ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ๋‹ค๋ฅธ ์ฝ”๋””๋„ค์ดํ„ฐ๋กœ ์ „ํ™˜ํ•˜๊ธฐ ์‰ฝ๋‹ค
  • ํ›จ์”ฌ ๋” ์œ ์—ฐํ•˜๋‹ค - ํ”„๋กœํ† ์ฝœ์€ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ํŠน์ • ํƒ€์ž…์ด ์•„๋‹Œ ์›ํ•˜๋Š” ๋™์ž‘์„ ๊ธฐ์ˆ ํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ

ํด๋กœ์ € ์‚ฌ์šฉํ•˜๊ธฐ

์ฝ”๋””๋„ค์ดํ„ฐ ๊ตฌ์ฒดํƒ€์ž…๊ณผ ํ”„๋กœํ† ์ฝœ์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ๋Œ€์‹  ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.

๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์— ์˜ํ•ด ๋ฐœ๋™๋˜๋Š” ์•ก์…˜์ด 1~2๊ฐœ๋ฟ์ผ ๋•Œ ์œ ์šฉํ•˜๋‹ค.

ViewController์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ”„๋กœํผํ‹ฐ๋กœ ์ •์˜ํ•œ๋‹ค

// ViewController
var buyAction: (() -> Void)?
var createAccountAction: (() -> Void)?

์‚ฌ์šฉํ•  ๋•Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ํ˜ธ์ถœํ•œ๋‹ค

// ViewController
@IBAction func buyTapped(_ sender: Any) {
    buyAction?()
}

@IBAction func createAccountTapped(_ sender: Any) {
    createAccountAction?()
}

์ฝ”๋””๋„ค์ดํ„ฐ์—์„œ ํ•ด๋‹น ํด๋กœ์ €๋“ค์„ ์ •์˜ํ•ด ์ค€๋‹ค

์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ๋ทฐ์ปจ์„ ์ƒ์„ฑํ•  ๋•Œ ํ•ด๋‹น ํด๋กœ์ €๋“ค์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ง€ ์ •์˜ํ•ด ์ค€๋‹ค.

  • ๊ธฐ์กด coordinator ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ญ์ œํ•˜๊ณ 
  • buyAction๊ณผ createAccountAction ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ค์ •ํ•œ๋‹ค
// MainCoordinator
vc.buyAction = { [weak self] in
    self?.buySubscription()
}

vc.createAccountAction = { [weak self] in
    self?.createAccount()
}

์ด๋ ‡๊ฒŒ ๋งŒ๋“ค๋ฉด ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ๋‚ด๋น„๊ฒŒ์ด์…˜์„ ์ œ์–ดํ•œ๋‹ค๋Š” ๊ฒƒ๋„ ๋ชจ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด..

  • ์ฝ”๋””๋„ค์ดํ„ฐ์—๊ฒŒ ๋‹จ์ง€ 1๊ฐœ ๋˜๋Š” 2๊ฐœ์˜ ์ฝœ๋ฐฑ๋งŒ์„ ๋ณด๋‚ผ ๋•Œ ์ข‹๋‹ค
  • ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์™„์ „ํžˆ ๋…๋ฆฝ์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค - ์ฝ”๋””๋„ค์ดํ„ฐ์˜ ์กด์žฌ์กฐ์ฐจ ๋ชจ๋ฅด๊ฒŒ
  • ํ•˜์ง€๋งŒ ๊ทœ๋ชจ๋ฅผ ํ‚ค์šฐ๊ธฐ์—๋Š” ์ข‹์ง€ ์•Š๋‹ค - 3๊ฐœ ์ด์ƒ์˜ ํด๋กœ์ € ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋‹ค ๋ณด๋ฉด ํ”„๋กœํ† ์ฝœ๋กœ ๋ฐ”๊พธ๊ณ  ์‹ถ์–ด์งˆ ๊ฒƒ.

https://khanlou.com/tag/advanced-coordinators/
์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ๋งŒ๋“  Khanlou์˜ ๋ธ”๋กœ๊ทธ ๊ธ€์„ ์ฝ์–ด๋ณด๊ธฐ๋ฅผ ์ถ”์ฒœ.