SwiftUI MVVM

Model-View-ViewModel

MVVM separates UI (View) from business logic (ViewModel) and data (Model).

The ViewModel is an ObservableObject the View observes.

Syntax:

  • class VM: ObservableObject { @Published var state }
  • @StateObject private var vm = VM()
  • List(vm.items) { ... }
  • ObservableObject: A class that notifies its observers when its state changes.
  • @Published: A property wrapper that publishes changes to a value, notifying observers.
  • @StateObject: A property wrapper that creates a new instance of a class, making it a source of truth for a view.

Example

Demo.swift

ViewModel.swift

ContentView.swift

App.swift

import SwiftUI

struct MVVMBasicView: View {
  @StateObject private var vm = TodoViewModel()
  var body: some View {
List(vm.todos) { t in Text(t.title) }
  .onAppear { vm.load() }
} }

This example shows the MVVM flow: a ViewModel exposes state, the View observes it, and triggers loading on appear.


REMOVE ADS


Sample App: Notes (MVVM)

Example

Demo.swift

ViewModel.swift

ContentView.swift

App.swift

import SwiftUI

struct NotesMVVMView: View {
  @StateObject private var vm = NotesViewModel()
  var body: some View {
List(vm.notes) { n in
  VStack(alignment: .leading) {
    Text(n.title).font(.headline)
    Text(n.body).font(.subheadline)
  }
}
.onAppear { vm.loadMock() }
} }

This example creates a simple Notes model and ViewModel, then renders a list and loads mock data when the view appears.

Next, connect data to a server in Networking or persist locally in Core Data.


App Group

Use an App Group to share small values (like counts, flags, timestamps) between your app and its Widget.

The hook below writes notesCount whenever the ViewModel’s notes change.

How to use it:

  • Enable the same App Group on both the app target and the widget target (e.g., group.com.example.notes).
  • Keep updateSharedNotesCount writing to that group ID; the didSet observer writes the new count on every change.
  • Read the value from the widget using the same suiteName (see snippet below).

App Group hook (Widget Count)

Example

NotesViewModel.swift

import Combine

func updateSharedNotesCount(_ count: Int) {
  let defaults = UserDefaults(suiteName: "group.com.example.notes")
  defaults?.set(count, forKey: "notesCount")
}

class NotesViewModel: ObservableObject {
  @Published var notes: [Note] = [] {
didSet { updateSharedNotesCount(notes.count) }
} func add(title: String, body: String) {
notes.append(.init(id: UUID(), title: title, body: body))
} func delete(at offsets: IndexSet) {
notes.remove(atOffsets: offsets)
} }

Example

WidgetRead.swift

import SwiftUI

// Option A: Direct read via UserDefaults (e.g., from a TimelineProvider)
let defaults = UserDefaults(suiteName: "group.com.example.notes")
let count = defaults?.integer(forKey: "notesCount") ?? 0

// Option B: @AppStorage with a custom store (usable in a Widget view)
struct WidgetCountView: View {
  @AppStorage("notesCount", store: UserDefaults(suiteName: "group.com.example.notes"))
  private var notesCount: Int = 0

  var body: some View {
Text("Notes: \(notesCount)")
} }

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *