[2023] SwiftUI @Observable macro: Basic MVVM setup
Swee Sen
Posted on July 1, 2023
With the new @Observable
macro, setting up MVVM in SwiftUI has become much simpler. We shall explain the setup by trying to build the following page:
1. Setup ViewModel
struct User {
let name: String
let profileImgUrl: String
}
struct ChatMessage {
var message: String
}
struct HomePageChat: Identifiable {
let id = UUID()
let sender: User
var latestMessage: ChatMessage
}
@Observable class HomePageViewModel {
var chats: [HomePageChat] = []
init() {
setupDummyData()
}
private func setupDummyData() {
let userA = User(name: "Tim Cook", profileImgUrl: "https://www.apple.com/leadership/images/bio/tim-cook_image.png.og.png?1685138662136")
let messageA = ChatMessage(message: "Hello! Tim Cook here, how are you doing?")
let chatA = HomePageChat(sender: userA, latestMessage: messageA)
let userB = User(name: "Craig Federighi", profileImgUrl: "https://www.apple.com/leadership/images/bio/craig_federighi_image.png.og.png?1685171686562")
let messageB = ChatMessage(message: "I am Craig Federighi, who are you?")
let chatB = HomePageChat(sender: userB, latestMessage: messageB)
self.chats = [chatA, chatB]
}
}
Note that all we need to do is to mark our HomePageViewModel
with @Observable
macro to make our viewModel observable by SwiftUI views.
2. Setup Views
struct HomePageView: View {
@Environment(HomePageViewModel.self) private var viewModel
var body: some View {
NavigationView {
VStack {
List(viewModel.chats) { chat in
HomePageCellView(chat: chat)
}
.listStyle(.sidebar)
}
.navigationTitle("Chats")
.navigationBarTitleDisplayMode(.large)
}
}
}
#Preview {
HomePageView()
.environment(HomePageViewModel())
}
struct HomePageCellView: View {
let chat: HomePageChat
var body: some View {
HStack {
VStack(alignment: .leading) {
AsyncImage(url: URL(string: chat.sender.profileImgUrl)) { image in
image
.resizable()
.scaledToFill()
} placeholder: {
ProgressView()
}
.frame(width: 50, height: 50)
.clipShape(Circle())
.clipShape(Circle())
}
VStack(alignment: .leading) {
Text(chat.sender.name)
.bold()
.padding(.bottom, 1)
Text(chat.latestMessage.message)
.foregroundStyle(.gray)
.font(.system(size: 12))
.fixedSize(horizontal: false, vertical: true)
}
.padding(.leading, 6)
Spacer()
VStack {
Text("Yesterday")
.foregroundStyle(.gray)
.font(.system(size: 12))
Spacer()
}
}
.padding(.leading, 0)
.padding(.vertical, 6)
.frame(height: 60)
}
}
💖 💪 🙅 🚩
Swee Sen
Posted on July 1, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.