Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

쥬스메이커 [STEP 2] yoshikim, Marco #85

Merged
merged 26 commits into from
Jun 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
905c3f0
docs : Readme.md 파일 생성 및 step1브런치 생성
Marcorable Jun 8, 2021
246a02a
feat : enum쥬스타입 구현
taeyoung-Kim-KR Jun 8, 2021
e68220f
feat : 쥬스메이커 타입 구현
taeyoung-Kim-KR Jun 9, 2021
c5e76a3
feat : FruitStore 클래스 currentStock메소드 생성
Marcorable Jun 9, 2021
a6d8205
feat : FruitStore 클래스 안에 changeStock 메소드 생성
taeyoung-Kim-KR Jun 9, 2021
2eb3ab8
feat : JuiceMaker 구조체 구현
Marcorable Jun 9, 2021
12158b4
fix : Step 1 PR 리뷰 관련 변경사항 적용
Marcorable Jun 10, 2021
56adcec
refactor : step_1 FruitStore 프로퍼티 타입 및 함수 변경
taeyoung-Kim-KR Jun 14, 2021
2a1a6b0
refactor : JuiceMaker.checkStock 재료 처리 방식 변경
Marcorable Jun 14, 2021
dbb4679
feat : 쥬스메이커에러 타입 생성
taeyoung-Kim-KR Jun 14, 2021
ca4ff2a
feat : JuiceMaker.makeJuice 함수 에러처리 형태로 변경
Marcorable Jun 14, 2021
6b7480c
feat : currentStock 에러 처리
taeyoung-Kim-KR Jun 14, 2021
202411e
feat : UI 오타 및 내비게이션 제목추가
Marcorable Jun 14, 2021
b1381d5
feat : 수정 뷰 컨트롤러 생성 및 UI 수정
taeyoung-Kim-KR Jun 14, 2021
3c43ff9
feat : 하나의 IBAction에서 UIButton 이용해서 Juice case 얻기 구현
Marcorable Jun 14, 2021
a5e679d
feat : Alert 생성
taeyoung-Kim-KR Jun 14, 2021
02a17b7
docs : 열거체 코드만 모아놓은 파일생성
Marcorable Jun 15, 2021
dcf3513
feat : NotificationCenter 생성
taeyoung-Kim-KR Jun 15, 2021
4aa4ae5
feat : 화면 Label 업데이트 하는 updateUILabel 함수 구현
Marcorable Jun 15, 2021
f37d256
refactor : 변수 및 함수명 수정
Marcorable Jun 15, 2021
b978ab9
refactor : 불필요한 줄바꿈 및 공백 정리
Marcorable Jun 15, 2021
fe8d284
refactor : 타입 변경 및 수정
taeyoung-Kim-KR Jun 18, 2021
f58c0a4
feat : 쥬스 열거체 객체화 시도
Marcorable Jun 18, 2021
e7b1e5a
feat : enum 타입의 Juice를 구조체로 변경
Marcorable Jun 18, 2021
5646fe1
feat : 버튼 구별 방식 변경
taeyoung-Kim-KR Jun 18, 2021
45f0d2f
feat : KVO 형태로 변화값 적용
Marcorable Jun 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions JuiceMaker/JuiceMaker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@
objects = {

/* Begin PBXBuildFile section */
B463F9C3267796BA0073F37A /* ModifiyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B463F9C2267796BA0073F37A /* ModifiyViewController.swift */; };
C71CD66B266C7ACB0038B9CB /* FruitStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71CD66A266C7ACB0038B9CB /* FruitStore.swift */; };
C73DAF37255D0CDD00020D38 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73DAF36255D0CDD00020D38 /* AppDelegate.swift */; };
C73DAF39255D0CDD00020D38 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73DAF38255D0CDD00020D38 /* SceneDelegate.swift */; };
C73DAF3B255D0CDD00020D38 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73DAF3A255D0CDD00020D38 /* ViewController.swift */; };
C73DAF3B255D0CDD00020D38 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73DAF3A255D0CDD00020D38 /* MainViewController.swift */; };
C73DAF3E255D0CDD00020D38 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C73DAF3C255D0CDD00020D38 /* Main.storyboard */; };
C73DAF40255D0CDE00020D38 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C73DAF3F255D0CDE00020D38 /* Assets.xcassets */; };
C73DAF43255D0CDF00020D38 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C73DAF41255D0CDF00020D38 /* LaunchScreen.storyboard */; };
C73DAF4C255D0D0400020D38 /* JuiceMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73DAF4B255D0D0400020D38 /* JuiceMaker.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
B463F9C2267796BA0073F37A /* ModifiyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifiyViewController.swift; sourceTree = "<group>"; };
C71CD66A266C7ACB0038B9CB /* FruitStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FruitStore.swift; sourceTree = "<group>"; };
C73DAF33255D0CDD00020D38 /* JuiceMaker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JuiceMaker.app; sourceTree = BUILT_PRODUCTS_DIR; };
C73DAF36255D0CDD00020D38 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
C73DAF38255D0CDD00020D38 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
C73DAF3A255D0CDD00020D38 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
C73DAF3A255D0CDD00020D38 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
C73DAF3D255D0CDD00020D38 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
C73DAF3F255D0CDE00020D38 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C73DAF42255D0CDF00020D38 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
Expand All @@ -46,7 +48,8 @@
children = (
C73DAF36255D0CDD00020D38 /* AppDelegate.swift */,
C73DAF38255D0CDD00020D38 /* SceneDelegate.swift */,
C73DAF3A255D0CDD00020D38 /* ViewController.swift */,
C73DAF3A255D0CDD00020D38 /* MainViewController.swift */,
B463F9C2267796BA0073F37A /* ModifiyViewController.swift */,
);
path = Controller;
sourceTree = "<group>";
Expand Down Expand Up @@ -168,9 +171,10 @@
buildActionMask = 2147483647;
files = (
C71CD66B266C7ACB0038B9CB /* FruitStore.swift in Sources */,
C73DAF3B255D0CDD00020D38 /* ViewController.swift in Sources */,
C73DAF3B255D0CDD00020D38 /* MainViewController.swift in Sources */,
C73DAF37255D0CDD00020D38 /* AppDelegate.swift in Sources */,
C73DAF39255D0CDD00020D38 /* SceneDelegate.swift in Sources */,
B463F9C3267796BA0073F37A /* ModifiyViewController.swift in Sources */,
C73DAF4C255D0D0400020D38 /* JuiceMaker.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
119 changes: 119 additions & 0 deletions JuiceMaker/JuiceMaker/Controller/MainViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// JuiceMaker - ViewController.swift
// Created by yagom.
// Copyright © yagom academy. All rights reserved.
//

import UIKit

class MainViewController: UIViewController {
@IBOutlet weak var strawberryLabel: UILabel!
@IBOutlet weak var bananaLabel: UILabel!
@IBOutlet weak var mangoLabel: UILabel!
@IBOutlet weak var kiwiLabel: UILabel!
@IBOutlet weak var pineappleLabel: UILabel!

let juiceMaker = JuiceMaker()
var observations = [NSKeyValueObservation]()

let strawberryJuice = Juice(name: "딸기쥬스", ingredients: [.strawberry:16])
let bananaJuice = Juice(name: "바나나쥬스", ingredients: [.banana:2])
let pineappleJuice = Juice(name: "파인애플쥬스", ingredients: [.pineapple:2])
let kiwiJuice = Juice(name: "키위쥬스", ingredients: [.kiwi:3])
let mangoJuice = Juice(name: "망고쥬스", ingredients: [.mango:3])
let strawberryBananaJuice = Juice(name: "딸바쥬스", ingredients: [.strawberry:10, .banana:1])
let mangoKiwiJuice = Juice(name: "망키쥬스", ingredients: [.mango:2, .kiwi:1])

override func viewDidLoad() {
super.viewDidLoad()

// NotificationCenter.default.addObserver(self, selector: #selector(alertMakingJuiceSuccess(_:)),
// name: Notification.Name(rawValue: "makeJuiceSuccess"), object: nil)
// NotificationCenter.default.addObserver(self, selector: #selector(alertMakingJuiceFail),
// name: Notification.Name(rawValue: "makeJuiceFail"), object: nil)

observations = [
juiceMaker.fruitStore.observe(\.strawberry, options: [.new]) { _, _ in
self.updateUILabel(.strawberry)
},
juiceMaker.fruitStore.observe(\.banana, options: [.new]) { _, _ in
self.updateUILabel(.banana)
},
juiceMaker.fruitStore.observe(\.pineapple, options: [.new]) { _, _ in
self.updateUILabel(.pineapple)
},
juiceMaker.fruitStore.observe(\.kiwi, options: [.new]) { _, _ in
self.updateUILabel(.kiwi)
},
juiceMaker.fruitStore.observe(\.mango, options: [.new]) { _, _ in
self.updateUILabel(.mango)
}
]

for fruit in Fruit.allCases {
updateUILabel(fruit)
}
}

@IBAction func strawberryJuiceOrder(_ sender: Any) {
juiceMaker.makeJuice(strawberryJuice)
}
@IBAction func bananaJuiceOrder(_ sender: Any) {
juiceMaker.makeJuice(bananaJuice)
}
@IBAction func mangoJuiceOrder(_ sender: Any) {
juiceMaker.makeJuice(pineappleJuice)
}
@IBAction func kiwiJuiceOrder(_ sender: Any) {
juiceMaker.makeJuice(kiwiJuice)
}
@IBAction func pineappleJuiceOrder(_ sender: Any) {
juiceMaker.makeJuice(pineappleJuice)
}
@IBAction func strawberryBananaJuiceOrder(_ sender: Any) {
juiceMaker.makeJuice(strawberryBananaJuice)
}
@IBAction func mangoKiwiJuiceOrder(_ sender: Any) {
juiceMaker.makeJuice(mangoKiwiJuice)
}
}

extension MainViewController {
func updateUILabel(_ fruit: Fruit) {
let currentStock = String(juiceMaker.fruitStore.currentStock(fruit))
switch fruit {
case .strawberry:
strawberryLabel.text = currentStock
case .banana:
bananaLabel.text = currentStock
case .pineapple:
pineappleLabel.text = currentStock
case .kiwi:
kiwiLabel.text = currentStock
case .mango:
mangoLabel.text = currentStock
}
}
@objc func alertMakingJuiceSuccess(_ notification: Notification) {
guard let userInfo = notification.userInfo else {
print("userInfo 에러"); return
}
guard let userInfoValue = userInfo["쥬스이름"], let juiceName = userInfoValue as? String else {
print("userInfoValue 에러"); return
}
Comment on lines +98 to +103
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notification을 통해서 객체의 상태값을 받고있어서 옵셔널 언래핑이나 타입 체크 코드가 필요한 상황으로 보이는데,
notification이 아닌 다른 방식으로 구현한다면 이런 guard문은 없어져도 되는 코드가 되겠네요!

그리고 guard문은 원하는 조건이 아닐 때 해당 블럭을(지금으로 치면 함수겠죠?) 다 타지 않고 리턴해버려야 할 때 주로 쓰게되는데요
현재 구조에서는 userInfo값의 오류때문에 에러처리를 할 필요는 없어보입니다!

let alert = UIAlertController(title: "\(juiceName) 쥬스 나왔습니다! 맛있게 드세요!", message: nil, preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "감사합니다.", style: .default)
alert.addAction(confirmAction)
present(alert, animated: true, completion: nil)
}
@objc func alertMakingJuiceFail() {
let alert = UIAlertController(title: "재료가 모자라요. 재고를 수정할까요?", message: nil, preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "예", style: .default){ _ in

}
let cancelAction = UIAlertAction(title: "아니오", style: .default)
alert.addAction(confirmAction)
alert.addAction(cancelAction)
present(alert, animated: true, completion: nil)
}
}
17 changes: 17 additions & 0 deletions JuiceMaker/JuiceMaker/Controller/ModifiyViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// ModifiyViewController.swift
// JuiceMaker
//
// Created by 김태영 on 2021/06/14.
//

import UIKit

class ModifiyViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

}

}
18 changes: 0 additions & 18 deletions JuiceMaker/JuiceMaker/Controller/ViewController.swift

This file was deleted.

50 changes: 49 additions & 1 deletion JuiceMaker/JuiceMaker/Model/FruitStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,55 @@

import Foundation

enum Fruit: CaseIterable {
case strawberry
case banana
case pineapple
case kiwi
case mango
}
// 과일 타입
class FruitStore {
class FruitStore: NSObject {
@objc dynamic var strawberry = 10
@objc dynamic var banana = 10
@objc dynamic var pineapple = 10
@objc dynamic var kiwi = 10
@objc dynamic var mango = 10

subscript(_ fruit: Fruit) -> Int {
get {
switch fruit {
case .strawberry:
return strawberry
case .banana:
return banana
case .pineapple:
return pineapple
case .kiwi:
return kiwi
case .mango:
return mango
}
}
set {
switch fruit {
case .strawberry:
strawberry = newValue
case .banana:
banana = newValue
case .pineapple:
pineapple = newValue
case .kiwi:
kiwi = newValue
case .mango:
mango = newValue
}
}
}
func currentStock(_ fruit: Fruit) -> Int {
return self[fruit]
}
func changeStock(_ fruit: Fruit, _ changingQuantity: Int) {
self[fruit] = currentStock(fruit) - changingQuantity
}
}
32 changes: 30 additions & 2 deletions JuiceMaker/JuiceMaker/Model/JuiceMaker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,35 @@

import Foundation

// 쥬스 메이커 타입
enum JuiceMakerError: Error {
case outOfStock
}

struct Juice {
var name: String
var ingredients: Dictionary<Fruit, Int>
}
// 쥬스 메이커 타입
struct JuiceMaker {

let fruitStore = FruitStore()

func makeJuice(_ juice: Juice) {
do {
try checkStock(juice.ingredients)

for (fruit, removingQuantity) in juice.ingredients {
fruitStore.changeStock(fruit, removingQuantity)
}
// NotificationCenter.default.post(name: Notification.Name(rawValue: "makeJuiceSuccess"), object: nil, userInfo: ["쥬스이름": juice.description])
} catch {
// NotificationCenter.default.post(name: Notification.Name(rawValue: "makeJuiceFail"), object: nil)
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if - else 는 두 가지 상황을 주어쥐고 상황에 맞게 동작하는 느낌이 있는 반면

makeJuice() 는 주스만들기 동작이 주 기능이고 주스만들기 실패(재고없음) 인 경우가 특수한경우 또는 오류의 개념으로 접근해서 do-catch 형태로 구현하고 Error Handling을 적용해 보았습니다.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러핸들링을 이용해서 동작의 흐름을 제어하는 구현을 하신 점 좋습니다 👍
다만, notification을 통해서 makeJuice()의 결과를 전달하는 건 좋지 않은 방법같습니다.
성공/실패 결과에 따라서 다음 액션을 해야하는 주체에게 notification이 아닌 방식으로 전달하는 방법을 생각해보세요🧐

func checkStock(_ ingredients: Dictionary<Fruit, Int>) throws {
for (fruit, removingQuantity) in ingredients {
if fruitStore.currentStock(fruit) < removingQuantity {
throw JuiceMakerError.outOfStock
}
}
}
}
Loading