MVVM = Model, View, View Model
1. Model
데이터 저장 구조체이다. 크로스핏 운동 어플을 만든다고 가정하면,
enum ExerciseType {
case weighted(Double) // 무게가 필요한 경우 (kg 단위 등)
case bodyweight // 무게가 필요 없는 경우
}
struct Exercise {
let name: String
let sets: Int
let reps: Int
let type: ExerciseType
}
struct WOD {
let id: Int
let name: String
let exercises: [Exercise]
}
2. View Model
View에서 사용하기 위해 Model의 데이터를 처리하는 과정이다. WOD 단위의 데이터 로드를 하고 싶으면,
import Foundation
import Combine
class WODViewModel: ObservableObject {
@Published var wodList: [WOD] = []
init() {
fetchWODs()
}
func fetchWODs() {
// Mock data 예시
let sampleWOD = WOD(
id: 1,
name: "Hero WOD",
exercises: [
Exercise(name: "Deadlift", sets: 5, reps: 10, type: .weighted(100)),
Exercise(name: "Pull-ups", sets: 3, reps: 15, type: .bodyweight),
Exercise(name: "Burpees", sets: 4, reps: 20, type: .bodyweight)
]
)
self.wodList = [sampleWOD]
}
}
3. View
ViewModel에서 처리된 데이터를 기반으로 화면을 구성한다.
import SwiftUI
struct WODView: View {
@ObservedObject var viewModel = WODViewModel()
var body: some View {
NavigationView {
List(viewModel.wodList, id: \.id) { wod in
VStack(alignment: .leading) {
Text(wod.name)
.font(.headline)
ForEach(wod.exercises, id: \.name) { exercise in
HStack {
Text(exercise.name)
Spacer()
Text("\(exercise.sets) sets x \(exercise.reps) reps")
.font(.subheadline)
.foregroundColor(.gray)
if case let .weighted(weight) = exercise.type {
Text("(\(weight, specifier: "%.1f") kg)")
.font(.subheadline)
.foregroundColor(.blue)
}
}
}
}
.padding(.vertical)
}
.navigationTitle("CrossFit WODs")
}
}
}
struct WODView_Previews: PreviewProvider {
static var previews: some View {
WODView()
}
}
이렇게 구성하는 이유는 데이터, 비즈니스 로직, UI를 구별해서 구현 및 유지, 보수, 사용하기 위함이다.