Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 리펙터링
- 리펙토링
- SWIFT
- MVVM
- ios
- clean architecture
- ribs
- Protocol
- 스위프트
- UICollectionView
- Refactoring
- Human interface guide
- map
- RxCocoa
- collectionview
- tableView
- 리팩토링
- rxswift
- uitableview
- 클린 코드
- swift documentation
- swiftUI
- Xcode
- combine
- 애니메이션
- uiscrollview
- Clean Code
- UITextView
- Observable
- HIG
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - SwiftUI] SwiftUI+MVVM (SwiftUI에서 MVVM 사용 방법, View, ViewModel) 본문
iOS 응용 (SwiftUI)
[iOS - SwiftUI] SwiftUI+MVVM (SwiftUI에서 MVVM 사용 방법, View, ViewModel)
jake-kim 2022. 9. 27. 23:50MVVM
핵심은 View와 ViewModel이고 각 역할을 기억
- View: ViewModel에서 상태가 변하면 그 상태를 단순히 구독하고 있다가 View를 변경하는 역할
- ViewModel: 상태 값을 저장하고 있고, 상태 값을 관리(계산 등)를 하는 역할
View와 ViewModel 구현 핵심
- View에서 특정 UI의 action이 발생하면 ViewModel에 던져줌
- ViewModel에서는 액션에 따라 특정 상태값을 관리하고 상태값을 변경
- ViewModel의 상태값을 바라보고 있는 View는 그에 맞추어서 UI 변경
주의사항) 상태 관리 포인트는 View가 아니라 ViewModel이므로, 상태 관련 코드는 ViewModel 한 곳에서 수행되도록 할 것
View, ViewModel 구현 아이디어
- enum을 사용하여, viewModel의 명확한 상태를 보여주도록 구현
- ViewModel의 공통적인 형태를 정의해주기 위해서 ViewModelable이라는 프로토콜을 정의
// ViewModelable.swift
import SwiftUI
import Combine
protocol ViewModelable: ObservableObject {
associatedtype Action
associatedtype State
var state: State { get }
func action(_ action: Action)
}
- ViewModel에서는 enum타입으로 Action과 State를 정의하고, View에서는 해당 state를 바라보고 있다가 적절히 뷰를 변경하는 것
- View -> ViewModel: action(_:)함수로 전달
- ViewModel -> View: 뷰쪽에서 state를 바라보고 있다가 업데이트
예제로 구현할 뷰) Counter
ViewModel 구현
- ViewModelable을 준수하도록 구현
import SwiftUI
import Combine
final class CounterViewModel: ViewModelable {
// MARK: Types
enum Action {
}
enum State {
}
// MARK: Properties
@Published var state: State
// MARK: Initailizer
init() {
}
// MARK: Action
func action(_ action: Action) {
}
}
- State 정의
- count 값 (뷰에 표출될 값)
enum State {
case count(Int)
}
- action 정의
- View에서 Add버튼과 Subtract버튼이 있으므로 두 가지 상태 정의
enum Action {
case onTapAddButton
case onTapSubtractButton
}
- 위 action에 따라 상태를 변경해줄 action 함수 구현
func action(_ action: Action) {
switch action {
case .onTapAddButton:
state = .count(getCurrnetCount() + 1)
case .onTapSubtractButton:
state = .count(getCurrnetCount() - 1)
}
}
private func getCurrnetCount() -> Int {
guard case let .count(int) = state else { return 0 }
return int
}
- 구현 완료된 CounterViewModel
import SwiftUI
import Combine
final class CounterViewModel: ViewModelable {
// MARK: Types
enum Action {
case onTapAddButton
case onTapSubtractButton
}
enum State {
case count(Int)
}
// MARK: Properties
@Published var state: State
// MARK: Initailizer
init() {
state = .count(0)
}
// MARK: Action
func action(_ action: Action) {
switch action {
case .onTapAddButton:
state = .count(getCurrnetCount() + 1)
case .onTapSubtractButton:
state = .count(getCurrnetCount() - 1)
}
}
private func getCurrnetCount() -> Int {
guard case let .count(int) = state else { return 0 }
return int
}
}
View 구현
- View는 @ObservedObject로 viewModel을 가지고 있는 형태
- View 관련 상태는 모두 viewModel에서 관리하도록 구현 (상태 관리는 ViewModel 한 곳에서만하여 관리포인트를 한곳으로 제한)
import SwiftUI
import Combine
struct CounterView: View {
// MARK: Properties
@ObservedObject var viewModel: CounterViewModel
// MARK: UI
var body: some View {
}
}
- contentView를 따로 놓고 이곳에서 viewModel의 상태에 따라 뷰를 변경하도록 구현
import SwiftUI
import Combine
struct CounterView: View {
// MARK: Properties
@ObservedObject var viewModel: CounterViewModel
// MARK: UI
var body: some View {
NavigationView {
self.contentView
.navigationTitle(Text("Counter 화면"))
}
}
@ViewBuilder
private var contentView: some View {
// TODO
}
}
- contentView에서 viewModel의 상태에 따라 뷰 적용
@ViewBuilder
private var contentView: some View {
switch viewModel.state {
case let .count(int):
VStack(alignment: .center, spacing: 20) {
getCountView(count: int)
HStack(alignment: .center, spacing: 50) {
getSubtractButtonView()
getAddButtonView()
}
}
}
}
- 위에서 호출하는 getCountView(count:), getAddButtonView(), getSubtractButtonView() 메소드 구현
@ViewBuilder
private func getSubtractButtonView() -> some View {
Button("-") {
viewModel.action(.onTapSubtractButton)
}
.font(.largeTitle)
}
@ViewBuilder
private func getAddButtonView() -> some View {
Button("+") {
viewModel.action(.onTapAddButton)
}
.font(.largeTitle)
}
@ViewBuilder
private func getCountView(count: Int) -> some View {
Text("\(count)")
.font(.title)
}
(완성)
'iOS 응용 (SwiftUI)' 카테고리의 다른 글
Comments