๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐ŸŽ iOS/iOS

[iOS/Architecture] Coordinator Pattern

์•ˆ๋…•ํ•˜์„ธ์š” ์ œ์ธ์ž…๋‹ˆ๋‹ค!

์˜ค๋Š˜์€ ์ฐธ์—ฌํ•˜๊ณ  ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์— ๋„์ž…ํ•˜๊ฒŒ ๋œ ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด๋ณด๋ คํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด๋Š” ๊ฒƒ์ด ์ฒ˜์Œ์ด๋ผ ๊ณต๋ถ€๋ฅผ ํ•˜๋ฉด์„œ ์ ์šฉํ•˜๋Š” ๊ณผ์ •์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์—

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์ด ๋ฌด์—‡์ธ์ง€ ์•Œ์•„๋ณด๋Š” ์ •๋„๋กœ๋งŒ ์ •๋ฆฌํ•˜๊ณ , ๋‹ค๋ฅธ ํฌ์ŠคํŒ…์œผ๋กœ ๊ตฌ์ฒด์ ์ธ ์ ์šฉ๊ธฐ๋ฅผ ๋“ค๊ณ ์˜ค๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!!

์ด ์  ์ฐธ๊ณ ํ•ด์„œ ์ฝ์–ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค : )

Coordinator Pattern์„ ๋„์ž…ํ•˜๊ฒŒ ๋œ ๊ณ„๊ธฐ

MVVM ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ ์šฉํ•ด UI์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ณผ์ •์—์„œ ํ™”๋ฉด์ „ํ™˜์€ ์–ด๋””์„œ ํ•ด์ค˜์•ผํ•˜๋Š”๊ฑฐ์ง€..?

๋ผ๋Š” ์˜๋ฌธ์ด ๋“ค์—ˆ์—ˆ๋Š”๋ฐ์š”, ํ™”๋ฉด์ „ํ™˜์ด ๋”ฐ์ง€๊ณ  ๋ณด๋ฉด UI์™€ ๊ด€๋ จ๋œ ๋ถ€๋ถ„์€ ์•„๋‹ˆ์ง€๋งŒ ViewController๊ฐ€ ๋‹ค์Œ ํ™”๋ฉด์„ present ํ•ด์ฃผ์–ด์•ผํ•˜๋‹ˆ๊นŒ..๋ผ๋Š” ์ƒ๊ฐ์— VC์— ํ™”๋ฉด์ „ํ™˜ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ค€ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

MVVM ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋„์ž…ํ•˜๊ฒŒ ๋œ ์ด์œ ๊ฐ€ ๊ธฐ์กด MVC ํŒจํ„ด์—์„œ VC๊ฐ€ ๋ชจ๋“  ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— VC๊ฐ€ ์ ์  ๋ฌด๊ฑฐ์›Œ์ง€๊ฒŒ ๋œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์–ด VC์˜ ์—ญํ• ์„ ๋ถ„๋ฆฌํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ์˜€๋Š”๋ฐ์š”, ํ™”๋ฉด์ „ํ™˜ ์ฝ”๋“œ๋„ VC๊ฐ€ ๋‹ด๋‹นํ•˜๋‹ˆ๊นŒ ๋ญ”๊ฐ€ ์—ญํ•  ๋ถ„๋ฆฌ๋ฅผ ๋œํ•œ ๋Š๋‚Œ...๊ณผ ํ•จ๊ป˜ MVVM ํŒจํ„ด์„ ์ œ๋Œ€๋กœ ์ ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ๋งž๋‚˜?? ํ•˜๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํ™”๋ฉด์ „ํ™˜ ์ฝ”๋“œ๊นŒ์ง€ ๋ถ„๋ฆฌํ•ด์„œ VC๋Š” ์ •๋ง UI๋งŒ ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• ๋งŒ ํ•˜๊ฒŒ ํ•  ์ˆ˜ ์—†์„๊นŒ? ํ•˜๋ฉฐ ์ฐพ์•„๋ณด๋‹ค Coordinator Pattern์œผ๋กœ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

