------------------------- | Notes List | |-----------------------| | + Add Note | |-----------------------| | - Note 1 | | - Note 2 | | - Note 3 | | | -------------------------
CRUD operations with SwiftData in iOS Swift - Mini App: Build & Ship
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() } }
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.
------------------------- | 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.
Practice
@Model attribute in SwiftData represent?Solution
Step 1: Understand the role of @Model
The@Modelattribute marks a class as a data model entity for SwiftData, enabling CRUD operations.Step 2: Differentiate from UI and functions
It is not related to UI components, network functions, or view modifiers.Final Answer:
A data model class used to define entities for CRUD operations -> Option BQuick Check:
@Model defines data entities = B [OK]
- Confusing @Model with UI components
- Thinking @Model fetches data
- Mixing @Model with view modifiers
Book with a title property of type String?Solution
Step 1: Identify correct @Model usage
SwiftData requires@Modelon a class, not a struct, to define a data entity.Step 2: Check class vs struct and attribute
@Model class Book { var title: String } correctly uses@Modelwith a class and a property.Final Answer:
@Model class Book { var title: String } -> Option AQuick Check:
@Model + class + properties = D [OK]
- Using struct instead of class for @Model
- Omitting @Model attribute
- Using ObservableObject instead of @Model
books.count after adding a new book?
struct ContentView: View {
@Environment(\"modelContext\") private var modelContext
@Query var books: [Book]
var body: some View {
Button(\"Add Book\") {
let newBook = Book(title: \"SwiftData Basics\")
modelContext.insert(newBook)
}
Text(\"Books count: \(books.count)\")
}
}Solution
Step 1: Understand @Query and modelContext.insert
The@Queryproperty fetches all Book entities. Inserting a new Book updates the data source.Step 2: Check the effect of insertion on books.count
After inserting one new Book, the count increases by 1, so it will be 1.Final Answer:
Books count: 1 -> Option CQuick Check:
Insert one book, count updates to 1 [OK]
- Assuming count stays 0 after insert
- Expecting count to be 2 without adding twice
- Thinking code causes compile error
func updateBookTitle(book: Book, newTitle: String) {
book.title = newTitle
modelContext.save()
}Solution
Step 1: Check SwiftData update pattern
In SwiftData, changes to model object properties are tracked automatically, butmodelContext.save()is not a valid method to persist changes explicitly.Step 2: Identify if there's an error
Directly mutatingbook.titleis correct, but callingmodelContext.save()causes an error because the method does not exist.Final Answer:
modelContext.save() is not a valid method in SwiftData -> Option DQuick Check:
Property change tracked automatically; no save() method = A [OK]
- Thinking modelContext.save() exists
- Believing properties are immutable
- Confusing update with insert
Book objects with an empty title using SwiftData. Which code snippet correctly performs this operation?Solution
Step 1: Understand deletion in SwiftData
To delete objects, you callmodelContext.delete(object)for each object.Step 2: Check each option for correctness
books.filter { $0.title.isEmpty }.forEach { modelContext.delete($0) } filters books with empty titles and deletes each correctly. for book in books where book.title.isEmpty { modelContext.delete(book) } syntax is invalid in Swift. modelContext.deleteAll(Book.self, where: \"title == ''\") uses a non-existent method. modelContext.remove(books.filter { $0.title.isEmpty }) uses wrong method name.Final Answer:
books.filter { $0.title.isEmpty }.forEach { modelContext.delete($0) } -> Option AQuick Check:
Filter then delete each = A [OK]
- Using invalid for-in with where clause
- Calling non-existent deleteAll method
- Using wrong method name like remove
Ready to prove it?
Pass the test and this pattern moves unknown
