# ๐ŸงƒJuice Maker ## ๐Ÿ“š๋ชฉ์ฐจ 1. [์†Œ๊ฐœ](#1-์†Œ๊ฐœ) 2. [ํŒ€์›](#2-ํŒ€์›) 3. [ํƒ€์ž„๋ผ์ธ](#3-ํƒ€์ž„๋ผ์ธ) 4. [ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ](#4-ํ”„๋กœ์ ํŠธ-๊ตฌ์กฐ) 5. [์‹คํ–‰ํ™”๋ฉด(๊ธฐ๋Šฅ ์„ค๋ช…)](#5-์‹คํ–‰-ํ™”๋ฉด๊ธฐ๋Šฅ-์„ค๋ช…) 6. [ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…](#6-ํŠธ๋Ÿฌ๋ธ”-์ŠˆํŒ…) 7. [์ฐธ๊ณ ๋งํฌ](#7-์ฐธ๊ณ -๋งํฌ) <br/> ## 1. ์†Œ๊ฐœ ์ƒ๊ณผ์ผ ์ฅฌ์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๊ฐ์ฒด์™€ ๊ณผ์ผ์ €์žฅ์†Œ ๊ฐ์ฒด์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ํ†ตํ•ด ๊ณผ์ผ์˜ ์žฌ๊ณ ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ ### Model - **FruitStore class** - **๋”ธ๊ธฐ, ๋ฐ”๋‚˜๋‚˜, ํŒŒ์ธ์• ํ”Œ, ํ‚ค์œ„, ๋ง๊ณ  5์ข…๋ฅ˜์˜ ๊ณผ์ผ ์žฌ๊ณ ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค ์ •์˜** <br/> > * ๊ฐ ๊ณผ์ผ์˜ ์ดˆ๊ธฐ ์žฌ๊ณ : 10๊ฐœ > * ๊ฐ ๊ณผ์ผ์˜ ์ˆ˜๋Ÿ‰ n๊ฐœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฉ”์„œ๋“œ > * ๊ฐ ๊ณผ์ผ์˜ ์žฌ๊ณ ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฉ”์„œ๋“œ - **JuiceMaker struct** - **FruitStore์˜ ๊ณผ์ผ์„ ์‚ฌ์šฉํ•ด ๊ณผ์ผ์„ ์ œ์กฐํ•˜๋Š” ๊ตฌ์กฐ์ฒด ์ •์˜** <br/> > * ๊ฐ ์ฅฌ์Šค ๋ณ„ ๋“ค์–ด๊ฐ€๋Š” ๊ณผ์ผ์˜ ๊ฐœ์ˆ˜๋ฅผ ๊ธฐ๋กํ•˜๋Š” Nested Type Juice ์—ด๊ฑฐ์ฒด > * ์ฅฌ์Šค๋ฅผ ๋งŒ๋“œ๋Š” make ๋ฉ”์„œ๋“œ <br/> ### ViewController - **MainViewController** - **์ฅฌ์Šค ์ฃผ๋ฌธ ํ™”๋ฉด์„ ๊ด€๋ฆฌํ•˜๋Š” ViewController ์ •์˜** <br/> > * ๊ฐ ๊ณผ์ผ์˜ ์žฌ๊ณ ๋ฅผ ํ‘œ์‹œํ•˜๋Š” Label > * ์ฅฌ์Šค ์ฃผ๋ฌธ ๋ฒ„ํŠผ์˜ ```@IBOutlet```, ```@IBAction``` > * ํ™”๋ฉด์ด Load๋˜์—ˆ์„ ๋•Œ, Label Text๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฉ”์„œ๋“œ > * ์žฌ๊ณ ๊ฐ€ ๋ถ€์กฑํ•  ๋•Œ ๋„์›Œ์ค„ Alert - **Modal** ```MainViewController```์—์„œ ```ChangeStockViewController```์œผ๋กœ ํ™”๋ฉด ์ „ํ™˜ ๊ตฌํ˜„ <br/> > * Modal ๋ฐฉ์‹์„ ํ†ตํ•œ ํ™”๋ฉด์ „ํ™˜ ๊ตฌํ˜„ > * Alert์„ ํ†ตํ•œ ํ™”๋ฉด์ „ํ™˜ > * Button์„ ํ†ตํ•œ ํ™”๋ฉด์ „ํ™˜ <br/> - **ChangeStockViewController** - **๊ณผ์ผ ์žฌ๊ณ  ์ˆ˜์ • ํ™”๋ฉด์„ ๊ด€๋ฆฌํ•˜๋Š” ChangeStockViewController ๊ตฌํ˜„** <br/> > * ๊ฐ ๊ณผ์ผ์˜ ์žฌ๊ณ ๋ฅผ ํ‘œ์‹œํ•˜๋Š” Label > * ๊ณผ์ผ ์žฌ๊ณ  ๋ณ€๊ฒฝ Stepper ๋ฒ„ํŠผ์˜ ```@IBOutlet```, ```@IBAction``` > * ํ™”๋ฉด์ด Load๋˜์—ˆ์„ ๋•Œ, Label Text๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฉ”์„œ๋“œ > * Delegation์„ ์œ„ํ•œ delegate ํ”„๋กœํผํ‹ฐ - **Delegate Pattern์„ ํ†ตํ•œ Mainํ™”๋ฉด ์—…๋ฐ์ดํŠธ** > ```ChangeStockViewController```์—์„œ ๋ฐœ์ƒํ•œ ์žฌ๊ณ  ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ```delegate```๋ฅผ ํ†ตํ•ด ```MainViewController``` ํ™”๋ฉด ์—…๋ฐ์ดํŠธ - **AutoLayout์œผ๋กœ ๋ทฐ ์„ค์ •** AutoLayout์„ ์ด์šฉํ•˜์—ฌ ChangeStockViewController ํ™”๋ฉด ๊ตฌํ˜„ <br/> > * StackView๋ฅผ ํ™œ์šฉํ•˜์—ฌ, ๊ณผ์ผ image, ๊ณผ์ผ์žฌ๊ณ  label, stepper ์œ„์น˜ ์กฐ์ • > * NavigationBar๋ฅผ ํ™œ์šฉํ•˜์—ฌ, title ์ƒ์„ฑ ๋ฐ dismissButton ์ƒ์„ฑ <br/> ## 2. ํŒ€์› | ์†ก์ค€ | Rowan | | :--------: | :--------:| | <Img src = "https://i.imgur.com/9Bd6NIT.png" width="200" height="200"/> | <Img src = "https://i.imgur.com/NP7PZ6m.png" width="200" height="200"/> | | <center>Driver, Navigator</center> | <center>Driver, Navigator</center> | <center>[Github Profile](https://github.com/kimseongj)</center> | <center>[Github Profile](https://github.com/Kyeongjun2)</center> | </br> ## 3. ํƒ€์ž„๋ผ์ธ **ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ๊ธฐ๊ฐ„** - **23.01.02 (์›”) ~ 23.01.20 (๊ธˆ)** > 23.01.02 (์›”) : ์ฐธ๊ณ ์ž๋ฃŒ ์ฝ๊ธฐ, Model ๊ธฐ๋ณธ ๊ฐ์ฒด ๋ฐ ๋ฉ”์„œ๋“œ ์ •์˜ <br/> > 23.01.03 (ํ™”) : ๋ณ€์ˆ˜๋ช… / ํ•จ์ˆ˜ ๋ฆฌํŒฉํ† ๋ง, PR ๋ณด๋‚ด๊ธฐ <br/> > 23.01.04 (์ˆ˜) : PR ํ”ผ๋“œ๋ฐฑ์„ ํ†ตํ•œ ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง <br/> > 23.01.05 (๋ชฉ) : STEP 1. ๋จธ์ง€ / STEP 2. ์‚ฌ์ „ํ•™์Šต <br/> > 23.01.06 (๊ธˆ) : STEP 2. ์ง„ํ–‰ (๋ทฐ์ปจํŠธ๋กค๋Ÿฌ ์ •์˜, ํ™”๋ฉด์ „ํ™˜ ๊ตฌํ˜„) <br/> > 23.01.09 (์›”) : STEP 2. ํ™”๋ฉด ๊ตฌ์„ฑ, ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ „๋‹ฌ์„ ์œ„ํ•œ ์‹ฑ๊ธ€ํ„ด ํŒจํ„ด ์ ์šฉ <br/> > 23.01.10 (ํ™”) : STEP 2. refactoring(์ปจ๋ฒค์…˜ ์ˆ˜์ •, ์ฃผ๋ฌธ ๋ฒ„ํŠผ ```@IBAction``` ํ†ตํ•ฉ) <br/> > 23.01.11 (์ˆ˜) : STEP 2. ๋จธ์ง€ / STEP 3. ์‚ฌ์ „ํ•™์Šต <br/> > 23.01.12 (๋ชฉ) : STEP 3. ์ง„ํ–‰ (NotificationCenter๋ฅผ ํ™œ์šฉํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ „๋‹ฌ) <br/> > 23.01.13 (๊ธˆ) : STEP 3. ์—ฌ๋Ÿฌ ๋””๋ฐ”์ด์Šค์— ๋งž๊ฒŒ autolayout refactoring <br/> > 23.01.16 (์›”) : STEP 3. refactoring(์ปจ๋ฒค์…˜ ์ˆ˜์ •, ํ•จ์ˆ˜ ์ˆœ์„œ ๋ณ€๊ฒฝ) <br/> > 23.01.17 (ํ™”) : STEP 3. ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ „๋‹ฌ์„ Delegate Pattern์œผ๋กœ ๋ณ€๊ฒฝ <br/> ## 4. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ #### UML ![juicemaker (1)](https://user-images.githubusercontent.com/88870642/213629726-93771aec-1ecd-4105-a456-eb5d896b0877.png) #### ํŒŒ์ผ ๊ตฌ์กฐ ``` โ”œโ”€โ”€ JuiceMaker โ”‚ย ย  โ”œโ”€โ”€ Controller โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ AppDelegate โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ ChangeStockViewController โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ MainViewController โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ SceneDelegate โ”œโ”€โ”€ Model โ”‚ย ย  โ”œโ”€โ”€ Error โ”‚ย ย  โ”œโ”€โ”€ FruitStore โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ Fruits โ”‚ย ย  โ””โ”€โ”€ JuiceMaker โ”‚ย ย  โ””โ”€โ”€ View ย ย  โ”œโ”€โ”€ Assets โ””โ”€โ”€ Main ``` <br/> ## 5. ์‹คํ–‰ ํ™”๋ฉด(๊ธฐ๋Šฅ ์„ค๋ช…) #### โ—พ๏ธ ์ฅฌ์Šค ์ฃผ๋ฌธ ๋ฒ„ํŠผ (์„ฑ๊ณต) ![์ฅฌ์Šค๋ฉ”์ด์ปค๋ฒ„ํŠผํด๋ฆญ](https://user-images.githubusercontent.com/88870642/212242601-a006c81c-e6b1-4020-a8ab-e62be3c00c24.gif) - ๊ฐ ์ฅฌ์Šค ์ฃผ๋ฌธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ณผ์ผ ํ•˜๋‹จ Label์— ์ฅฌ์Šค์˜ ์žฌ๋ฃŒ๋งŒํผ ๊ฐ์†Œํ•œ ์žฌ๊ณ  ๋ณ€๋™์‚ฌํ•ญ์„ ๋ฐ˜์˜ #### โ—พ๏ธ ์ฅฌ์Šค ์ฃผ๋ฌธ ๋ฒ„ํŠผ (์‹คํŒจ) ![์ฅฌ์Šค๋ฉ”์ด์ปค-์–ผ๋Ÿฟ](https://user-images.githubusercontent.com/88870642/212242609-0163ee2a-f9ec-42ca-9ad9-7763f8166c0a.gif) - ์žฌ๊ณ ๊ฐ€ ๋ชจ์ž๋ž„ ๊ฒฝ์šฐ, ์ฃผ๋ฌธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์žฌ๊ณ  ์ˆ˜์ • ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก Alert๋ฅผ ๋„์›Œ์คŒ #### โ—พ๏ธ ํ™”๋ฉด ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ณต์œ  - ๋ฉ”์ธ ํ™”๋ฉด์—์„œ ์ฃผ๋ฌธ ํ›„ ์žฌ๊ณ ์ˆ˜์ •ํ™”๋ฉด์œผ๋กœ ์ด๋™ ![ํ™”๋ฉด๊ฐ„๋ฐ์ดํ„ฐ์ „๋‹ฌ1](https://i.imgur.com/yl8THeQ.gif) - ์žฌ๊ณ ์ˆ˜์ •ํ™”๋ฉด์—์„œ ์žฌ๊ณ  ์ˆ˜์ • ํ›„ ๋ฉ”์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ ![ํ™”๋ฉด๊ฐ„๋ฐ์ดํ„ฐ์ „๋‹ฌ2](https://i.imgur.com/TJnXTE5.gif) <br/> ## 6. ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… ### STEP 1. <details> <summary>๊ณผ์ผ ์žฌ๊ณ  ํ‘œํ˜„ ๋ฐ ์ดˆ๊ธฐ๊ฐ’ ํ• ๋‹น</summary> - 3๋‹จ๊ณ„์— ๊ฑธ์ณ ๊ณผ์ผ ์žฌ๊ณ ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ฐœ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. #### 1. ๊ณผ์ผ ํด๋ž˜์Šค๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•ด์„œ ๊ณผ์ผ/๊ฐœ์ˆ˜ ํ• ๋‹น - ์žฌ๊ณ  ๋ณ€๊ฒฝ ์‹œ, ๊ฐ๊ฐ์˜ ๊ณผ์ผ ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๊นŒ๋‹ค๋กœ์› ์Šต๋‹ˆ๋‹ค. ๋นˆ ๋ฐฐ์—ด์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์„œ ๊ฐ๊ฐ์˜ ๊ณผ์ผ ์ธ์Šคํ„ด์Šค๋“ค์„ ๋ฐฐ์—ด์— ๋„ฃ์–ด์ฃผ์—ˆ๊ณ  ์žฌ๊ณ  ๋ณ€๊ฒฝ์„ ์›ํ•˜๋Š” ๊ณผ์ผ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก findFruit ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€๋กœ ์ •์˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ```swift class FruitStore { class Fruit { let name: String let stock = 10 init(name: String) { self.name = name } } let strawberry = Fruit(name: "๋”ธ๊ธฐ") let banana = Fruit(name: "๋ฐ”๋‚˜๋‚˜") let kiwi = Fruit(name: "ํ‚ค์œ„") let pineapple = Fruit(name: "ํŒŒ์ธ์• ํ”Œ") let mango = Fruit(name: "๋ง๊ณ ") func makeFruitsList() -> [Fruit] { let fruitsList = [self.strawberry, self.banana, self.kiwi, self.pineapple, self.mango] return fruitsList } func findFruit(fruitName: String) -> Fruit? { for i in 0..<makeFruitsList().count { if makeFruitsList()[i].name == fruitName { return makeFruitsList()[i] } } } } ``` <br/> #### 2. Dictionary[String: Int]๋กœ ๊ณผ์ผ/๊ฐœ์ˆ˜ ํ• ๋‹น - ๊ณผ์ผ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด Array์— ๋‹ด์•„์ฃผ๋Š” ๋ฐฉ๋ฒ•์ด ๋ถˆํŽธํ•˜๊ณ , ๊ณผ์ผ ์ธ์Šคํ„ด์Šค ํ”„๋กœํผํ‹ฐ์— ์žฌ๊ณ ๋ฅผ ํ• ๋‹นํ•˜์—ฌ ์žฌ๊ณ ๊ด€๋ฆฌ์— ์–ด๋ ค์›€์„ ๋А๊ผˆ์Šต๋‹ˆ๋‹ค. - ์ข€ ๋” ๊ฐ„๊ฒฐํ•œ ํ‘œํ˜„๊ณผ ์žฌ๊ณ  ์ ‘๊ทผ์˜ ์šฉ์ด์„ฑ์„ ์œ„ํ•ด Array์—์„œ Dictionary๋กœ ์žฌ๊ณ  ํ‘œํ˜„ ๋ฐฉ๋ฒ•์„ ๋ณ€๊ฒฝํ•˜์˜€์Šต๋‹ˆ๋‹ค. ```swift class FruitStore { var fruitsStock = ["๋”ธ๊ธฐ":10, "๋ง๊ณ ":10, "๋ฐ”๋‚˜๋‚˜":10, "ํ‚ค์œ„":10, "ํŒŒ์ธ์• ํ”Œ":10] func subtractInventory(fruit: String, number: Int) throws { guard let selectedStock = self.fruitsStock[fruit], selectedStock - number >= 0 else { throw StockError.outOfStock } fruitsStock[fruit] = selectedStock - number } func addInventory(fruit: String, number: Int) { if let selectedStock = self.fruitsStock[fruit] { fruitsStock[fruit] = selectedStock + number } } func checkStock(fruit: String) { let resultMessage = "\(fruit)์˜ ์žฌ๊ณ ๋Š” \(fruitsStock[fruit])๊ฐœ์ž…๋‹ˆ๋‹ค." } ``` <br/> #### 3. Dictionary[Fruits: Int]๋กœ ๊ณผ์ผ/๊ฐœ์ˆ˜ ํ• ๋‹น - Dictionary Key๊ฐ’์ด String์ผ ๊ฒฝ์šฐ Key๊ฐ’์˜ ๋ฒ”์œ„๊ฐ€ ๋„ˆ๋ฌด ๋„“์–ด ๋ช…์‹œ์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. - ์—ด๊ฑฐํ˜• ํƒ€์ž…์œผ๋กœ Key๊ฐ’์˜ ๋ฒ”์œ„๋ฅผ ํ•œ์ •ํ•˜์—ฌ ๊ฐ€๋…์„ฑ๊ณผ ํƒ€์ž…์˜ ๋ช…์‹œ์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค. ```swift enum Fruits: CaseIterable { case strawberry case mango case pineapple case kiwi case banana } var fruitsStock = [Fruits: Int]() func fillFruitsStock() { for fruit in Fruits.allCases { fruitsStock[fruit] = 10 } } init() { self.fillFruitsStock() } } ``` <br/> </details> ### STEP2. <details> <summary>๊ฐ’ ํƒ€์ž…? ์ฐธ์กฐ ํƒ€์ž…?</summary> #### ์ˆ˜์ • ์ „ - ์‹ฑ๊ธ€ํ†ค ํ˜•ํƒœ์ธ ```FruitStore.shared.fruitsStock``` ํ”„๋กœํผํ‹ฐ๋ฅผ ```fruitsStock```๋ณ€์ˆ˜๋กœ ์„ ์–ธํ–ˆ์Šต๋‹ˆ๋‹ค. - ```FruitStore.shared```์˜ ๊ฒฝ์šฐ class๋กœ ์ฐธ์กฐํƒ€์ž…์ด์ง€๋งŒ, ```FruitStore.shared.fruitsStock```์€ ์ผ๋ฐ˜ ํ”„๋กœํผํ‹ฐ๋กœ ๊ฐ’ํƒ€์ž…์ž…๋‹ˆ๋‹ค. ๊ฐ’ํƒ€์ž… ํ”„๋กœํผํ‹ฐ๋ฅผ ์ƒˆ๋กœ์šด ๋ณ€์ˆ˜์— ํ• ๋‹นํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์žฌ๊ณ  ๋ณ€๊ฒฝ ์‹œ ๊ฐ’์˜ ๋ณต์‚ฌ๊ฐ€ ์ผ์–ด๋‚ฌ์Šต๋‹ˆ๋‹ค. - **Copy on Write**์— ์˜ํ•ด ๋ณ€๊ฒฝ๋˜๊ธฐ ์ „๊นŒ์ง€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฐธ์กฐํ•˜์ง€๋งŒ, ์ €ํฌ ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฒฝ์šฐ ```fruitsStock```์˜ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์—†๋Š” ๋ฌธ์ œ์ ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. ```swift // ์ˆ˜์ • ์ „ final class ViewController: UIViewController { private let juiceMaker = JuiceMaker() private var fruitsStock = FruitStore.shared.fruitsStock override func viewDidLoad() { super.viewDidLoad() displayStock() } func displayStock() { if let strawberryStock = fruitsStock[.strawberry], ... { stockOfStrawberry.text = String(strawberryStock) ... } } @IBAction func orderStrawberryBananaJuice(_ sender: UIButton) { do { try juiceMaker.make(juice: .strawberryBanana) displayStock() } catch ... } } ``` #### ์ˆ˜์ • ํ›„ - ์œ„์˜ ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. - ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ์˜ ํŠน์ง•์€ ์—ฐ์‚ฐ ์‹ค์ œ ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ```getter```์™€ ```setter```๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ ์†์„ฑ๊ณผ ๊ฐ’์„ ๊ฐ„์ ‘์ ์œผ๋กœ ์ฝ๊ณ , ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ๋ฅผ ํ†ตํ•ด ```FruitStore.shared.fruitsStock```์„ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š” ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ```swift // ์ˆ˜์ • ํ›„ final class ViewController: UIViewController { private let juiceMaker = JuiceMaker() private var fruitsStock: [Fruits: Int] { return FruitStore.shared.fruitsStock } override func viewDidLoad() { super.viewDidLoad() displayStock() } func displayStock() { if let strawberryStock = fruitsStock[.strawberry], ... { stockOfStrawberry.text = String(strawberryStock) ... } } @IBAction func orderStrawberryBananaJuice(_ sender: UIButton) { do { try juiceMaker.make(juice: .strawberryBanana) displayStock() } catch ... } } ``` <br/> </details> <details> <summary>ํ™”๋ฉด์ „ํ™˜ ๋ฐฉ๋ฒ•</summary> #### ์ˆ˜์ • ์ „ - ```self.navigationController?.pushViewController(nextVC, animated: true) ```๋ฅผ ํ†ตํ•ด ํ™”๋ฉด ์ „ํ™˜ ์‹œ Navigation ๋ฐฉ์‹์œผ๋กœ ํ™”๋ฉด์ „ํ™˜์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ```swift func moveToChangeStockViewController() { guard let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "ChangeStock") as? ChangeStockViewController else { return } self.navigationController?.pushViewController(nextVC, animated: true) } ``` #### ์ˆ˜์ • ํ›„ - Navigation ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ ์ •๋ณด์˜ ๊นŠ์ด๊ฐ€ ๊นŠ์–ด์งˆ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ธ๋ฐ, ์žฌ๊ณ  ๋ณ€๊ฒฝ ํ™”๋ฉด์€ ๊ธฐ์กด์˜ ์ฅฌ์Šค๋ฅผ ์ฃผ๋ฌธํ•˜๋˜ ํ™”๋ฉด๊ณผ๋Š” ๋‹ค๋ฅธ ๋งฅ๋ฝ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์—ฌ Modal ๋ฐฉ์‹์„ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. - ```pushViewController```๋ฅผ ```present```๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. ```swift func moveToChangeStockViewController() { guard let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "ChangeStock") as? ChangeStockViewController else { return } self.navigationController?.present(nextVC, animated: true) } ``` <br/> </details> <details> <summary>์ฃผ๋ฌธ ๋ฒ„ํŠผ์˜ @IBAction ํ†ตํ•ฉ</summary> #### ์ˆ˜์ • ์ „ * ์ฃผ๋ฌธ ๋ฒ„ํŠผ ๊ฐ๊ฐ์˜ ```@IBAction```์„ ์ •์˜ํ•˜์—ฌ 7๊ฐœ์˜ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ```swift @IBAction func orderStrawberryBananaJuice(_ sender: UIButton) { do { try juiceMaker.make(juice: .strawberryBanana) setSuccessAlert(juice: .strawberryBanana) displayStock() } catch StockError.outOfStock { setFailAlert() } catch { print(error) } } @IBAction func orderPineappleJuice(_ sender: UIButton) { do { try juiceMaker.make(juice: .pineapple) setSuccessAlert(juice: .pineapple) displayStock() } catch StockError.outOfStock { setFailAlert() } catch { print(error) } } @IBAction func orderKiwiJuice(_ sender: UIButton) { // ์œ„์™€ ๋™์ผ, juice == .kiwi } @IBAction func orderBananaJuice(_ sender: UIButton) { // ์œ„์™€ ๋™์ผ, juice == .banana } ... ``` <br/> #### ์ˆ˜์ • ํ›„ * sender๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ฉ”์„œ๋“œ(```identifyJuice(of:)```)์™€ ```order(_:)```๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ```@IBAction```์„ ํ•˜๋‚˜๋งŒ ์ •์˜ํ•˜๊ณ  7๊ฐœ์˜ ๋ฒ„ํŠผ์„ ํ•ด๋‹น Action์— ์—ฐ๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. * ๋ณธ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…์„ ํ†ตํ•ด ๋ฐ˜๋ณต๋œ ์ฝ”๋“œ๋ฅผ ํ˜„์ €ํžˆ ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ```swift @IBAction func pushOrderButton(_ sender: UIButton) { guard let selectedJuice = identifyJuice(of: sender) else { return } order(selectedJuice) } func identifyJuice(of button: UIButton) -> JuiceMaker.Juice? { switch button { case orderStrawberry: return .strawberry case orderBanana: return .banana case orderPineapple: return .pineapple case orderKiwi: return .kiwi case orderMango: return .mango case orderStrawberryBanana: return .strawberryBanana case orderMangoKiwi: return .mangoKiwi default: return nil } } func order(_ juice: JuiceMaker.Juice) { do { try juiceMaker.make(juice: juice) setSuccessAlert(juice: juice) displayStock() } catch StockError.outOfStock { setFailAlert() } catch { print(error) } } ``` </details> ### STEP3. <details> <summary>NavigationBar์˜ Height๋ฅผ ๋Š˜๋ฆด ์ˆ˜ ์—†์„๊นŒ?</summary> ์š”๊ตฌ์‚ฌํ•ญ ํ™”๋ฉด์—๋Š” NavigationBar์˜ ํฌ๊ธฐ๊ฐ€ ์ €ํฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” NavigationBar๋ณด๋‹ค ์ปธ์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž์ถฐ์„œ NavigationBar๋Œ€์‹  View๋ฅผ ์ด์šฉํ•ด์„œ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทผ๋ณธ์ ์ธ ์›์ธ์„ ํ•ด๊ฒฐํ•œ๊ฑฐ ๊ฐ™์ง€ ์•Š์•„์„œ NavigationBar๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ height ๊ฐ’์„ ๋†’์—ฌ์ฃผ๋ ค ํ–ˆ์Šต๋‹ˆ๋‹ค. height๊ฐ’์„ ์˜ฌ๋ ค์ฃผ๊ธฐ ์œ„ํ•ด ์กฐ์‚ฌํ•ด๋ณธ ๊ฒฐ๊ณผ iOS11 ์ด์ƒ๋ถ€ํ„ฐ๋Š” ๋†’์ด ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์„ ํ•  ์ˆ˜ ์—†๋‹ค๋ผ๋Š” ๊ฒฐ๋ก ์ด ๋‚˜์™€ height๊ฐ’์„ ์˜ฌ๋ ค์ฃผ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. </details> <details> <summary>ChangeStockViewController์—์„œ ๋ ˆ์ด๋ธ”์„ ๊ฐ๊ฐ ์ดˆ๊ธฐํ™” or ๋ชจ๋‘ ์ดˆ๊ธฐํ™”</summary> displayStock()๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋ชจ๋“  Label์„ ์ดˆ๊ธฐํ™” ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ํ•˜๋‚˜์˜ Stepper์˜ IBAction์œผ๋กœ ์žฌ๊ณ ์˜ ๋ณ€๊ฒฝ์ด ์žˆ์„ ๋•Œ, ์ „์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ํ•ด๋‹นํ•˜๋Š” ๊ณผ์ผ์˜ ์žฌ๊ณ ๋งŒ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ๋„๋ก pushStepper(_:)๋ฉ”์„œ๋“œ์—์„œ ํ•ด๋‹น Label์„ ์—…๋ฐ์ดํŠธ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ```swift @IBAction func pushStepper(_ sender: UIStepper) { guard let fruitsLabel = identifyRelatedLabel(of: sender), let fruit = identifyRelatedFruit(of: sender) else { return } FruitStore.shared.fruitsStock[fruit] = Int(sender.value) fruitsLabel.text = Int(sender.value).description } ``` </details> <details> <summary>IBOutlet Collection</summary> #### ๋ณ€๊ฒฝ ์ „ ๊ฐ๊ฐ์˜ stepper์— ์ ‘๊ทผํ•˜์—ฌ stepper ์ดˆ๊ธฐํ™”๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ```swift @IBOutlet weak var strawberryStepper: UIStepper! @IBOutlet weak var bananaStepper: UIStepper! @IBOutlet weak var pineappleStepper: UIStepper! @IBOutlet weak var kiwiStepper: UIStepper! @IBOutlet weak var mangoStepper: UIStepper! ``` #### ๋ณ€๊ฒฝ ํ›„ Stepper์˜ ์ดˆ๊ธฐํ™”๋ฅผ ํ•œ ๋ฒˆ์— ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด initializeSteppers()๋ฉ”์„œ๋“œ์—์„œ ๋ชจ๋“  Stepper์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก IBOutlet Collection์— ๋‹ด์•„์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ```swift @IBOutlet weak var strawberryStepper: UIStepper! @IBOutlet weak var bananaStepper: UIStepper! @IBOutlet weak var pineappleStepper: UIStepper! @IBOutlet weak var kiwiStepper: UIStepper! @IBOutlet weak var mangoStepper: UIStepper! @IBOutlet var steppers: [UIStepper]! ``` </details> <details> <summary>stepper ์ดˆ๊ธฐํ™” ์œ„์น˜</summary> #### ๋ณ€๊ฒฝ ์ „ stepper์˜ ๊ฐ’์„ ํ˜„์žฌ ๊ณผ์ผ ์žฌ๊ณ  ์ˆ˜๋Ÿ‰์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด @IBAction func pushStepper(_ sender: UIStepper)์—์„œ stepper.value๊ฐ’์„ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ@IBAction func pushStepper(_ sender: UIStepper)๋กœ ์ดˆ๊ธฐํ™”ํ•  ๊ฒฝ์šฐ stepper๋ฅผ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ์ดˆ๊ธฐํ™”๋˜๋Š” ๋ฌธ์ œ์ ์ด ๋ฐœ๊ฒฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค ```swift @IBAction func pushStepper(_ sender: UIStepper) { initializeSteppers() guard let fruitsLabel = identifyRelatedLabel(of: sender), let fruit = identifyRelatedFruit(of: sender) else { return } FruitStore.shared.fruitsStock[fruit] = Int(sender.value) fruitsLabel.text = Int(sender.value).description } ``` #### ๋ณ€๊ฒฝ ํ›„ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ViewController๊ฐ€ ์ดˆ๊ธฐํ™”๋  ๋•Œ, ์ฆ‰ viewDidLoad์— stepper.value๋ฅผ ํ•จ๊ป˜ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ```swift override func viewDidLoad() { super.viewDidLoad() displayStock() initializeSteppers() setUpNavigationBar() } ``` </details> </details> <details> <summary>delegate ํ• ๋‹น ์‹œ๊ธฐ</summary> #### ์ˆ˜์ • ์ „ - ์ดˆ๊ธฐ `ChangeStockViewController`์˜ `delegate` ํ”„๋กœํผํ‹ฐ์— `MainViewController`๋ฅผ ํ• ๋‹นํ•˜๋Š” ์œ„์น˜๋Š” `viewDidLoad()`์˜€์Šต๋‹ˆ๋‹ค. - `ChangeStockViewController`์—์„œ `delegate`๋ฅผ ํ†ตํ•ด Main ํ™”๋ฉด์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์˜€์œผ๋‚˜ `delegate`์— `MainViewController`๊ฐ€ ํ• ๋‹น๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. - ํ™”๋ฉด ์ „ํ™˜ ์‹œ ์ƒ์„ฑํ•œ `ChangeStockViewController` ์ธ์Šคํ„ด์Šค์™€ `viewDidLoad()`์—์„œ ์ƒ์„ฑํ•œ `ChangeStockViewController`์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค. ```swift override func viewDidLoad() { super.viewDidLoad() guard let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "ChangeStock") as? ChangeStockViewController else { return } nextVC.delegate = self displayStock() } private func moveToChangeStockViewController() { guard let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "ChangeStock") as? ChangeStockViewController else { return } self.navigationController?.present(nextVC, animated: true) } ``` #### ์ˆ˜์ • ํ›„ - `delegate`ํ• ๋‹น ์‹œ๊ธฐ๋ฅผ `viewDidLoad`์—์„œ ํ™”๋ฉด์ „ํ™˜ ๋ฉ”์„œ๋“œ์ธ `moveToChangeStockViewController()`๋กœ ์ˆ˜์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. - ํ™”๋ฉด์ „ํ™˜ ์‹œ ์ƒ์„ฑํ•œ ์ธ์Šคํ„ด์Šค์˜ `delegate`์— ์ •์ƒ์ ์œผ๋กœ `MainViewController`๊ฐ€ ํ• ๋‹น๋˜์–ด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ```swift private func moveToChangeStockViewController() { guard let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "ChangeStock") as? ChangeStockViewController else { return } nextVC.delegate = self self.navigationController?.present(nextVC, animated: true) } ``` </details> ## 7. ์ฐธ๊ณ  ๋งํฌ - [Nested Type - Swift Language Guide](https://docs.swift.org/swift-book/LanguageGuide/NestedTypes.html) - [Properties; Computed Property - Swift Language Guide](https://docs.swift.org/swift-book/LanguageGuide/Properties.html) - [NavigationController - Swift Language Guide](https://developer.apple.com/documentation/uikit/uinavigationcontroller) - [Type Casting - Swift Language Guide](https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html ) - [Error Handling - Swift Language Guide](https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html) - [Protocol - Swift Laguage Guide](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html) <br/>