Getting Started with Cloud Firestore and SwiftUI

In this tutorial, you’ll learn how to use Firebase Cloud Firestore to add persistence to a SwiftUI iOS application with Swift. By Libranner Santos.

4.8 (11) · 2 Reviews

Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Using the authentication service

Open AppDelegate.swift and add the following line after FirebaseApp.configure() in application(_:didFinishLaunchingWithOptions:):

AuthenticationService.signIn()

This code ensures the user is signed in when the app starts.

Next, open CardRepository.swift and add these properties at the top of the class:

// 1
var userId = ""
// 2
private let authenticationService = AuthenticationService()
// 3
private var cancellables: Set<AnyCancellable> = []

This code:

  1. Declares userId, which you’ll use to store the current user id generated by Firebase.
  2. Creates an instance of AuthenticationService.
  3. Creates a set of AnyCancellables. This property stores your subscriptions so you can cancel them later.

Next, change init() to this:

init() {
  // 1
  authenticationService.$user
    .compactMap { user in
      user?.uid
    }
    .assign(to: \.userId, on: self)
    .store(in: &cancellables)

  // 2
  authenticationService.$user
    .receive(on: DispatchQueue.main)
    .sink { [weak self] _ in
      // 3
      self?.get()
    }
    .store(in: &cancellables)
}

Here you:

  1. Bind user‘s id from AuthenticationService to the repository’s userId. It also stores the object in cancellables so it can be canceled later.
  2. This code observes the changes in user, uses receive(on:options:) to set the thread where the code will execute and then attaches a subscriber using sink(receiveValue:). This guarantees that when you get a user from AuthenticationService, the code in the closure executes in the main thread.
  3. Call get(), like in your original initializer.

Change add(_:) to this:

func add(_ card: Card) {
  do {
    var newCard = card
    newCard.userId = userId
    _ = try store.collection(path).addDocument(from: newCard)
  } catch {
    fatalError("Unable to add card: \(error.localizedDescription).")
  }
}

Here, you make a copy of card and mutate its userId to the value of the repository’s userId. Now, every time you create a new card it’ll contain the actual user id generated by Firebase.

Finally, add the following line before .addSnapshotListener(_:) on get():

.whereField("userId", isEqualTo: userId)

This code lets you filter cards by userId.

Adding Authorization Using Security Rules

Open your Firebase project in your web browser. Then go to Cloud Firestore, and click Rules on the top horizontal navigation bar. You’ll see something similar to this:

Configuring Security Rules

This JavaScript-like code is the Firestore Security Rules. These rules define if a user is authorized to access or modify a document. Replace the existing code with this:

// 1
rules_version = '2';
// 2
service cloud.firestore {
  // 3 
  match /databases/{database}/documents {
    // 4
    match /{document=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Here what this does:

  1. Sets the rules_version to '2'. This is, at the moment, the latest version, and determines how to interpret the following code.
  2. Indicates which service these rules apply to. In this case, Cloud Firestore.
  3. Specifies rules should match any Cloud Firestore database in the project.
  4. Specifies only authenticated users can read or write into the document.

These rules determine the authorization part of the app. With authentication, you get to know who the user is. With authorization, you determine what this user can do.

Click Publish to save the changes. Then go back to the project and build and run.

Displaying Cards Filtered by User

The app won’t display any cards since it’s now filtering by userId. If you add a new one, it’ll appear. You can even close and re-open the app, and only cards created from now on will appear.

Understanding Firestore Pricing

Understanding Cloud Firestore pricing can save you from spending too much money. Keep the following in mind:

  • Cloud Firestore charges you for the number of operations you perform: reads, writes and deletes.
  • The pricing varies from one location to another.
  • You also have to pay for the storage your database uses and the network bandwidth.
  • If you change a single field or a complete document it counts as an operation.
  • The number of reads is the number of records returned. So, if your query returns ten documents, you’ll have ten reads. When possible, use limit to restrict the number of documents your queries can return.
  • You can monitor your current budget using Alerts. You can configure it using Google Cloud Console which will also let you check your previous invoices and set your desired daily spending.
  • Google Cloud Operation lets you monitor performance and get metrics which can also help your budget plan.

When possible, you should also cache data locally to avoid requesting data from Firestore.

You can find more information in the the Firestore documentation.

Where to Go From Here?

You can download the completed project using the Download Materials button at the top or bottom of the tutorial. Remember, you still have to add your own GoogleService-Info.plist after downloading the final project.

In this tutorial, you learned how to use Cloud Firestore to persist your data and use MVVM to integrate it with your SwiftUI views. You also learned how to implement Anonymous Authentication using Firebase from scratch.

Firebase and Cloud Firestore have much more to offer. If you want to dive deeper into Cloud Firestore, look into the official Cloud Firestore documentation. Or check out Firebase Tutorial: Getting Started, Firebase Tutorial: Real-time Chat or Video Tutorial: Beginning Firebase.

I hope you enjoyed this tutorial. If you have any questions or comments, please join the forum below.