Bibi's DevLog ๐ค๐
[iOS] How to use the coordinator pattern in iOS apps ํด์ ๋ฐ ์ ๋ฆฌ ๋ณธ๋ฌธ
[iOS] How to use the coordinator pattern in iOS apps ํด์ ๋ฐ ์ ๋ฆฌ
๋น๋น bibi 2022. 11. 25. 01:06How to use the coordinator pattern in iOS apps
VC๊ฐ ์ฝ๋๋ค์ดํฐ๋ฅผ ์์ ํ๋ ๋ฐฉ์์ผ๋ก ์กฐ๊ธ ๋ ๊ฐ๋จํ๋ค.
์์ด ํด์๊ณผ ์ฝ๋ ์์ ์์ฃผ๋ก ์ ๋ฆฌ.
iOS์ฑ์์ ์ฝ๋๋ค์ดํฐ ํจํด์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
view controller์ navigation controller๋ฅผ ๋จ์ํํ๊ธฐ.
์ฌ๊ธฐ์๋ child coordinator ์ฌ์ฉ ์ง์ ๊น์ง ๋ฐฐ์ด๋ค.
child coordinator ์ฌ์ฉ๋ฒ์ ๋ค์ ๋ฌธ์์์ ๋ฐฐ์ด๋ค.
iOS ์ฑ์์ ์ฝ๋๋ค์ดํฐ ํจํด์ ์ฌ์ฉํ๋ฉด…
- View Controller์์ ์ฑ ํ์(app navigation) ์์ ์ ์ ๊ฑฐํ ์ ์๋ค.
- ์ฐ๋ฆฌ๊ฐ ํ์ํ ๋ ์ฑ์ ํ๋ฆ์ ์กฐ์ ํ ์ ์๋ค.
- ViewController๋ค์ ๊ด๋ฆฌํ๊ณ ์ฌ์ฌ์ฉํ๊ธฐ ๋ ์ฌ์์ง๋ค.
- massive view controller๋ฅผ ๋ง๋ ๋ฐฉ๋ฒ ์ค ํ๋์ด๋ค.
๋ทฐ ์ปจํธ๋กค๋ฌ๋ ์ฑ์์ ๋ ๋ฆฝ์ ์ผ ๋ ๊ฐ์ฅ ์ ์๋ํ๋ค. ๋ทฐ ์ปจํธ๋กค๋ฌ๋ ์ฑ์ ํ๋ก์ฐ ์์์ ์์ ์ ์์น๋ฅผ ์์ํ์ง ์๊ณ , ๊ทธ๋ฆฌ๊ณ ์ฒ์๋ถํฐ ์์ ์ด ํ๋ฆ์ ์ผ๋ถ๋ผ๋ ๊ฒ์กฐ์ฐจ ๋ชจ๋ฅผ ์ ๋๋ก ๋ ๋ฆฝ์ ์ธ ๊ฒ์ด ์ข๋ค.
์ด๊ฒ์ ์ฝ๋์ ํ ์คํธ์ ์ถ๋ก ์ ์ฝ๊ฒ ํด์ค ๋ฟ ์๋๋ผ, ๋น์ ์ ์ฑ ๋ด๋ถ์์ ๋ทฐ์ปจํธ๋กค๋ฌ์ ์ฌ์ฌ์ฉ์ ๋์ฑ ์ฝ๊ฒ ํด ์ค๋๋ค.
๋ฌด์์ ๋ฐ๊ฟ์ผ ํ๋๊ฐ?
iOS ๊ฐ๋ฐ์๋ผ๋ฉด ๋ฐฑ๋ฒ๋ ๋๊ฒ ์์ฑํ์ ์ฝ๋๋ฅผ ๋จผ์ ์ดํด๋ณด์.
if let vc = storyboard?.instantiateViewController(withIdentifier: "SomeVC") {
navigationController?.pushViewController(vc, animated: true)
}
์ด๋ฐ ์ฝ๋์์, ํ๋์ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ ๋ฐ๋์ ๋ค๋ฅธ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ฅผ ์์ฑํ๊ณ , ๊ตฌ์ฑํ๊ณ , ํ์ํด์ผ ํ๋ค.
์ด๋ ๋น์ ์ ์ฑ์์ ๊ฐํ ๊ฒฐํฉ์ ํ์ฑํ๋ค: ๋น์ ์ ๋ทฐ ์ปจํธ๋กค๋ฌ ๊ฐ์ ์ฐ๊ฒฐ์ ํ๋์ฝ๋ฉํ๊ณ , ์ฌ์ง์ด ๊ฐ์ ๋ทฐ ์ปจํธ๋กค๋ฌ๊ฐ ์ฌ๋ฌ ์ฅ์์์ ๋ณด์ฌ์ ธ์ผ ํ ๋ ์ด๋ฌํ ๊ตฌ์ฑ ์ฝ๋๋ฅผ ๋ณต์ฌํ์์ง๋ ๋ชจ๋ฅธ๋ค.
๋น์ ์ด ์์ดํจ๋ ์ฌ์ฉ์, VoiceOver ์ฌ์ฉ์, A/Bํ ์คํธ์ ์ฌ์ฉ์ ์ผ๋ถ๋ฅผ ์ํ ๋ค๋ฅธ ๋์์ ์ํ๋ค๋ฉด ์ด๋ป๊ฒ ๋๊ฒ ๋๊ฐ? ๋น์ ์ด ํ ์ ์๋ ์ ์ผํ ์ ํ์ ๋ทฐ ์ปจํธ๋กค๋ฌ์ ๋ ๋ง์ ๊ตฌ์ฑ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ ๋ฟ์ด๋ฉฐ, ๋ฌธ์ ๋ ๋ ์ฌ๊ฐํด์ง ๋ฟ์ด๋ค.
๋ ์ฌ๊ฐํ ๊ฒ์, ์ด ๋ชจ๋ ๊ฒ์ด ‘์์ ์ ๋ด๋น๊ฒ์ด์ ์ปจํธ๋กค๋ฌ์๊ฒ ๋ฌด์์ ํ๋ผ๊ณ ์ง์ํ๋ ์์’์ ํฌํจํ๋ค๋ ๊ฒ์ด๋ค - ์ฐ๋ฆฌ์ ์ฒซ ๋ฒ์งธ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ ์์ ์ ๋ถ๋ชจ์๊ฒ๊น์ง ์ ๊ทผํด, ๋ถ๋ชจ์๊ฒ ๋ ๋ฒ์งธ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ฅผ ํ์ํ๋๋ก ์ง์ํ๋ค.
์ด๋ฐ ๋ฌธ์ ๋ฅผ ๊น๋ํ๊ฒ ํด๊ฒฐํ๊ธฐ ์ํด์, ์ฝ๋๋ค์ดํฐ ํจํด์ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ค์ ์ฐ๊ฒฐ์ ๋์ด์ ๊ฐ๊ฐ์ ๋ทฐ ์ปจํธ๋กค๋ฌ๊ฐ ์์ ์ ์ ํ์ ๋ฌด์์ด ์๋์ง, ๊ทธ๋ฆฌ๊ณ ๋ทฐ ์ปจํธ๋กค๋ฌ์ ํ๋ฆ์ด ์๋์ง์กฐ์ฐจ ๋ชจ๋ฅด๊ฒ ๋ง๋ค์ด ์ค๋ค.
๊ทธ ๋์ , ๋น์ ์ ์ฑ ํ๋ฆ(flow)์ ์ฝ๋๋ค์ดํฐ๋ฅผ ์ฌ์ฉํด ์ ์ด๋๋ฉฐ, ๋ทฐ๋ ์ฝ๋๋ค์ดํฐ๋ฅผ ํตํด์๋ง ์ํตํ๊ฒ ๋๋ค. ๋ง์ฝ ๋น์ ์ด ์ฌ์ฉ์ ์ธ์ฆ์ ๋ฐ๊ณ ์ถ๋ค๋ฉด, ์ฝ๋๋ค์ดํฐ์๊ฒ ์ธ์ฆ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ํ์ํ๋๋ก ์์ฒญํด์ผ ํ๋ค. ์ฝ๋๋ค์ดํฐ๊ฐ ๊ทธ๊ฒ์ด ๋ฌด์์ ์๋ฏธํ๋์ง ์ดํดํ๊ณ ์ ์ ํ๊ฒ ํ์ํด ์ค ๊ฒ์ด๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ๋น์ ์ ๋น์ ์ด ์ด๋ค ์์๋ก๋ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ค์ ์ฌ์ฉํ ์ ์์์ ์๊ฒ ๋ ๊ฒ์ด๋ค. ํ์์ ๋ฐ๋ผ ๊ทธ๊ฒ๋ค์ ์ฌ์ฉํ๊ฑฐ๋ ์ฌ์ฌ์ฉํ ์ ์๋ค. ๋ ์ด์ ๋น์ ์ ์ฑ์ ํ๋์ฝ๋ฉ๋ ๊ฒฝ๋ก๋ ์๋ค. ๋น์ ์ ์ฑ์ ์๋ก ๋ค๋ฅธ ๋ค์ฏ๊ฐ์ ๋ถ๋ถ์์ ์ฌ์ฉ์ ์ธ์ฆ์ ๋ฐ๋๋ผ๋ ์๊ด์๋ค. ์๋ํ๋ฉด ์์ ์ ์ฝ๋๋ค์ดํฐ์์ ๊ฐ์ ๋ฉ์๋๋ฅผ ํธ์ถํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ ํฐ ์ฑ์ ์ํด์๋, ์์ ์ฝ๋๋ค์ดํฐ child coordinators(๋๋ ์๋ธ์ฝ๋๋ค์ดํฐ subcoordinators)๋ฅผ ์์ฑํ ์๋ ์์ ๊ฒ์ด๋ค - ์ด๊ฒ์ ๋น์ ์ ์ฑ ํ์์ ์ผ๋ถ๋ฅผ ๋ถ๋ฆฌํ ์ ์๊ฒ ํด ์ค๋๋ค. ์๋ฅผ ๋ค์ด, ์ด๋ค ์์ ์ฝ๋๋ค์ดํฐ๋ ๊ณ์ฑ ์์ฑ ํ๋ฆ์ ์ ์ดํ๋ ๋ฐ ์ฌ์ฉํ๊ณ , ๋ค๋ฅธ ๊ฒ์ ์ ํ ๊ตฌ๋ ํ๋ฆ์ ์ ์ดํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ค.
๋ง์ฝ ๋ ๋ง์ ์ ์ฐํจ์ ์ํ๋ค๋ฉด, ๋ทฐ ์ปจํธ๋กค๋ฌ์ ์ฝ๋๋ค์ดํฐ ๊ฐ์ ๋ํ๊ฐ ๊ตฌ์ฒด ํ์ ๋ณด๋ค ํ๋กํ ์ฝ์ ํตํด ์ด๋ฃจ์ด์ง๋๋ก ํ๋ ๊ฒ๋ ์ข์ ์๊ฐ์ด๋ค. ์ด๊ฒ์ ๋์ค์ ๋น์ ์ด ์ฝ๋๋ค์ดํฐ ์ ์ฒด๋ฅผ ๋ค๋ฅธ ํฌ์ธํธ๋ก ๋์ฒดํ ์ ์๋๋ก ํด ์ค ๊ฒ์ด๋ฉฐ, ๋ค๋ฅธ ํ๋ก๊ทธ๋จ ํ๋ฆ์ ๊ฐ๋๋ก ํ๋ค - ์๋ฅผ ๋ค์ด ๋น์ ์ ์์ดํฐ, ์์ดํจ๋, ์ ํTV ๋ฑ์ ๋ํ ๊ฐ๊ฐ์ ์ฝ๋๋ค์ดํฐ๋ฅผ ์ ๊ณตํ ์ ์๋ค.
๋ฐ๋ผ์, ๋น์ ์ด ๊ฑฐ๋ํ ๋ทฐ ์ปจํธ๋กค๋ฌ ๋๋ฌธ์ ๊ณ ๊ตฐ๋ถํฌํ๊ณ ์๋ค๋ฉด, ๋ด๋น๊ฒ์ด์ (ํ์)์ ๋จ์ํํ๋ ๊ฒ์ด ๋์์ด ๋๋ค๋ ๊ฒ์ ์๊ฒ ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค. ์ด์ ์ถ์์ ์ธ ์ด์ผ๊ธฐ๋ ์ถฉ๋ถํ๋ค - ์ค์ ํ๋ก์ ํธ์ ์ฝ๋๋ค์ดํฐ๋ฅผ ์ฌ์ฉํด ๋ณด์.
์ฝ๋๋ค์ดํฐ ํ๋กํ ์ฝ ๋ง๋ค๊ธฐ
Coordinator ํ๋กํ ์ฝ
import UIKit
protocol Coordinator: AnyObject {
var childCoordinators: [Coordinator] { get set }
var navigationController: UINavigationController { get set }
func start()
}
- childCoordinators : ์์ ์ฝ๋๋ค์ดํฐ๋ฅผ ์ ์ฅ
- navigationController : ๋ทฐ์ปจํธ๋กค๋ฌ๋ฅผ ๋ณด์ฌ์ค ๋ ์ฌ์ฉ๋ ๋ด๋น๊ฒ์ด์
์ปจํธ๋กค๋ฌ๋ฅผ ์ ์ฅ.
- ๋ด๋น๊ฒ์ด์ ๋ฐ๋ฅผ ํ์ํ์ง ์๋๋ผ๋, ๋ด๋น๊ฒ์ด์ ์ปจํธ๋กค๋ฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ทฐ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ด๋ค.
- start() : ํด๋น ์ฝ๋๋ค์ดํฐ๊ฐ ์ ์ด๊ถ์ ๊ฐ๋๋ก ํ๋ ๋ฉ์๋. ์์ ํ ๋ง๋ค๊ณ ์ค๋น๋์์ ๋๋ง ์ฝ๋๋ค์ดํฐ๋ฅผ ํ์ฑํํจ.
- AnyObject : ํด๋์ค์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ ํ๋กํ ์ฝ์์ ๋ช ์
์ฝ๋๋ค์ดํฐ ๋ง๋ค๊ณ ์คํํ๊ธฐ
MainCoordinator
์ฑ ์คํ๊ณผ ๋์์ ์ฑ์ ๋ํ ์ ์ด๊ถ์ ๊ฐ๋ ์ฒซ ๋ฒ์งธ ์ฝ๋๋ค์ดํฐ.
์์ ์ฝ๋๋ค์ดํฐ๊น์ง ์๋ ๊ฒฝ์ฐ, AppCoordinator๋ผ๊ณ ์ด๋ฆ์ง๊ธฐ๋ ํ๋ค.
import UIKit
class MainCoordinator: Coordinator {
var childCoordinators = [Coordinator]()
var navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let vc = ViewController.instantiate()
vc.coordinator = self
navigationController.pushViewController(vc, animated: false)
}
}
- ํด๋น ์ฝ๋๋ค์ดํฐ๊ฐ ๋ค๋ฅธ ๋ทฐ์ปจ๋ค์ ์ํด ๊ณต์ ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ struct๋ณด๋ค class๋ก ์ ์ธ
- Coordinator ํ๋กํ ์ฝ์ ์ค์
start()
: ViewController ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ , navigationController์ pushํ๋ค.
โ MainCoordinator๋ ViewController๊ฐ ์๋๋ค.
(UIKit์ ์ํด ์๋์ผ๋ก ํธ์ถ๋๋ viewDidLoad(), viewWillAppear()์ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉฐ, UIViewController์ ๋จ์ ๊ณผ ์ธ์ธ ํ์๊ฐ ์๋ค.)
AppDelegate
์ฑ์ด ์์ํ ๋ ์ ์ฝ๋๋ค์ดํฐ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก AppDelegate์ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค.
// AppDelegate.swift
var coordinator: MainCoordinator? // <- ํด๋น ํ๋กํผํฐ๋ฅผ ์ฑ๋ธ๋ฆฌ๊ฒ์ดํธ์ ์ถ๊ฐ
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// ์ฑ์์ ์ฌ์ฉํ ๋ฉ์ธ ๋ค๋น๊ฒ์ด์
์ปจํธ๋กค๋ฌ๋ฅผ ์์ฑ
let navController = UINavigationController()
// ๋ฉ์ธ ์ฝ๋๋ค์ดํฐ์ ๋ค๋น๊ฒ์ด์
์ปจํธ๋กค๋ฌ๋ฅผ ๋ฃ์ - ๋ทฐ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด
coordinator = MainCoordinator(navigationController: navController)
// ์ฝ๋๋ค์ดํฐ๊ฐ ์ ์ด๊ถ์ ๊ฐ๋๋ก ์ง์
coordinator?.start()
// ๊ธฐ๋ณธ UIWindow๋ฅผ ๋ง๋ค๊ณ ํ์ฑํํ๊ธฐ
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navController
window?.makeKeyAndVisible()
return true
}
var coordinator: MainCoordinator?
- ์ฑ์ ๋ฉ์ธ ์ฝ๋๋ค์ดํฐ๋ฅผ ์ ์ฅํด ๋ฉ๋ชจ๋ฆฌ์์ ํด์ ๋๋ ๊ฒ์ ๋ฐฉ์งํ๋ค.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
- MainCoordinator๋ฅผ ๊ตฌ์ฑ ๋ฐ ์คํ
- ์ฑ์ ๊ธฐ๋ณธ window๋ฅผ ์ค์
- (์๋ ๊ธฐ๋ณธ window๋ ์คํ ๋ฆฌ๋ณด๋์ ์ํด ์ค์ ๋์ง๋ง, ์ด์ ๋ ์ฐ๋ฆฌ์ ์ฑ ์์ด๋ค)
์ฑ์ ํ๋ฆ ์ฒ๋ฆฌํ๊ธฐ
ViewController
- ๋ชจ๋ ๋ทฐ์ปจํธ๋กค๋ฌ๋ค์ ์์ ์ ์ฝ๋๋ค์ดํฐ์ ๋ํํ ์ ์์ด์ผ ํ๋ค.
- ํ๋กํ ์ฝ์ ์ฌ์ฉํ๊ฑฐ๋, (ํฐ ์ฑ์ ๊ฒฝ์ฐ) - Advanced coordinators in iOS ์ฐธ๊ณ
- ์ฝ๋๋ค์ดํฐ๋ฅผ ํ๋กํผํฐ๋ก ์ง์ ์์ ํ ์ ์๋ค (์์ ์ฑ์ ๊ฒฝ์ฐ)
weak var coordinator: MainCoordinator?
Coordinator๋ก ํ๋ฉด ์ ํ
์ฝ๋๋ค์ดํฐ์ ํ๋ฉด์ ์ ํํ ๋ฉ์๋๋ฅผ ์ถ๊ฐํด ๋ทฐ ์ปจํธ๋กค๋ฌ ์ฌ์ด๋ฅผ ์ด๋ํ ์ ์๋ค.
- start()์ ๊ฑฐ์ ๋น์ทํ๊ฒ ์๊ฒผ์ง๋ง, ๋ณด์ฌ์ฃผ๋ ๋ทฐ์ปจํธ๋กค๋ฌ๊ฐ ์กฐ๊ธ์ฉ ๋ค๋ฅด๋ค
- ๋ทฐ์ปจํธ๋กค๋ฌ์ ๋ํ ์ด๋ค ๊ตฌ์ฑ ์์ ์ด ํ์ํ๋ค๋ฉด ํด๋น ๋ฉ์๋์์ ์งํํ๋ฉด ๋๋ค.
// MainCoordinator์ ์ถ๊ฐ
func buySubscription() {
let vc = BuyViewController.instantiate()
vc.coordinator = self
navigationController.pushViewController(vc, animated: true)
}
func createAccount() {
let vc = CreateAccountViewController.instantiate()
vc.coordinator = self
navigationController.pushViewController(vc, animated: true)
}
- ๊ทธ ๋ค์, ๋ทฐ์ปจํธ๋กค๋ฌ์์ ํ๋ฉด์ ์ ํํด์ผ ํ๋ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ coordinator์ ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ํ๋ฉด์ด ์ ํ๋๋ค.
// ํ๋ฉด์ ํ์ด ํ์ํ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ViewController์ ์ถ๊ฐ
@IBAction func buyTapped(_ sender: Any) {
coordinator?.buySubscription()
}
@IBAction func createAccount(_ sender: Any) {
coordinator?.createAccount()
}