๊ธฐ์กด์˜ ํ™”๋ฉด ์ „ํ™˜ ๋ฐฉ์‹

iOS์—์„œ๋Š” ํ™”๋ฉด ์ „ํ™˜์„ ๋‹ด๋‹นํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์ธ UINavigationController๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Stack ๋ฐฉ์‹์œผ๋กœ ์ƒˆ๋กœ์šด ํ™”๋ฉด์„ pushํ•˜๊ณ , ์ด์ „ํ™”๋ฉด์œผ๋กœ ๋Œ์•„๊ฐ€๊ฐ€๊ธฐ ์œ„ํ•ด popํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜์ฃ ! ๊ฐ€์žฅ ์ฒซ ํ™”๋ฉด์„ ๊ธฐ์ค€์œผ๋กœ ์ƒˆ๋กœ์šด ํ™”๋ฉด์œผ๋กœ ๋„˜์–ด๊ฐˆ๋•Œ ๋งˆ๋‹ค ์ˆœ์„œ๋Œ€๋กœ ์Œ“์ด๊ณ , ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ํ†ตํ•ด ์ด์ „์— ๋ฐฉ๋ฌธํ–ˆ๋˜ ํ™”๋ฉด๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ ๊บผ๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

navigationController?.pushviewController(nextViewController, animated:true)

 

push, pop์„ ์ด์šฉํ•˜์—ฌ ์‰ฝ๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ํ™”๋ฉด์„ ์ „ํ™˜ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‹จ์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

์•ฑ์ด ์ ์ฐจ ์ปค์ง€๊ณ  ํ™”๋ฉด์ด ๋งŽ์•„์ ธ ํ™”๋ฉด์ „ํ™˜ ํ”Œ๋กœ์šฐ๊ฐ€ ๋ณต์žกํ•ด์ง€๊ฒŒ ๋˜๋ฉด ๋„ค๋น„๊ฒŒ์ด์…˜ ์Šคํƒ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ์ง€๊ณ ,

ํ™”๋ฉด์„ ์ „ํ™˜ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ViewController๋ฅผ ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์— VC๊ฐ€ ๋ฌด๊ฑฐ์›Œ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค..

์ด๋Ÿฌํ•œ ๋‹จ์ ๋“ค์„ ์ง์ ‘ ๋Š๊ผˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ„์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ Coordinator ํŒจํ„ด์„ ๋„์ž…ํ•˜๊ฒŒ ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค!

Coordinator Pattern์ด๋ž€?

์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์€ ViewController๋กœ ๋ถ€ํ„ฐ ํ™”๋ฉด ์ „ํ™˜์˜ ๋ถ€๋‹ด์„ ์ค„์—ฌ์ฃผ๊ณ , ํ™”๋ฉด์ „ํ™˜์„ ๋ณด๋‹ค ๋” ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ๋„๋ก ๋„์™€์ฃผ๊ธฐ ์œ„ํ•œ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. (ํ™”๋ฉด์˜ ํ๋ฆ„์„ ์ œ์–ดํ•ด์ฃผ๋Š” ์—ญํ• , ๋ผ์šฐํŒ…, VC๊ด€๋ฆฌ)

 

์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์€ ๋ฌด์—‡์ผ๊นŒ์š”??

coordinator ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด, ViewController ์‚ฌ์ด์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถฐ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ™”๋ฉด์ „ํ™˜์€ coordinator๊ฐ€ ๋ชจ๋‘ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ ViewController๋Š” ์ด์ „์— ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์žˆ์—ˆ๋Š”์ง€, ๋‹ค์Œ์— ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์˜ค๋Š”์ง€ ์•Œ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์˜ค๋กœ์ง€ coordinator๋งŒ์ด ์ด๊ฒƒ์„ ์•Œ๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— VC ์‚ฌ์ด์˜ ๊ฒฐํ•ฉ๋„๋Š” ๋‚ฎ์•„์ง€๊ฒ ์ฃ ??

 

