top of page

Integrating and Using Firestore with SwiftUI

Updated: Sep 21, 2023


Integrating and Using Firestore with SwiftUI

Firestore is a powerful NoSQL database service provided by Google Cloud that allows developers to store and sync data for their applications. When combined with SwiftUI, Apple's modern user interface framework, you can create robust and dynamic iOS applications.


In this article, we will walk you through the process of integrating Firestore with SwiftUI and demonstrate how to perform basic CRUD (Create, Read, Update, Delete) operations using Firestore in a SwiftUI application.


Prerequisites

  1. Xcode installed on your Mac.

  2. Basic knowledge of Swift and SwiftUI.

  3. A Google Firebase account (https://firebase.google.com/).

  4. A new SwiftUI project created in Xcode.

Step 1: Set up Firebase Project


Before you can use Firestore in your SwiftUI project, you need to create a Firebase project and configure it.

  1. Go to the Firebase Console (https://console.firebase.google.com/).

  2. Click "Add Project" and follow the setup instructions.

  3. Once your project is created, click on it in the Firebase Console.

  4. In the left sidebar, click on "Firestore."

  5. Click on "Create Database" and choose "Start in test mode" for now. You can adjust security rules later.

Step 2: Add Firebase to your SwiftUI Project


In Xcode, open your SwiftUI project, and follow these steps to integrate Firebase into your project:

  1. Go to your Firebase project settings and click on the gear icon (Project Settings).

  2. Scroll down and click on "iOS" under Your apps.

  3. Click on the iOS icon to register your app with Firebase.

  4. Follow the setup instructions and download the GoogleService-Info.plist file.

Now, add Firebase to your Xcode project:

  1. Drag and drop the downloaded GoogleService-Info.plist file into your Xcode project's root folder.

  2. In the file inspector, make sure it's added to your target.


Step 3: Install Firebase SDK using CocoaPods


We'll use CocoaPods to install the Firebase SDK. If you haven't installed CocoaPods, follow the official installation guide (https://cocoapods.org/).


Open a terminal and navigate to your project directory. Run the following commands:


cd your-project-directory
pod init

This will create a Podfile in your project directory. Edit the Podfile to include Firebase Firestore by adding this line:

pod 'Firebase/Firestore'

Save the Podfile and run:


pod install

Close your Xcode project and open the .xcworkspace file that CocoaPods generated.


Step 4: Install Firebase SDK via SPM


Swift Package Manager (SPM) is an alternative to CocoaPods tool that can be used to manage dependencies in your iOS app projects.


To install Firebase SDK using SPM, open Xcode and navigate to "File" > "Add Packages."

In the prompt that appears, enter or paste the following Firebase GitHub repository URL:


https://github.com/firebase/firebase-ios-sdk.git

Select the specific version of Firebase you want to use. For new projects, it's recommended to use the latest version available.


Next, choose the FireStore library you want to include in your app.


Once you've made your selections, Xcode will start resolving your package dependencies.


Step 5: Initialize Firestore


In your SwiftUI project, you'll need to initialize Firestore. Open your AppDelegate.swift file and add the following import statement at the top:


import Firebase

In the application(_:didFinishLaunchingWithOptions:) method, add the following code to initialize Firestore:


FirebaseApp.configure()

Step 6: Create a Firestore Data Model


In this example, let's assume we want to build a simple task management app. Create a Swift file for your data model, e.g., Task.swift, and define the Task structure:


import FirebaseFirestoreSwift

struct Task: Identifiable, Codable {
    @DocumentID var id: String?
    var title: String
    var description: String
    var isCompleted: Bool
}

This data model represents a task with an optional ID, title, description, and completion status.


Step 7: Create a FirestoreManager


Next, create a Swift file for your FirestoreManager, which will encapsulate Firestore operations:


import Firebase
import FirebaseFirestore

class FirestoreManager: ObservableObject {
    private var db = Firestore.firestore()
    
    @Published var tasks: [Task] = []
    
    func fetchTasks() {
        db.collection("tasks").addSnapshotListener { (querySnapshot, error) in
        guard let documents = querySnapshot?.documents else {
                print("Error fetching tasks: \(error?.localizedDescription ?? "Unknown error")")
                return
            }
            
            self.tasks = documents.compactMap { queryDocumentSnapshot in
            return try? queryDocumentSnapshot.data(as: Task.self)
            }
        }
    }
    
    func addTask(task: Task) {
        do {
            _ = try db.collection("tasks").addDocument(from: task)
        } catch {
            print("Error adding task: \(error.localizedDescription)")
        }
    }
    
    func updateTask(task: Task) {
        if let taskID = task.id {
            do {
                try db.collection("tasks").document(taskID).setData(from: task)
            } catch {
                print("Error updating task: \(error.localizedDescription)")
            }
        }
    }
    
    func deleteTask(task: Task) {
        if let taskID = task.id {
            db.collection("tasks").document(taskID).delete { error in
            if let error = error {
                    print("Error removing task: \(error.localizedDescription)")
                }
            }
        }
    }
}

This FirestoreManager class includes functions for fetching tasks, adding tasks, updating tasks, and deleting tasks. It also publishes the tasks array as an @Published property, which SwiftUI can observe for updates.


Step 8: Create a SwiftUI Task List


In your SwiftUI view, create a Task list view that displays tasks and allows users to add, edit, and delete tasks. Here's an example of how you might structure the view:


import SwiftUI

struct TaskListView: View {
    @ObservedObject var firestoreManager = FirestoreManager()
    @State private var newTaskTitle = ""
    @State private var newTaskDescription = ""
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("New Task")) {
                    TextField("Title", text: $newTaskTitle)
                    TextField("Description", text: $newTaskDescription)
                    Button("Add Task") {
                        let newTask = Task(title: newTaskTitle, description: newTaskDescription, isCompleted: false)
                        firestoreManager.addTask(task: newTask)
                        newTaskTitle = ""
                        newTaskDescription = ""
                    }
                }
                
                Section(header: Text("Tasks")) {
                    ForEach(firestoreManager.tasks) { task in
                    NavigationLink(destination: TaskDetailView(task: task)) {
                            Text(task.title)
                        }
                    }
                    .onDelete { indexSet in
                    let taskToDelete = firestoreManager.tasks[indexSet.first!]
                        firestoreManager.deleteTask(task: taskToDelete)
                    }
                }
            }
            .navigationBarTitle("Task List")
            .onAppear {
                firestoreManager.fetchTasks()
            }
        }
    }
}

