MVVM architecture pattern for iOS

Sofia Martinez Jan 2026
2 tabs
import SwiftUI

struct PostDetailView: View {
    @StateObject private var viewModel: PostDetailViewModel

    init(postId: Int) {
        _viewModel = StateObject(wrappedValue: PostDetailViewModel(postId: postId))
    }

    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 16) {
                if viewModel.isLoading {
                    ProgressView()
                } else if let post = viewModel.post {
                    Text(post.title)
                        .font(.title)
                        .fontWeight(.bold)

                    HStack {
                        Image(systemName: "person.circle")
                        Text(post.author.name)
                            .font(.subheadline)
                        Spacer()
                        Text(post.createdAt, style: .date)
                            .font(.caption)
                            .foregroundColor(.secondary)
                    }

                    Divider()

                    Text(post.body)
                        .font(.body)

                    HStack(spacing: 20) {
                        Button(action: viewModel.toggleLike) {
                            Label("\(viewModel.likesCount)", systemImage: viewModel.isLiked ? "heart.fill" : "heart")
                        }
                        .foregroundColor(viewModel.isLiked ? .red : .primary)

                        Label("\(post.commentsCount)", systemImage: "bubble.left")
                    }
                    .padding(.top)

                    CommentsSection(comments: viewModel.comments)
                } else if let error = viewModel.errorMessage {
                    Text(error)
                        .foregroundColor(.red)
                }
            }
            .padding()
        }
        .navigationTitle("Post")
        .onAppear {
            viewModel.loadPost()
        }
    }
}
2 files · swift Explain with highlit

MVVM (Model-View-ViewModel) separates concerns cleanly in iOS apps. Models hold data, Views display UI, and ViewModels mediate between them with business logic and state. Views bind to ViewModel properties using Combine or SwiftUI's property wrappers. ViewModels expose @Published properties that Views observe. This separation makes code testable—ViewModels can be unit tested without UI, and Views become thin presentation layers. I keep ViewModels framework-agnostic, importing only Foundation, not UIKit or SwiftUI. Navigation and coordination logic goes in Coordinators. This architecture scales well from small to large apps while maintaining clear boundaries.