๋˜ํ•œ, coordinator๋ฅผ ์ด์šฉํ•œ ํ™”๋ฉด ์ „ํ™˜ ์‹œ ViewController์—์„œ ์‚ฌ์šฉํ•  ViewModel์„ ํ•จ๊ป˜ ์ฃผ์ž…ํ•ด์ค„ ์ˆ˜ ์žˆ์–ด DI ๋˜ํ•œ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! (ViewModel์„ VC ๋‚ด๋ถ€๊ฐ€ ์•„๋‹Œ ์™ธ๋ถ€์— ์ •์˜ํ•˜๋„๋ก ํ•˜์—ฌ ์˜์กด์„ฑ์„ ๋ถ„๋ฆฌ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” ํ—ˆ๋ธŒ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์ดํ•ด๊ฐ€ ์‰ฌ์šธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ •๋ฆฌํ•˜์ž๋ฉด, ์ฝ”๋””๋„ค์ดํ„ฐ๋Š” ํ™”๋ฉด ์ „ํ™˜ ์ œ์–ด ๋‹ด๋‹น์ž์ด์ž ์˜์กด์„ฑ ์ฃผ์ž…์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ํ—ˆ๋ธŒ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋„ค์š”!!

Coordinator Pattern์˜ ํ๋ฆ„

์•ฑ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง„๋‹ค๋ฉด, ์œ„์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

๋ฌผ๋ก  ๊ฐ„๋‹จํ•œ ์•ฑ์˜ ๊ฒฝ์šฐ ์ฝ”๋””๋„ค์ดํ„ฐ ํ•˜๋‚˜๋งŒ ๋‘๊ณ ๋„ ํ™”๋ฉด์ „ํ™˜์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, ๊ธฐ์กด์— UINavigationController๋ฅผ ์‚ฌ์šฉํ•ด ํ™”๋ฉด์ „ํ™˜์„ ํ•ด์คฌ๋˜ ๊ฒฝํ—˜์„ ์ƒ๊ฐํ•ด๋ณธ๋‹ค๋ฉด, ์•ฑ์˜ ๊ทœ๋ชจ๊ฐ€ ํด ๊ฒฝ์šฐ ์Šคํƒ ํ•˜๋‚˜๋งŒ์œผ๋กœ ํ™”๋ฉด์ „ํ™˜์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด์˜ ๋ฐฉ์‹์—์„œ ๋ณดํ†ต ํƒญ๋ณ„๋กœ NavigationController๋ฅผ ํ•˜๋‚˜์”ฉ ๋‘๊ณ  ๊ด€๋ฆฌํ•ด์ฃผ์—ˆ์—ˆ๋Š”๋ฐ,

coordinator ํŒจํ„ด๋„ ๋น„์Šทํ•˜๊ฒŒ ์ตœ์ƒ์œ„์— MainCoordinator๊ฐ€ ์กด์žฌํ•˜๊ณ  ํ•˜์œ„์— ๊ฐ๊ฐ์˜ ์Šคํƒ(๋ณดํ†ต ํƒญ ๋‹จ์œ„๋‚˜ ์—ญํ• ๋‹จ์œ„)์„ ๊ด€๋ฆฌํ•˜๋Š” ํ•˜์œ„ ์ฝ”๋””๋„ค์ดํ„ฐ์ธ ChildCoordinator๋“ค์„ ๋‘๊ณ  ํ™”๋ฉด์ „ํ™˜์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

  • ์ตœ์ƒ์œ„ Coordinator: ChildCoordinator(ํ•˜์œ„ ์ฝ”๋””๋„ค์ดํ„ฐ)๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ œ๊ฑฐ. ๋ชจ๋“  ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ
  • ChildCoordinator: ํ™”๋ฉด ์ „ํ™˜์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์—ญํ• 

ํ™”๋ฉด์„ ์ „ํ™˜ํ•ด์•ผํ•  ๋•Œ, ๊ฐ๊ฐ์˜ View ๋‹ด๋‹น Coordinator์—๊ฒŒ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์ด ์š”์ฒญ์„ ๋ฐ›์€ Coordinator๋Š” ์‘๋‹ต์œผ๋กœ ํ™”๋ฉด์„ ์ „ํ™˜์‹œ์ผœ์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ์š”, ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด์„œ ์ดํ•ดํ•ด๋ด…์‹œ๋‹ค!

์ฝ”๋“œ๋กœ ์ดํ•ดํ•ด๋ณด์ž

 

MainViewController์—์„œ ์ฒซ๋ฒˆ์งธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด FirstViewController, ๋‘๋ฒˆ์งธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด SecondViewController๋กœ ๋ฒ„ํŠผ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๊ณ , FirstViewController๋กœ ์ด๋™ํ–ˆ์„ ๊ฒฝ์šฐ์—๋Š” ํ™”๋ฉด์˜ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋˜ ๋‹ค๋ฅธ ๋ทฐ๋กœ์˜ ์ „ํ™˜๊นŒ์ง€ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์ด์šฉํ•ด์„œ ๊ตฌํ˜„ํ•ด๋ณด๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค! ๊ตฌ์กฐ๋Š” ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด MainCoordinator ๋ฐ‘์— ๋‘๊ฐœ์˜ ํ•˜์œ„ ์ฝ”๋””๋„ค์ดํ„ฐ๋“ค(FirstCoordinator, SecondCoordinator)์„ ๋‘๋Š” ํ˜•ํƒœ๋กœ ์žก์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

protocol ์ƒ์„ฑ

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

 

๋จผ์ €, ํ”„๋กœํ† ์ฝœ์„ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค.

์ด ํ”„๋กœํ† ์ฝœ์€ ์ž์‹ Coordinator๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋ฐฐ์—ด๊ณผ navigation ์Šคํƒ์„ ์Œ“์„ UINavigationController ํƒ€์ž…์˜ ๋ณ€์ˆ˜(ํ™”๋ฉด์ „ํ™˜์‹œ ๊ธฐ์ค€์ด ๋˜๋Š” ๋„ค๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์†Œ์œ ํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜), ๊ทธ๋ฆฌ๊ณ  ์‹œ์ž‘ ์‹œ ์‹คํ–‰๋  ํ•จ์ˆ˜(์ฒซ ํ™”๋ฉด์„ ๋„์›Œ์ฃผ๋Š” ๋ฉ”์†Œ๋“œ)๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

MainCoordinator ์ •์˜

์œ„์—์„œ ์ •์˜ํ•œ ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” MainCoordinator ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

class MainCoordinator: Coordinator {
    var childCoordinators = [Coordinator]()
    var navigationController: UINavigationController
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() {
        let mainViewController = MainViewController()
        mainViewController.coordinator = self
        navigationController.pushViewController(mainViewController, animated: true) // ํ™”๋ฉด์ „ํ™˜
    }
    
    func pushFirstViewController() {
        let firstCoordinator = FirstCoordinator(navigationController: navigationController)
        firstCoordinator.parentCoordinator = self
        childCoordinators.append(firstCoordinator)
        firstCoordinator.start()
    }
    
    func pushSecondViewController() {
        let secondCoordinator = SecondCoordinator(navigationController: navigationController)
        secondCoordinator.parentCoordinator = self
        childCoordinators.append(secondCoordinator)
        secondCoordinator.start()
    }
}

 

init ์ƒ์„ฑ์ž์˜ ์ธ์ž๋กœ navigationController๋ฅผ ๋„ฃ์–ด์ฃผ์–ด ์ดˆ๊ธฐํ™”๋ฅผ ํ•ด์ฃผ๊ณ  start() ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด VC๋กœ ๋ถ€ํ„ฐ ํ™”๋ฉด ์ „ํ™˜ ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด, ์‘๋‹ต์œผ๋กœ ์ฒซ ํ™”๋ฉด์„ ๋„์šธ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ ์ฝ”๋“œ๋ผ ๋ทฐ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•ด์ฃผ์–ด ๋ทฐ๋ชจ๋ธ์„ ์ฃผ์ž…ํ•ด์ฃผ๋Š” ์ฝ”๋“œ๊ฐ€ ์—†๋Š”๋ฐ, ๋ทฐ๋ชจ๋ธ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋Š” ViewController ์ƒ์„ฑ์‹œ์ ์— ๋ทฐ๋ชจ๋ธ์„ ์ฃผ์ž…ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค!!

 

๋‹ค์Œ์œผ๋กœ, MainViewController์˜ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๊ฒŒ ๋˜๋ฉด ์ ์ ˆํ•œ ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด ๋ฉ”์„œ๋“œ ์ƒ์„ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๋‹น ์˜ˆ์ œ์—์„œ ๋ฉ”์ธ ๋ทฐ ๋‹ค์Œ์œผ๋กœ ์˜ค๋Š˜ ํ”Œ๋กœ์šฐ์— ๋Œ€ํ•ด์„œ๋Š” ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๋“ค์ด ๊ด€๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

์ด๋ฅผ ์œ„ํ•ด์„œ FirstCoordinator ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ํ›„, ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ self๋กœ ์„ค์ •ํ•ด์ฃผ๊ณ  childCoordinators์— ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, start() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ์–ด ํ™”๋ฉด ์ „ํ™˜ ์š”์ฒญ ์‹œ ๋‹ค์Œ ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.

ChildCoordinator(FirstCoordinator, SecondCoordinator) ์ •์˜

class FirstCoordinator: Coordinator {
    var childCoordinators: [Coordinator] = []
    var navigationController: UINavigationController
    weak var parentCoordinator: MainCoordinator?
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() {
        let firstViewController = FirstViewController()
        firstViewController.coordinator = self
        navigationController.pushViewController(firstViewController, animated: true)
    }
    
    func pushNextViewController() {
        let nextViewController = FirstSubViewController()
        nextViewController.coordinator = self
        navigationController.pushViewController(nextViewController, animated: true)
    }
}

 

Parent์™€ Child์‚ฌ์ด์— ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ณ  ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ child๋Š” ๋ถ€๋ชจ๋ฅผ ๊ธฐ์–ตํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•˜๊ฒ ์ฃ ?
์ด ๋•Œ๋ฌธ์— ChildCoordinator๋Š” ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ํ”„๋กœํผํ‹ฐ๋กœ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ ๋ถ€๋ชจ ์ฝ”๋””๋„ค์ดํ„ฐ์™€ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์„œ๋กœ๋ฅผ ์•Œ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ์ฐธ์กฐ ์ˆœํ™˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๋Š” weak ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

FirstCoordinator(๋ฉ”์ธ ์ฝ”๋””๋„ค์ดํ„ฐ์— ๋Œ€ํ•œ ์ž์‹ ์ฝ”๋””๋„ค์ดํ„ฐ)์—์„œ๋Š” FirstViewController์™€ FirstSubViewController์— ๋Œ€ํ•œ ํ™”๋ฉด ์ „ํ™˜ ๋กœ์ง์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ๋ฉ”์ธ ์ฝ”๋””๋„ค์ดํ„ฐ์—์„œ์™€ ๋˜‘๊ฐ™์ด ํ™”๋ฉด์ „ํ™˜ ์š”์ฒญ์ด ์˜ค๋ฉด ํ™”๋ฉด์„ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก start()ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด FirstViewController๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๊ณ , pushNextViewController()๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€๋กœ ์ƒ์„ฑํ•ด FirstSubViewController๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋Š” ๋กœ์ง๋„ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

SecondCoordinator๋„ ๋™์ผํ•œ ๊ณผ์ •์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋˜๋Š”๋ฐ, SecondCoordinator๋Š” SecondViewController์— ๋Œ€ํ•œ ํ™”๋ฉด ์ „ํ™˜ ๋กœ์ง๋งŒ์„ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— start() ๋ฉ”์„œ๋“œ๋งŒ ๊ตฌํ˜„ํ•ด์ฃผ๋ฉด ๋˜๊ฒ ์ฃ ??

 

SceneDelegate ์„ค์ •

์ด์ œ ์ฝ”๋””๋„ค์ดํ„ฐ์— ๋Œ€ํ•œ ์ค€๋น„๋Š” ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ์ฒซ ํ™”๋ฉด์„ ๋„์›Œ๋ด…์‹œ๋‹ค!!

 

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 }
        let navigationController = UINavigationController()
        mainCoordinator = MainCoordinator(navigationController: navigationController)
        mainCoordinator?.start()
        
        self.window = UIWindow(windowScene: windowScene)
        self.window?.rootViewController = navigationController
        self.window?.makeKeyAndVisible()
    }
}

 

