# ๐ง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
โย ย โโโ Controller
โย ย โย ย โโโ AppDelegate
โย ย โย ย โโโ ChangeStockViewController
โย ย โย ย โโโ MainViewController
โย ย โย ย โโโ SceneDelegate
โโโ Model
โย ย โโโ Error
โย ย โโโ FruitStore
โย ย โย ย โโโ Fruits
โย ย โโโ JuiceMaker
โย ย
โโโ View
ย ย โโโ Assets
โโโ Main
```
<br/>
## 5. ์คํ ํ๋ฉด(๊ธฐ๋ฅ ์ค๋ช
)
#### โพ๏ธ ์ฅฌ์ค ์ฃผ๋ฌธ ๋ฒํผ (์ฑ๊ณต)

- ๊ฐ ์ฅฌ์ค ์ฃผ๋ฌธ ๋ฒํผ์ ๋๋ฅด๋ฉด ๊ณผ์ผ ํ๋จ Label์ ์ฅฌ์ค์ ์ฌ๋ฃ๋งํผ ๊ฐ์ํ ์ฌ๊ณ ๋ณ๋์ฌํญ์ ๋ฐ์
#### โพ๏ธ ์ฅฌ์ค ์ฃผ๋ฌธ ๋ฒํผ (์คํจ)

- ์ฌ๊ณ ๊ฐ ๋ชจ์๋ ๊ฒฝ์ฐ, ์ฃผ๋ฌธ ๋ฒํผ์ ๋๋ฅด๋ฉด ์ฌ๊ณ ์์ ํ๋ฉด์ผ๋ก ์ด๋ํ ์ ์๋๋ก Alert๋ฅผ ๋์์ค
#### โพ๏ธ ํ๋ฉด ๊ฐ ๋ฐ์ดํฐ ๊ณต์
- ๋ฉ์ธ ํ๋ฉด์์ ์ฃผ๋ฌธ ํ ์ฌ๊ณ ์์ ํ๋ฉด์ผ๋ก ์ด๋

- ์ฌ๊ณ ์์ ํ๋ฉด์์ ์ฌ๊ณ ์์ ํ ๋ฉ์ธ ํ๋ฉด์ผ๋ก ์ด๋

<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/>