MVVM Pattern
1. Overview
View ←→ ViewModel ←→ Model
UI Logic Data2. Model
struct User: Identifiable, Codable {
let id: Int
let name: String
let email: String
}3. ViewModel
@MainActor
class UserListViewModel: ObservableObject {
@Published var users: [User] = []
@Published var isLoading = false
@Published var error: String?
private let service: UserService
init(service: UserService = UserService()) {
self.service = service
}
func loadUsers() async {
isLoading = true
error = nil
do {
users = try await service.fetchUsers()
} catch {
self.error = error.localizedDescription
}
isLoading = false
}
func deleteUser(_ user: User) async {
do {
try await service.delete(user)
users.removeAll { $0.id == user.id }
} catch {
self.error = error.localizedDescription
}
}
}4. View
struct UserListView: View {
@StateObject var viewModel = UserListViewModel()
var body: some View {
Group {
if viewModel.isLoading {
ProgressView()
} else if let error = viewModel.error {
Text(error).foregroundColor(.red)
} else {
List(viewModel.users) { user in
UserRow(user: user)
}
}
}
.task {
await viewModel.loadUsers()
}
}
}5. Benefits
- Testable: ViewModel có thể test riêng
- Reusable: Logic tách khỏi UI
- Maintainable: Clear separation
📝 Best Practices
- ViewModel là
@MainActorcho UI updates - Dùng
@StateObjectkhi tạo ViewModel - Dùng
@ObservedObjectkhi nhận từ parent
Last updated on