์ฒซ ํ™”๋ฉด ์„ค์ •์€ SceneDelegate์—์„œ ํ•ด์ฃผ๋ฉด ๋˜๋Š”๋ฐ์š”!(iOS13 ์ดํ•˜๋ผ๋ฉด AppDelegate์—์„œ)

 

MainCoordinator ์ƒ์„ฑ์„ ์œ„ํ•ด ํ•„์š”ํ•œ NavigationController ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ MainCoordinator ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ดํ›„, start() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ•ด๋‹น NavigationController์— MainViewController๋ฅผ pushํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ์•ฑ์—์„œ ๋Ÿฐ์นญ์ด ๋๋‚˜๋ฉด ์ฒซ ํ™”๋ฉด์ธ MainViewController๊ฐ€ ํ™”๋ฉด์— ๋ณด์—ฌ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค!

MainViewController ์„ค์ •

๊ทธ๋ ‡๋‹ค๋ฉด ํ˜„์žฌ ํ™”๋ฉด์—์„œ ๋‹ค์Œ ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด VC์—์„œ ์ฝ”๋””๋„ค์ดํ„ฐ๋กœ ์–ด๋–ป๊ฒŒ ํ™”๋ฉด ์ „ํ™˜ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š”์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค!

 

class MainViewController: UIViewController {
    