struct TaskListView_Previews: PreviewProvider {
    static var previews: some View {
        TaskListView()
    }
}

This TaskListView displays a list of tasks, allows users to add new tasks, and provides navigation to a TaskDetailView for editing individual tasks.


Step 9: Create a Task Detail View


Create a SwiftUI view for editing individual tasks:


import SwiftUI

struct TaskDetailView: View {
    @ObservedObject var firestoreManager = FirestoreManager()
    var task: Task
    @State private var isEditing = false
    @State private var editedTask: Task
    init(task: Task) {
        self.task = task
        self._editedTask = State(initialValue: task)
    }
    
    var body: some View {
        Form {
            Section {
                if isEditing {
                    TextField("Title", text: $editedTask.title)
                    TextField("Description", text: $editedTask.description)
                } else {
                    Text(editedTask.title)
                        .font(.largeTitle)
                    Text(editedTask.description)
                }
            }
            
            Section {
                Toggle("Completed", isOn: $editedTask.isCompleted)
            }
            
            Section {
                if isEditing {
                    Button("Save Changes") {
                        firestoreManager.updateTask(task: editedTask)
                        isEditing.toggle()
                    }
                    Button("Cancel") {
                        editedTask = task
                        isEditing.toggle()
                    }
                } else {
                    Button("Edit") {
                        isEditing.toggle()
                    }
                }
            }
        }
        .navigationBarTitle(isEditing ? "Edit Task" : "Task Detail")
    }
}

struct TaskDetailView_Previews: PreviewProvider {
    static var previews: some View {
        TaskDetailView(task: Task(title: "Sample Task", description: "This is a sample task.", isCompleted: false))
    }
}

This TaskDetailView allows users to edit task details and toggle the completion status. Changes are saved to Firestore when the "Save Changes" button is tapped.


Conclusion


In this article, we covered the process of integrating Firestore with SwiftUI and demonstrated basic CRUD operations using Firestore in a SwiftUI application. Firestore is a versatile database service that can be used to build dynamic and interactive iOS apps, and SwiftUI makes it easier than ever to create beautiful and responsive user interfaces. You can extend this example to include more advanced features and improve the user experience of your app.


Happy coding!

Blog for Mobile App Developers, Testers and App Owners

 

This blog is from Finotes Team. Finotes is a lightweight mobile APM and bug detection tool for iOS and Android apps.

In this blog we talk about iOS and Android app development technologies, languages and frameworks like Java, Kotlin, Swift, Objective-C, Dart and Flutter that are used to build mobile apps. Read articles from Finotes team about good programming and software engineering practices, testing and QA practices, performance issues and bugs, concepts and techniques. 

bottom of page