1. Overview
Goals
In this codelab you will build a Firestore-backed restaurant recommendation app on iOS in Swift. You will learn how to:
- Read and write data to Firestore from an iOS app
- Listen to changes in Firestore data in realtime
- Use Firebase Authentication and security rules to secure Firestore data
- Write complex Firestore queries
Prerequisites
Before starting this codelab make sure you have installed:
- Xcode version 14.0 (or higher)
- CocoaPods 1.12.0 (or higher)
2. Get the Sample Project
Download the Code
Begin by cloning the sample project and running pod update in the project directory:
git clone https://github.com/firebase/friendlyeats-ios cd friendlyeats-ios pod update
Open FriendlyEats.xcworkspace in Xcode and run it (Cmd+R). The app should compile correctly and immediately crash on launch, since it's missing a GoogleService-Info.plist file. We'll correct that in the next step.
3. Set up Firebase
Create a Firebase project
- Sign into the Firebase console using your Google Account.
- Click the button to create a new project, and then enter a project name (for example,
FriendlyEats).
- Click Continue.
- If prompted, review and accept the Firebase terms, and then click Continue.
- (Optional) Enable AI assistance in the Firebase console (called "Gemini in Firebase").
- For this codelab, you do not need Google Analytics, so toggle off the Google Analytics option.
- Click Create project, wait for your project to provision, and then click Continue.
Connect your app to Firebase
Create an iOS app in your new Firebase project.
Download your project's GoogleService-Info.plist file from Firebase console and drag it to the root of the Xcode project. Run the project again to make sure the app configures correctly and no longer crashes on launch. After logging in, you should see a blank screen like the example below. If you're unable to log in, make sure you've enabled the Email/Password sign-in method in Firebase console under Authentication.

4. Write Data to Firestore
In this section we'll write some data to Firestore so that we can populate the app UI. This can be done manually via the Firebase console, but we'll do it in the app itself to demonstrate a basic Firestore write.
The main model object in our app is a restaurant. Firestore data is split into documents, collections, and subcollections. We will store each restaurant as a document in a top-level collection called restaurants. If you'd like to learn more about the Firestore data model, read about documents and collections in the documentation.
Before we can add data to Firestore, we need to get a reference to the restaurants collection. Add the following to the inner for loop in the RestaurantsTableViewController.didTapPopulateButton(_:) method.
let collection = Firestore.firestore().collection("restaurants")
Now that we have a collection reference we can write some data. Add the following just after the last line of code we added:
let collection = Firestore.firestore().collection("restaurants")
// ====== ADD THIS ======
let restaurant = Restaurant(
name: name,
category: category,
city: city,
price: price,
ratingCount: 0,
averageRating: 0
)
collection.addDocument(data: restaurant.dictionary)
The code above adds a new document to the restaurants collection. The document data comes from a dictionary, which we get from a Restaurant struct.
We're almost there–before we can write documents to Firestore we need to open up Firestore's security rules and describe which parts of our database should be writeable by which users. For now, we'll allow only authenticated users to read and write to the entire database. This is a little too permissive for a production app, but during the app-building process we want something relaxed enough so we won't constantly run into authentication issues while experimenting. At the end of this codelab we'll talk about how to harden your security rules and limit the possibility of unintended reads and writes.
In the Rules tab of the Firebase console add the following rules and then click Publish.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /restaurants/{any}/ratings/{rating} {
// Users can only write ratings with their user ID
allow read;
allow write: if request.auth != null
&& request.auth.uid == request.resource.data.userId;
}
match /restaurants/{any} {
// Only authenticated users can read or write data
allow read, write: if request.auth != null;
}
}
}
We'll discuss security rules in detail later, but if you're in a hurry, take a look at the security rules documentation.
Run the app and sign in. Then tap the "Populate" button in the upper left, which will create a batch of restaurant documents, although you won't see this in the app yet.
Next, navigate to the Firestore data tab in the Firebase console. You should now see new entries in the restaurants collection:

Congratulations, you have just written data to Firestore from an iOS app! In the next section you'll learn how to retrieve data from Firestore and display it in the app.
5. Display Data from Firestore
In this section you will learn how to retrieve data from Firestore and display it in the app. The two key steps are creating a query and adding a snapshot listener. This listener will be notified of all existing data that matches the query and receive updates in real time.
First, let's construct the query that will serve the default, unfiltered list of restaurants. Take a look at the implementation of RestaurantsTableViewController.baseQuery():
return Firestore.firestore().collection("restaurants").limit(to: 50)
This query retrieves up to 50 restaurants of the top-level collection named "restaurants". Now that we have a query, we need to attach a snapshot listener to load data from Firestore into our app. Add the following code to the RestaurantsTableViewController.observeQuery() method just after the call to stopObserving().
listener = query.addSnapshotListener