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

[iOS] ViewModel 구현(#20) #21

Merged
merged 14 commits into from
Apr 26, 2021
Merged

[iOS] ViewModel 구현(#20) #21

merged 14 commits into from
Apr 26, 2021

Conversation

1song2
Copy link
Collaborator

@1song2 1song2 commented Apr 23, 2021

  • 구현된 코드를 ViewModel로 수정

@JacksonPk - 예제코드를 보고 ViewModel을 구현해보았습니다.
아직 엉성한 부분이 많지만 일단 코드 보실 수 있게 PR 드려요.
저도 주말동안 좀더 공부하고 개선할 부분 있으면 수정해서 iOS/feat/view-model 브랜치에 반영한 후,
다시 PR 드리겠습니다.

그럼 즐거운 금요일 밤 & 주말 되시길 바래요🔥

@1song2 1song2 added the iOS iOS 관련 이슈 사항 label Apr 23, 2021
@1song2 1song2 requested a review from JacksonPk April 23, 2021 12:34
Copy link
Collaborator

@JacksonPk JacksonPk left a comment

Choose a reason for hiding this comment

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

하루정도 지나니 이제 몸이 좀 괜찮아진것 같습니다.
내일 좀 더 공부해볼게요. 이번주도 수고하셨습니다 쏭!!
댓글 달아주시면 머지하구 내일 저도 이 브랜치로 받아서 다시한번 확인해볼게요!!

private var allDishes: [DishList]

init(allDishes: [DishList]) {
self.allDishes = allDishes
super.init()
}

convenience init(mainDishes: [Dish], soupDishes: [Dish], sideDishes: [Dish]) {
self.init(allDishes: [DishList(category: .main, dishes: mainDishes),
DishList(category: .soup, dishes: soupDishes),
DishList(category: .side, dishes: sideDishes)])
}

func load(dishes: DishList) {
allDishes.enumerated().forEach { (index, dishList) in
if dishList.category == dishes.category {
allDishes[index].dishes = dishes.dishes
}
}
}
var viewModels: [DishesListViewModel]!
Copy link
Collaborator

Choose a reason for hiding this comment

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

MVVM구조를 통해서 dataSource에서 viewModel를 선언하면 sectionNumber()등을 지정할 수 있군요!
훨씬 간결해진것 같습니다.

Comment on lines -24 to -34

func getSectionTitle() -> String {
switch self {
case .main:
return "모두가 좋아하는 든든한 메인요리"
case .soup:
return "정성이 담긴 뜨끈뜨끈 국물요리"
case .side:
return "식탁을 풍성하게 하는 정갈한 밑반찬"
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Model에서는 구조만! 저도 헷갈렸던 부분인데 이 부분을 ViewModel에서 처리해주셨네요!

Comment on lines 21 to 29
let mainViewModel = DefaultDishesListViewModel()
let soupViewModel = DefaultDishesListViewModel()
let sideViewModel = DefaultDishesListViewModel()
mainViewModel.category.value = "main"
soupViewModel.category.value = "soup"
sideViewModel.category.value = "side"
mainViewModel.load()
soupViewModel.load()
sideViewModel.load()
Copy link
Collaborator

Choose a reason for hiding this comment

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

3개의 변수를 설정해서 각각 load를 지정해 주셧네요.
24~26 line에 직접 값을 정해주는 것 말고 다른 방법이 있을지와
3가지 메뉴 이상이면 어떻게 처리하는 방법이 있을까요? 저도 아직 기초가 약해서 좋은 방법이 있는지 모르겠네요.
배열을 써도 결국은 추가를 직접해야할것 같기두하구... 😕

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

저도 작성하면서 이 부분이 가장 신경 쓰였던거 같아요. main, soup, side를 아우르는 상위의 ViewModel을 만들어야 하는건가 고민입니다🤔 (DishedListViewModel이 DishesListItemViewModel을 포함하는 것처럼) 오늘 좀더 공부하면서 고민해볼게요!

Comment on lines 10 to 16
public final class Observable<Value> {

struct Observer<Value> {
weak var observer: AnyObject?
let block: (Value) -> Void
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

이 Observalble 파일은 의도하는 내용이 이해가 되질 않네요 ㅜㅜ
어떤 것을 공부해야 이해하기 쉬울까요?
구글링을 하니까 RxSwift이 연관검색이어인데, 그러면 cocoapod로 설치를 하신건가요??
키워드를 알려주시면 감사하겠습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Observable 같은 경우에는 clean architecture 예제 코드에 있는 걸 그대로 복붙해온거라 저도 완벽하게 이해하고 있진 않지만!
일단 따로 라이브러리나 패키지를 설치한 것은 아닙니다.
View와 ViewModel을 binding하기 위한 util 코드인거 같아요. 비슷한 역할을 하는 걸로는 combine이 있는 거 같습니다.
계속계속 MVVM 공부하면서 좀더 잘 이해하게 되면 내용 바로 공유드릴게요!

Comment on lines 10 to 16
struct DishesListItemViewModel {
let name: String
let description: String
let imageURL: String
let prices: [Int]
let badges: [String]
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Entity의 Dish.swift와 비교해보았는데 변수가 거의 같네요.
이렇게보니 Model과 ViewModel의 차이를 아직 잘 모르겠네요. 더 공부해봐야겠습니다 ㅜㅜ
그리고 id가 존재하질 않아서 그런데 그 이유가 있으신지 궁금하네요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵 Entity와 ViewModel의 내부 속성이 겹치는 것에 대해서는 저도 좀 의문이네요🤔
MVVM이 관심사 분리는 잘되는 한편 개인적으로는 좀 중복되는 코드들이 많아 보여요.
id 값은 현재 상태에서는 사용되는 곳이 없어서 일단은 넣지 않았습니다!

Comment on lines 26 to 37
var sectionTitle: String? {
switch category.value {
case "main":
return "모두가 좋아하는 든든한 메인요리"
case "soup":
return "정성이 담긴 뜨끈뜨끈 국물요리"
case "side":
return "식탁을 풍성하게 하는 정갈한 밑반찬"
default:
return nil
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

이곳에서 설정을 해주신거군요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵넵 Entity나 DataSource 보다는 나은거 같아서 이쪽으로 넣어주었습니다!

Comment on lines 14 to 19
protocol DishesListViewModelOutput {
var category: Observable<String> { get }
var sectionTitle: String? { get }
var dishes: Observable<[DishesListItemViewModel]> { get }
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

이쪽 부분을 프로토콜로 만들어서 DefaultDishesListViewModel에서 사용했네요.
확장성에서 좋은 부분인거같아요!

static let identifier = "DishCell"
static let reuseIdentifier = String(describing: DishCell.self)
Copy link
Collaborator

Choose a reason for hiding this comment

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

엇! 이 identifier 부분을 String으로 구현하셨네요. extenstion으로 하신건가요? 아니면 어떻게 구현하신건가요
엄청 신기하네요!!

Copy link
Collaborator Author

@1song2 1song2 Apr 25, 2021

Choose a reason for hiding this comment

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

그죵 신기하죠ㅎㅎ
extension은 아니고 String(describing:) 형식으로 작성하면 describing 뒤에 오는 instance 값을 String으로 반환해주는 것 같아요. 참고링크

특히 저희처럼 String(describing: DishCell.self) 이렇게 작성하면 타입에 대한 String을 반환해주어서 "DishCell"이 나오더라구요.
결국 static let reuseIdentifier = "DishCell" 이렇게 작성하는 것과 결과는 같지만 그냥 String 값을 넣어주는 것보단 안전한 방법일 것 같아 한번 적용해보았습니다!

Comment on lines 33 to 50
func fill(with viewModel: DishesListItemViewModel) {
self.viewModel = viewModel

updateThumbImage()
nameLabel.text = viewModel.name
descriptionLabel.text = viewModel.description
let prices = viewModel.prices
let originalPrice = prices[0]
if prices.count > 1 {
let discountPrice = prices[1]
originalPriceLabel.attributedText = "\(originalPrice)원".strikethrough()
discountPriceLabel.text = "\(discountPrice)원"
} else {
discountPriceLabel.text = "\(originalPrice)원"
originalPriceLabel.text = ""
}
let badges = viewModel.badges
if badges.count > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기서 viewModel에서 저장한 image를 Thumbnail에 뿌려주면서 타이틀, 가격등을 채우는 함수네요. 신기합니다.
이 부분도 잘 보게씁니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵 원래 DataSource의 cellForItemAt 메소드 안에 들어 있던 코드인데 따로 빼서 cell로 옮겼습니다!

Copy link
Collaborator

@JacksonPk JacksonPk left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!!!!

@@ -26,6 +26,6 @@ class SectionHeaderView: UICollectionReusableView {

func fill(with viewModel: DishesListViewModel) {
self.viewModel = viewModel
sectionTitleLabel.text = viewModel.sectionTitle
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿!!!

@JacksonPk JacksonPk merged commit 5a5ad22 into dev-iOS Apr 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
iOS iOS 관련 이슈 사항
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants