iOS/๋””์ž์ธํŒจํ„ด

[๋””์ž์ธ ํŒจํ„ด] Coordinator๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ(feat.MVVM-C)

๊ฒฝํ‘ธ 2023. 9. 15. 15:30
๋ฐ˜์‘ํ˜•

์•ˆ๋…•ํ•˜์„ธ์š”.

์˜ค๋Š˜์€ Coordinator ๋””์ž์ธ ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์†Œ๊ฐœํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ตœ๊ทผ์— ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ ์ค‘์ธ๋ฐ

Coordinator ์‚ฌ์šฉ์— ์žˆ์–ด ๋ญ”๊ฐ€ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐ๋˜์–ด ๋‹ค์‹œ ๊ณต๋ถ€ํ•  ๊ฒธ ํฌ์ŠคํŒ… ๋‚จ๊ฒจ๋ด…๋‹ˆ๋‹ค.

 


 

Coordinator

Coordinator ๋””์ž์ธ ํŒจํ„ด์€ ๊ธฐ์กด์˜ ViewController์—์„œ ์ฒ˜๋ฆฌํ–ˆ๋˜ ํ™”๋ฉด์ „ํ™˜์ด๋‚˜ ํ๋ฆ„์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๋‹ด๋‹นํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

์ฆ‰, ViewController๊ฐ€ ๋‹ค๋ฅธ ViewController๋ฅผ ์ง์ ‘ ํ‘ธ์‹œํ•˜๊ฑฐ๋‚˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋˜ ๊ฒƒ์„ Coordinator๊ฐ€ ๋‹ด๋‹นํ•˜๊ฒŒ ๋˜๋ฉด์„œ ๋ช…ํ™•ํ•œ ์—ญํ• ์˜ ๋ถ„๋ฆฌ๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋˜๊ณ  ์ด๋ ‡๊ฒŒ ๋ช…ํ™•ํ•œ ์—ญํ• ์˜ ๋ถ„๋ฆฌ๋กœ ๋” ๊น”๋”ํ•œ ์ฝ”๋“œ, ํ™•์žฅ์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ์— ์ด์ ์„ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์žฅ๋‹จ์ 

์•ž์„œ ์„ค๋ช…ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๊ธฐ์กด์˜ ViewController๊ฐ€ ํ•˜๋˜ ํ™”๋ฉด์ „ํ™˜์ด๋‚˜ ํ๋ฆ„์ œ์–ด์˜ ์—ญํ• ์„

Coordinator๊ฐ€ ๋‹ด๋‹นํ•˜๊ฒŒ ๋˜๋ฉด์„œ ๋ช…ํ™•ํ•œ ์—ญํ• ์˜ ๋ถ„๋ฆฌ๋ฅผ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ž์ฃผ ๋ด์™”๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ์—ญํ• ์ด ๋ถ„๋ฆฌ๋œ๋‹ค๋ฉด ViewController์™€ Coordinator๋ฅผ ๊ฐœ๋ณ„์ ์ธ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ณ , ํ•˜๋‚˜์˜ Coordinator๊ฐ€ ์—ฌ๋Ÿฌ ViewController์˜ ํ™”๋ฉด์ „ํ™˜์— ์‚ฌ์šฉ์ด ๋  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋งค๋ฒˆ ํ™”๋ฉด์ „ํ™˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด ์žฌ์‚ฌ์šฉ์„ฑ ์ธก๋ฉด์—์„œ๋„ ๊ต‰์žฅํ•œ ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ์ ์ด ์žˆ๋‹ค๋ฉด ์ž‘์€ ์•ฑ์ด๋‚˜ ๊ฐ„๋‹จํ•œ ํƒ์ƒ‰(1ํšŒ์„ฑ, ์ž์ฃผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ™”๋ฉด์ „ํ™˜์ด๋‚˜ ํ๋ฆ„ ์ œ์–ด)์— Coordinator๋ฅผ ๋„์ž…ํ•  ์‹œ์—๋Š” ์˜คํžˆ๋ ค ๋ถˆํ•„์š”ํ•œ ๋ณต์žก์„ฑ๋งŒ ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋น„๊ตํ•ด ๋ณด์ž

์•„๋ž˜๋Š” ๊ธฐ์กด์— ViewController์—์„œ ๋‹ด๋‹นํ•˜๋˜ ํ™”๋ฉด์ „ํ™˜ ๋กœ์ง์ž…๋‹ˆ๋‹ค.

LoginViewController์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ํ•จ์ˆ˜ ๋‚ด์—์„œ ์ƒ์„ฑํ•˜๊ณ  

์ƒ์œ„ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์ธ navigationController์—๊ฒŒ push๋ฅผ ์š”์ฒญํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

class MyViewController: UIViewController {

   ...
   
   func showLgoinViewController() {
        let loginViewController = LoginViewController() 
        self.navigationController?.pushViewController(loginViewController, animated: true)
   }
}

 

Coordinator๋ฅผ ์ด์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

 

1. Coordinator ํ”„๋กœํ† ์ฝœ ๊ตฌํ˜„

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

 

2. ํ•ด๋‹น ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•œ Coordinator

class LoginCoordinator: Coordinator {
    var navigationController: UINavigationController
    var childCoordinators = [Coordinator]()
        
    init(_ navigationController: UINavigationController) {
    	self.navigationController = navigationController
    }
    
    func start() {
        let loginViewController = LoginViewController()
        navigationController.pushViewController(loginViewController, animated: false)
    }
}

- ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๋ฐ›์€ NavigationController๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ™”๋ฉด์ „ํ™˜์ด ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.

 

3. Coordinator ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  start()  ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

๋กœ๊ทธ์ธ ํ™”๋ฉด์ด ๊ฐ€์žฅ ์ฒซ ํ™”๋ฉด์ด๋ผ๋Š” ๊ฐ€์ •ํ•˜์— ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ณดํ†ต AppCoordinator๋ผ๋Š” Coordinator๋ฅผ ๋งŒ๋“ค์–ด ์œ ์ €๊ฐ€ ๊ฐ€์ž…์ด ๋˜์—ˆ๋Š”์ง€ ์•ˆ๋˜์—ˆ๋Š”์ง€์— ๋”ฐ๋ผ ๋กœ๊ทธ์ธํ™”๋ฉด์œผ๋กœ ๊ฐˆ์ง€, ํ™ˆํ™”๋ฉด์œผ๋กœ ๊ฐˆ์ง€ ๋“ฑ์„ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    var coordinator: LoginCoordinator?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let navigationController = UINavigationController()
        coordinator = LoginCoordinator(navigationController)
        coordinator?.start()
        
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = navigationController
        window?.makeKeyAndVisible()
        
        return true
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์•ฑ์ด ์‹คํ–‰๋˜์—ˆ์„ ๋•Œ, LoginCoordinator์˜ start() ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ํ™”๋ฉด์—๋Š” LoginViewController๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

4. ์กฐ๊ธˆ ๋” ์•Œ์•„๋ณด์ž.

๋งŒ์•ฝ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ํ™ˆ์œผ๋กœ ํ™”๋ฉด์ „ํ™˜์„ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์˜ˆ์‹œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ

delegate ํŒจํ„ด์„ ํ™œ์šฉํ•œ ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

protocol LoginViewModelDelegate {
    func showHomeViewController()
}

class LoginViewModel {
	
    weak var delegate: LoginViewModelDelegate?
    
    ...
    
    func showHomeViewController() {
        delegate?.showHomeViewController()
    }
}

class LoginCoordinator: Coordinator {

   ...
   
    func start() {
        let viewModel = LoginViewModel()
        let loginViewController = LoginViewController(viewModel: viewModel)
        viewModel.delegate = self
        navigationController.pushViewController(loginViewController, animated: false)
    }
}

// MARK: - Navigation

extension LoginCoordinator: LoginViewModelDelegate {
    
    func showHomeViewController() {
        let coordinator = HomeCoordinator(navigationController)
        childCoordinators.append(coordinator)
        coordinator.start()
    }
}

๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ViewModel์—์„œ showHomeViewController ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ, delegate๋ฅผ ์ฑ„ํƒํ•˜๊ณ  ์žˆ๋Š” LoginCoordinator์˜ showHomeViewController๊ฐ€ ํ˜ธ์ถœ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ธฐ์กด์˜ Coordinator๋’ค์— HomeCoordinator๊ฐ€ ์ถ”๊ฐ€๋˜๊ณ ,  start ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ํ™”๋ฉด์ „ํ™˜์ด ์ด๋ฃจ์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๋‹ค์‹œ ๊ณต๋ถ€ํ•ด์•ผ๊ฒ ๋‹ค

์ €์˜ ๊ฒฝ์šฐ์—๋Š” ViewModel์˜ ์ƒ์„ฑ์ž์— Coordinator๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ViewModel ๋‚ด๋ถ€์—์„œ Coordinator๋ฅผ ์‚ฌ์šฉํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

class LoginViewModel {

    weak var coordinator: HomeCoordinator?
    
    init(coordinator: HomeCoordinator...) {
        self.coordinator = coordiantor
        ...
    }

    func showHomeViewController() {
         coordinator?.showHomeViewController()
    }
}

์˜ˆ๋ฅผ ๋“ค์–ด ์œ„์ฒ˜๋Ÿผ ์‚ฌ์šฉํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. 

ViewModel์—์„œ coordinator๋ฅผ ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋‹ˆ ํŠน์ •ํ•œ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ Home, Profile, Chat ๋“ฑ Coordinator์—์„œ ๋ชจ๋‘ ์‚ฌ์šฉ๋˜๊ฒŒ ๋  ๊ฒฝ์šฐ์—๋Š” ํ•ด๋‹น ๋ทฐ๋ชจ๋ธ๋“ค์€ ์ƒ์„ฑ์ž๋ฅผ Coordinator ํ”„๋กœํ† ์ฝœ๋กœ ๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜๋ฐ–์— ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœํ† ์ฝœ๋กœ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•˜๋”๋ผ๊ณ  ์–ด๋–ค Coordinator์ธ์ง€ ( Chat, Profile, Home...) ๋ถ„๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ๋งค๋ฒˆ ํ•ด์•ผ๋งŒ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

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

์•„๋ฌด์ชผ๋ก ๋„์›€์ด ๋˜์…จ์œผ๋ฉด ์ข‹๊ฒ ๋„ค์š”.

๊ทธ๋Ÿผ ์ด๋งŒ  ๐Ÿ‘‹๐Ÿป ๐Ÿ‘‹๐Ÿป ๐Ÿ‘‹๐Ÿป

 

๋ฐ˜์‘ํ˜•