    // MARK: Properties
    private let firstButton = UIButton()
    private let secondButton = UIButton()
    weak var coordinator: MainCoordinator?
    
    let disposeBag = DisposeBag()
    
    // MARK: Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setStyle()
        setLayout()
        tapButtonAction()
    }
    
    // MARK: UI
    private func setStyle() {
        // UI ์„ค์ •
    }
    
    private func setLayout() {
        // ๋ ˆ์ด์•„์›ƒ ์„ค์ •
    }
    
    // MARK: Action
    private func tapButtonAction() {
        firstButton.rx.tap.asDriver(onErrorJustReturn: ())
            .drive(onNext: { [weak self] in
                self?.coordinator?.pushFirstViewController()
            })
            .disposed(by: disposeBag)
        
        secondButton.rx.tap.asDriver(onErrorJustReturn: ())
            .drive(onNext: { [weak self] in
                self?.coordinator?.pushSecondViewController()
            })
            .disposed(by: disposeBag)
    }
}

 

๋ฒ„ํŠผ์„ ๋‘ ๊ฐœ ๋งŒ๋“ค๊ณ  RxCocoa๋ฅผ ์ด์šฉํ•ด ํ•ด๋‹น ๋ฒ„ํŠผ๋“ค์˜ ํ„ฐ์น˜ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋…ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฒ„ํŠผ์˜ ํ„ฐ์น˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐฉ์ถœ๋˜๊ฒŒ ๋˜๋ฉด coordinator์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์›ํ•˜๋Š” ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ํ•ด์ค๋‹ˆ๋‹ค! (๋ทฐ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๋ทฐ๋ชจ๋ธ์ด VC์˜ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ํ˜•ํƒœ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋””๋„ค์ดํ„ฐ์˜ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์€ ๋ทฐ๋ชจ๋ธ์—์„œ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค)

 

ํ˜„์žฌ๊นŒ์ง€์˜ ํ™”๋ฉด ์ „ํ™˜ ๊ณผ์ •์„ ์ •๋ฆฌํ•˜์ž๋ฉด, MainViewController๋ฅผ  ์ฒ˜์Œ์— ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์€ MainCoordinator๊ฐ€ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, MainViewController์—์„œ First, SecondViewController๋กœ์˜ ์ด๋™์€ ๊ฐ๊ฐ FirstViewCoordinator, SecondViewCoordinator๊ฐ€ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. (MainCoordinator.push~VC() → ~ViewCoordinator.start()์˜ ํ๋ฆ„์œผ๋กœ ๋™์ž‘)

FirstViewController ์„ค์ •

๋งˆ์ง€๋ง‰์œผ๋กœ FirstViewController์—์„œ FirstSubViewController์˜ ํ™”๋ฉด์ „ํ™˜์„ ์‚ดํŽด๋ด…์‹œ๋‹ค.

 

class FirstViewController: UIViewController {

    // MARK: Properties
    private let titleLabel = UILabel()
    private let nextButton = UIButton()
    weak var coordinator: FirstCoordinator?
    
    let disposeBag = DisposeBag()
    
    // MARK: Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setStyle()
        setLayout()
        tapButtonAction()
    }
    
    // MARK: UI
    private func setStyle() {
        // UI ์„ค์ •
    }
    
    private func setLayout() {
        // ๋ ˆ์ด์•„์›ƒ ์„ค์ •
    }
    
    // MARK: Action
    private func tapButtonAction() {
        nextButton.rx.tap.asDriver(onErrorJustReturn: ())
            .drive(onNext: { [weak self] in
                self?.coordinator?.pushNextViewController()
            })
            .disposed(by: disposeBag)
    }
}

 

์œ„์—์„œ ํ–ˆ๋˜ ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ RxCocoa๋ฅผ ์ด์šฉํ•ด ํ•ด๋‹น ๋ฒ„ํŠผ์˜ ํ„ฐ์น˜ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋…ํ•ด ์ด๋ฒคํŠธ ๋ฐฉ์ถœ ์‹œ coordinator์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋‹ค์Œ ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ํ•ด์ค๋‹ˆ๋‹ค. ๋ฐฉ์‹์€ ๋™์ผํ•˜์ง€๋งŒ, ๋‹ค์Œ ํ™”๋ฉด์ธ FirstSubViewController๋กœ์˜ ์ด๋™์€ ๋™์ผํ•œ ์ฝ”๋””๋„ค์ดํ„ฐ(=FirstCoordinator)๊ฐ€ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๊ธฐ์„œ๋Š” pushNextViewController() ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ ๋ณ„๋„์˜ ์ฝ”๋””๋„ค์ดํ„ฐ ์ƒ์„ฑ ์—†์ด ํ™”๋ฉด์ „ํ™˜์ด ์ด๋ฃจ์–ด ์ง„๋‹ค๋Š” ์ ์ด ์•ž์—์„œ ๋ดค๋˜ ํ™”๋ฉด ์ „ํ™˜ ๊ณผ์˜ ์ฐจ์ด์ ์ž…๋‹ˆ๋‹ค!

 

์ „์ฒด์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ํ™”๋ฉด ์ „ํ™˜์ด ์ด๋ฃจ์–ด์ง€๊ณ  ์žˆ๋Š”์ง€ ๊ทธ๋ฆผ๊ณผ ์˜ˆ์‹œ ์ฝ”๋“œ๋กœ ์ดํ•ด๊ฐ€ ๋˜์…จ๊ธธ ๋ฐ”๋ผ๋ฉฐ,,

