Bird
Raised Fist0
iOS Swiftmobile~20 mins

Cloud Firestore in iOS Swift - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Simple Firestore Notes
A screen to add, list, and delete notes stored in Cloud Firestore.
Target UI
-------------------------
| Simple Firestore Notes |
-------------------------
| + Add Note             |
|-----------------------|
| - Note 1              |
| - Note 2              |
| - Note 3              |
-------------------------
Display a list of notes fetched from Firestore collection 'notes'.
Add a '+' button in the navigation bar to add a new note.
When '+' is tapped, show an alert with a text field to enter note text.
Save the new note to Firestore and update the list.
Each note row has a delete swipe action to remove the note from Firestore.
Use Firestore's real-time listener to update the list automatically.
Starter Code
iOS Swift
import SwiftUI
import Firebase

struct NotesView: View {
    @State private var notes: [Note] = []

    var body: some View {
        NavigationView {
            List {
                // TODO: Show notes here
            }
            .navigationTitle("Simple Firestore Notes")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        // TODO: Show add note alert
                    }) {
                        Image(systemName: "plus")
                    }
                }
            }
            // TODO: Add Firestore listener
        }
    }
}

struct Note: Identifiable {
    var id: String
    var text: String
}

// TODO: Add Firestore functions here

struct NotesView_Previews: PreviewProvider {
    static var previews: some View {
        NotesView()
    }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
iOS Swift
import SwiftUI
import Firebase

struct NotesView: View {
    @State private var notes: [Note] = []
    @State private var showingAddNote = false
    @State private var newNoteText = ""

    private var db = Firestore.firestore()

    var body: some View {
        NavigationView {
            List {
                ForEach(notes) { note in
                    Text(note.text)
                }
                .onDelete(perform: deleteNote)
            }
            .navigationTitle("Simple Firestore Notes")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        showingAddNote = true
                    }) {
                        Image(systemName: "plus")
                    }
                    .accessibilityLabel("Add note")
                }
            }
            .onAppear(perform: listenToNotes)
            .alert("Add New Note", isPresented: $showingAddNote, actions: {
                TextField("Note text", text: $newNoteText)
                Button("Save", action: addNote)
                Button("Cancel", role: .cancel, action: {
                    newNoteText = ""
                })
            })
        }
    }

    func listenToNotes() {
        db.collection("notes").addSnapshotListener { snapshot, error in
            guard let documents = snapshot?.documents else {
                print("No documents")
                return
            }
            notes = documents.map { doc in
                let data = doc.data()
                let text = data["text"] as? String ?? ""
                return Note(id: doc.documentID, text: text)
            }
        }
    }

    func addNote() {
        let trimmed = newNoteText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return }
        db.collection("notes").addDocument(data: ["text": trimmed])
        newNoteText = ""
        showingAddNote = false
    }

    func deleteNote(at offsets: IndexSet) {
        offsets.forEach { index in
            let note = notes[index]
            db.collection("notes").document(note.id).delete()
        }
    }
}

struct Note: Identifiable {
    var id: String
    var text: String
}

struct NotesView_Previews: PreviewProvider {
    static var previews: some View {
        NotesView()
    }
}

This solution uses SwiftUI with Firebase Firestore to create a simple notes app.

We use addSnapshotListener to listen for real-time updates from the 'notes' collection. This keeps the list updated automatically.

The '+' button shows an alert with a text field to enter a new note. When saved, it adds a document to Firestore.

Notes are displayed in a list with swipe-to-delete enabled. Deleting removes the document from Firestore.

Accessibility label is added to the add button for screen readers.

Final Result
Completed Screen
-------------------------
| Simple Firestore Notes |
-------------------------
| + Add Note             |
|-----------------------|
| - Buy groceries        |
| - Call mom             |
| - Walk the dog         |
-------------------------
Tap '+' button to open alert with text field.
Enter note text and tap 'Save' to add note to Firestore and update list.
Swipe left on a note to reveal 'Delete' and remove it from Firestore.
List updates automatically when notes change in Firestore.
Stretch Goal
Add pull-to-refresh to manually refresh notes list.
💡 Hint
Use SwiftUI's .refreshable modifier on the List and call a Firestore fetch function.

Practice

(1/5)
1. What is the main purpose of Cloud Firestore in an iOS app?
easy
A. To compile Swift code into an app
B. To design the app's user interface
C. To handle user authentication only
D. To store and sync app data in the cloud with live updates

Solution

  1. Step 1: Understand Cloud Firestore's role

    Cloud Firestore is a cloud database that stores app data and syncs it live across devices.
  2. Step 2: Compare options with Firestore's function

    Only To store and sync app data in the cloud with live updates describes storing and syncing data live, which matches Firestore's purpose.
  3. Final Answer:

    To store and sync app data in the cloud with live updates -> Option D
  4. Quick Check:

    Firestore = cloud data storage and sync [OK]
Hint: Firestore is for cloud data storage and live sync [OK]
Common Mistakes:
  • Confusing Firestore with UI design tools
  • Thinking Firestore only handles authentication
  • Believing Firestore compiles code
2. Which Swift code snippet correctly gets a reference to a Firestore collection named "users"?
easy
A. let usersRef = Firestore.firestore().collection("users")
B. let usersRef = Firestore.collection("users")
C. let usersRef = Firestore.getCollection("users")
D. let usersRef = Firestore.firestore().get("users")

