0
0
iOS Swiftmobile~20 mins

CRUD operations with SwiftData in iOS Swift - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Notes List
A simple notes app screen where users can create, read, update, and delete notes stored using SwiftData.
Target UI
-------------------------
| Notes List            |
|-----------------------|
| + Add Note            |
|-----------------------|
| - Note 1              |
| - Note 2              |
| - Note 3              |
|                       |
-------------------------
Display a list of notes stored with SwiftData.
Add a button to create a new note with a title and content.
Tap a note to edit its title and content.
Swipe left on a note to delete it.
Persist all changes using SwiftData.
Starter Code
iOS Swift
import SwiftUI
import SwiftData

@Model
class Note {
  var id: UUID = UUID()
  var title: String
  var content: String

  init(title: String = "", content: String = "") {
    self.title = title
    self.content = content
  }
}

struct NotesListView: View {
  @Query var notes: [Note]
  @Environment(\.modelContext) private var modelContext

  var body: some View {
    NavigationView {
      List {
        // TODO: Show notes here
      }
      .navigationTitle("Notes List")
      .toolbar {
        // TODO: Add button to add new note
      }
    }
  }
}

struct NotesListView_Previews: PreviewProvider {
  static var previews: some View {
    NotesListView()
  }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
iOS Swift
import SwiftUI
import SwiftData

@Model
class Note {
  var id: UUID = UUID()
  var title: String
  var content: String

  init(title: String = "", content: String = "") {
    self.title = title
    self.content = content
  }
}

struct NotesListView: View {
  @Query var notes: [Note]
  @Environment(\.modelContext) private var modelContext
  @State private var showingAddNote = false

  var body: some View {
    NavigationView {
      List {
        ForEach(notes, id: \.id) { note in
          NavigationLink(destination: NoteDetailView(note: note)) {
            Text(note.title.isEmpty ? "Untitled" : note.title)
          }
        }
        .onDelete(perform: deleteNotes)
      }
      .navigationTitle("Notes List")
      .toolbar {
        ToolbarItem(placement: .navigationBarTrailing) {
          Button(action: { showingAddNote = true }) {
            Image(systemName: "plus")
              .accessibilityLabel("Add Note")
          }
        }
      }
      .sheet(isPresented: $showingAddNote) {
        AddNoteView(isPresented: $showingAddNote)
          .environment(\.modelContext, modelContext)
      }
    }
  }

  private func deleteNotes(at offsets: IndexSet) {
    for index in offsets {
      let note = notes[index]
      modelContext.delete(note)
    }
  }
}

struct AddNoteView: View {
  @Environment(\.modelContext) private var modelContext
  @Binding var isPresented: Bool
  @State private var title = ""
  @State private var content = ""

  var body: some View {
    NavigationView {
      Form {
        TextField("Title", text: $title)
        TextEditor(text: $content)
          .frame(height: 150)
          .accessibilityLabel("Note Content")
      }
      .navigationTitle("New Note")
      .toolbar {
        ToolbarItem(placement: .cancellationAction) {
          Button("Cancel") {
            isPresented = false
          }
        }
        ToolbarItem(placement: .confirmationAction) {
          Button("Save") {
            let newNote = Note(title: title, content: content)
            modelContext.insert(newNote)
            isPresented = false
          }
          .disabled(title.trimmingCharacters(in: .whitespaces).isEmpty)
        }
      }
    }
  }
}

struct NoteDetailView: View {
  @ObservedObject var note: Note
  @Environment(\.modelContext) private var modelContext

  var body: some View {
    Form {
      TextField("Title", text: $note.title)
      TextEditor(text: $note.content)
        .frame(height: 150)
        .accessibilityLabel("Note Content")
    }
    .navigationTitle(note.title.isEmpty ? "Edit Note" : note.title)
    .toolbar {
      ToolbarItem(placement: .navigationBarTrailing) {
        Button("Save") {
          // Changes auto-saved by SwiftData
          modelContext.insert(note) // Ensure note is tracked
        }
      }
    }
  }
}

struct NotesListView_Previews: PreviewProvider {
  static var previews: some View {
    NotesListView()
  }
}

This app uses SwiftData to store notes with a title and content. The Note class is marked with @Model so SwiftData can manage it.

The NotesListView shows all notes using @Query. It has a plus button to add a new note, which opens a sheet with AddNoteView. This view lets the user enter a title and content, then saves the new note to the model context.

Tapping a note navigates to NoteDetailView, where the user can edit the note's title and content. Changes are automatically saved by SwiftData.

Users can swipe left on a note in the list to delete it, which removes it from the model context.

Accessibility labels are added for screen readers, and the UI uses standard SwiftUI navigation and forms for a familiar experience.

Final Result
Completed Screen
-------------------------
| Notes List            |
|-----------------------|
| + Add Note            |
|-----------------------|
| Note 1                |
| Note 2                |
| Note 3                |
|                       |
-------------------------

-- Tap '+' opens a form to add a new note.
-- Tap a note opens detail to edit.
-- Swipe left on a note to delete it.
Tap '+' button to open a modal form to add a new note with title and content.
Tap a note in the list to navigate to an edit screen where title and content can be changed.
Swipe left on a note row to reveal a delete action that removes the note.
All changes are saved automatically and reflected in the list.
Stretch Goal
Add dark mode support with automatic color scheme adaptation.
💡 Hint
Use SwiftUI's built-in color schemes and system colors to ensure the UI adapts automatically to light and dark modes.