์ „์ฒด ์ฝ”๋“œ๋„ ๊ฐ™์ด ์ฒจ๋ถ€ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!!

 

์ „์ฒด ์ฝ”๋“œ

https://github.com/jane1choi/Coordinator-Practice

 

GitHub - jane1choi/Coordinator-Practice

Contribute to jane1choi/Coordinator-Practice development by creating an account on GitHub.

github.com

์œ„์˜ ๋งํฌ์—์„œ ์ „์ฒด ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์–ด๋–ป๊ฒŒ ์ ์šฉํ–ˆ๋Š”์ง€ ์ข€ ๋” ๋ณต์žกํ•œ ํ”Œ๋กœ์šฐ์˜ ํ™”๋ฉด์ „ํ™˜ ์˜ˆ์‹œ๋ฅผ ๊ฐ€์ ธ์™€๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!!

๋ถ€์กฑํ•œ ์ ์ด ๋งŽ์€ ๊ธ€์ด๋‹ˆ ์กฐ์–ธ์ด๋‚˜ ์ง€์  ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค๐Ÿ˜Š๐Ÿ˜Š 

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!!

 


[์ฐธ๊ณ  ์ž๋ฃŒ]

 

[iOS] Coordinator Pattern

Coordinator ์˜ค๋Š˜์€ Coordinator์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•ด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. Coordinator๋ž€ ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š”์ง€, ๋˜ ์–ด๋–ค ์—ญํ• ์„ ํ•˜๋Š”์ง€ ๋จผ์ € ์•Œ์•„๋ณด๋„๋ก ํ• ๊ผ์š”! Coordinator ๋ž€? Coordinator ํŒจํ„ด์„ ์†Œ๊ฐœํ•œ Soroush Khanlou๋Š” ์ฝ”

duwjdtn11.tistory.com

 

 

Coordinator Pattern

Coordinator ํŒจํ„ด์˜ ์‹œ์ž‘ Coordinator๋ž€ ํ™”๋ฉด์ „ํ™˜์„ ์œ„ํ•œ delegate์ด๋‹ค. Stack ๋ฐฉ์‹์œผ๋กœ ์ƒˆ๋กœ์šด ํ™”๋ฉด์„ pushํ•˜๊ณ , ์ด์ „ํ™”๋ฉด์œผ๋กœ ๋Œ์•„๊ฐ€๊ฐ€๊ธฐ ์œ„ํ•ด popํ•œ๋‹ค. ๊ฐ€์žฅ ์ฒซ ํ™”๋ฉด์„ ๊ธฐ์ค€์œผ๋กœ ์ƒˆ๋กœ์šด ํ™”๋ฉด์œผ๋กœ ๋„˜์–ด๊ฐˆ๋•Œ

velog.io

 

 

[iOS] ๋ฉ”์ดํŠธ๋Ÿฌ๋„ˆ: ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด ์ ์šฉ๊ธฐ

๋ฉ”์ดํŠธ ๋Ÿฌ๋„ˆ ์•ฑ ๊ฐœ๋ฐœ ๊ณผ์ •์„ ๊ณต์œ ํ•˜๋Š” ํฌ์ŠคํŠธ์ž…๋‹ˆ๋‹ค! GitHub - boostcampwm-2021/iOS06-MateRunner: ํ•จ๊ป˜ ๋‹ฌ๋ฆฌ๋Š” ์ฆ๊ฑฐ์›€, Mate Runner ๐Ÿƒ๐Ÿป‍โ™‚๏ธ๐Ÿƒ๐Ÿป‍โ™€๏ธ ํ•จ๊ป˜ ๋‹ฌ๋ฆฌ๋Š” ์ฆ๊ฑฐ์›€, Mate Runner ๐Ÿƒ๐Ÿป‍โ™‚๏ธ๐Ÿƒ๐Ÿป

jeonyeohun.tistory.com