Solution

  1. Step 1: Recall Firestore syntax for collection reference

    The correct way is to call Firestore.firestore() then .collection("name") to get a collection reference.
  2. Step 2: Check each option's method calls

    Only let usersRef = Firestore.firestore().collection("users") uses Firestore.firestore().collection("users"), which is correct syntax.
  3. Final Answer:

    let usersRef = Firestore.firestore().collection("users") -> Option A
  4. Quick Check:

    Firestore.firestore().collection("name") = correct [OK]
Hint: Use Firestore.firestore().collection("name") to get collection [OK]
Common Mistakes:
  • Omitting .firestore() before .collection()
  • Using non-existent methods like getCollection()
  • Calling get("users") instead of collection("users")
3. Given this Swift code, what will be printed if the document exists with field "name" = "Alice"?
let docRef = Firestore.firestore().collection("users").document("user123")
docRef.getDocument { document, error in
  if let document = document, document.exists {
    let name = document.data()?["name"] as? String ?? "Unknown"
    print(name)
  } else {
    print("No document")
  }
}
medium
A. Unknown
B. No document
C. Alice
D. Error

Solution

  1. Step 1: Understand the getDocument completion handler

    If the document exists, it extracts the "name" field and prints it; otherwise prints "No document".
  2. Step 2: Given document exists with "name" = "Alice"

    The code will print the value "Alice" from the document field.
  3. Final Answer:

    Alice -> Option C
  4. Quick Check:

    Document exists with name = Alice, so print Alice [OK]
Hint: If document exists, print its field value [OK]
Common Mistakes:
  • Assuming print shows "No document" even if document exists
  • Confusing default "Unknown" with actual field value
  • Ignoring optional cast and default fallback
4. What is wrong with this Swift Firestore code snippet?
let docRef = Firestore.firestore().collection("users").document("user123")
docRef.getDocument { document, error in
  if error != nil {
    print("Error fetching document")
  }
  if let document = document, document.exists {
    print(document.data())
  }
}
medium
A. It does not handle the case when document does not exist
B. It uses wrong method getDocument instead of get()
C. It should unwrap error with if let error = error
D. It tries to print document.data() without casting

Solution

  1. Step 1: Analyze error and document handling

    The code prints error if error exists, then prints data if document exists, but does nothing if document does not exist and no error.
  2. Step 2: Identify missing handling for non-existing document

    There is no else clause for when document does not exist, so no message is shown in that case.
  3. Final Answer:

    It does not handle the case when document does not exist -> Option A
  4. Quick Check:

    Missing else for document.exists = problem [OK]
Hint: Always handle document not existing case [OK]
Common Mistakes:
  • Confusing getDocument with get() method
  • Not unwrapping error properly but still works
  • Printing data() without casting is allowed
5. You want to listen for live updates to a Firestore collection "messages" and update your UI in SwiftUI. Which code snippet correctly sets up a listener?
hard
A. Firestore.firestore().collection("messages").getDocuments { snapshot, error in let messages = snapshot?.documents.map { $0.data() } ?? [] self.messages = messages }
B. Firestore.firestore().collection("messages").addSnapshotListener { snapshot, error in guard let snapshot = snapshot else { return } let messages = snapshot.documents.map { $0.data() } DispatchQueue.main.async { self.messages = messages } }
C. Firestore.firestore().collection("messages").document("messages").addSnapshotListener { snapshot, error in if let snapshot = snapshot { self.messages = snapshot.data() } }
D. Firestore.firestore().collection("messages").listen { snapshot, error in self.messages = snapshot?.documents ?? [] }

Solution

  1. Step 1: Identify method for live updates

    addSnapshotListener listens for real-time updates; getDocuments fetches once.
  2. Step 2: Check correct usage of addSnapshotListener and UI update

    Firestore.firestore().collection("messages").addSnapshotListener { snapshot, error in guard let snapshot = snapshot else { return } let messages = snapshot.documents.map { $0.data() } DispatchQueue.main.async { self.messages = messages } } uses addSnapshotListener on collection, maps documents to data, and updates UI on main thread.
  3. Step 3: Verify other options

    Firestore.firestore().collection("messages").getDocuments { snapshot, error in let messages = snapshot?.documents.map { $0.data() } ?? [] self.messages = messages } fetches once, not live. Firestore.firestore().collection("messages").document("messages").addSnapshotListener { snapshot, error in if let snapshot = snapshot { self.messages = snapshot.data() } } listens on a document named "messages" (wrong). Firestore.firestore().collection("messages").listen { snapshot, error in self.messages = snapshot?.documents ?? [] } uses non-existent listen method.
  4. Final Answer:

    Firestore.firestore().collection("messages").addSnapshotListener { snapshot, error in guard let snapshot = snapshot else { return } let messages = snapshot.documents.map { $0.data() } DispatchQueue.main.async { self.messages = messages } } -> Option B
  5. Quick Check:

    Use addSnapshotListener for live updates [OK]
Hint: Use addSnapshotListener for live Firestore updates [OK]
Common Mistakes:
  • Using getDocuments for live updates
  • Listening on wrong document instead of collection
  • Using non-existent listen method
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard