Firebase Tutorial: Getting Started
Learn Firebase fundamentals including saving data, real-time sync, authentication, user status and offline support. By Lea Marolt Sonnenschein.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Firebase Tutorial: Getting Started
35 mins
- Getting Started
- Setting up a Firebase Account
- Creating a Connection to Firebase
- Structuring Data
- Understanding Firebase References
- Adding New Items to the List
- Retrieving Data
- Synchronizing Data to the Table View
- Removing Items From the Table View
- Checking Off Items
- Sorting the Grocery List
- Authenticating Users
- Registering Users
- Logging Users In
- Observing Authentication State
- Setting the User in the Grocery List
- Logging Users Out
- Monitoring Users’ Online Status
- Updating the Online User Count
- Displaying a List of Online Users
- Enabling Offline
- Where to Go From Here?
Adding New Items to the List
At the bottom of GroceryListTableViewController.swift, find addItemDidTouch(_:)
.
Here, you present the user with a UIAlertController
to add a new item.
Inside the method, locate saveAction
. Currently, it only saves the data to a local array, so saveAction
won’t sync across multiple clients and disappears when you restart the app.
Nobody wants to use an app that doesn’t remember or sync their grocery list! Replace saveAction
with:
let saveAction = UIAlertAction(title: "Save", style: .default) { _ in
// 1
guard
let textField = alert.textFields?.first,
let text = textField.text,
let user = self.user
else { return }
// 2
let groceryItem = GroceryItem(
name: text,
addedByUser: user.email,
completed: false)
// 3
let groceryItemRef = self.ref.child(text.lowercased())
// 4
groceryItemRef.setValue(groceryItem.toAnyObject())
}
Here you:
- Get the text field and its text from the alert controller.
- Using the current user’s data, you create a new
GroceryItem
. - Then you create a child reference using
child(_:)
. This reference’s key value is the item’s name in lowercase, so when users add duplicate items, even if they capitalize them, or use mixed case, the database saves only the latest entry. - Use
setValue(_:)
to save data to the database. This method expects aDictionary
.GroceryItem
has a helper method calledtoAnyObject()
to turn it into aDictionary
.
You need to change the database settings too. Go to the Firebase dashboard in your browser. Under Build on the left, select Realtime Database:
Click Create Database on the right:
Select your country and click Next:
In the dialog for Security rules for Cloud Firestore, select Start in test mode. By default, the Realtime Database requires user authentication for reading and writing. Click Enable.
You now see your very own Realtime Database:
Select Rules and verify the following in the editor:
{
"rules": {
".read": true,
".write": true
}
}
If they don’t match, replace them. Then, click Publish to save your changes.
Build and run. In the Firebase dashboard, select Data and position the browser window next to the simulator. When you add an item in the simulator, you’ll see it appear in the dashboard:
Now you have a grocery list app that adds data to Firebase in real time! However, while this key feature works, the table view doesn’t show any of this data.
You’ll get that data to sync from the database to the table view next.
Retrieving Data
You retrieve data in Firebase by attaching an asynchronous listener to a reference using observe(_:with:)
.
In GroceryListTableViewController.swift, add the following to the end of viewWillAppear(_:)
:
ref.observe(.value, with: { snapshot in
print(snapshot.value as Any)
})
This method takes two parameters: an instance of DataEventType
and a closure.
The event type specifies what event you want to listen for. The code listens for a .value
event type, which reports all types of changes to the data in your Firebase database: added, removed and changed.
When the change occurs, the database updates the app with the most recent data.
The closure notifies the app of a change, passed through as an instance of DataSnapshot
. The snapshot, as its name suggests, represents the data at that specific moment in time. To access the data in the snapshot, you use value
.
Build and run. You’ll see list items log to the console as they’re added:
Optional({
pizza = {
addedByUser = "hungry@person.food";
completed = 0;
name = Pizza;
};
})
Now it’s time to display the grocery list in your table view.
Synchronizing Data to the Table View
In GroceryListTableViewController.swift, replace the previous snippet in viewWillAppear(_:)
with:
// 1
let completed = ref.observe(.value) { snapshot in
// 2
var newItems: [GroceryItem] = []
// 3
for child in snapshot.children {
// 4
if
let snapshot = child as? DataSnapshot,
let groceryItem = GroceryItem(snapshot: snapshot) {
newItems.append(groceryItem)
}
}
// 5
self.items = newItems
self.tableView.reloadData()
}
// 6
refObservers.append(completed)
Here’s a breakdown:
- You attach a listener to receive updates whenever the
grocery-items
endpoint changes. The database triggers the listener block once for the initial data and again whenever the data changes. - Then, you store the latest version of the data in a local variable inside the listener’s closure.
- The listener’s closure returns a snapshot of the latest set of data. The snapshot contains the entire list of grocery items, not just the updates. Using
children
, you loop through the grocery items. -
GroceryItem
has an initializer that populates its properties using aDataSnapshot
. A snapshot’s value is of typeAnyObject
and can be a dictionary, array, number or string. After creating an instance ofGroceryItem
, you add it to the array that contains the latest version of the data. - You replace
items
with the latest version of the data, then reload the table view so it displays the latest version. - Store a reference to the listener block so you can remove it later.
In viewDidDisappear(_:)
, add:
refObservers.forEach(ref.removeObserver(withHandle:))
refObservers = []
Here, you iterate over all the observers you’ve previously added and remove them from ref
.
Build and run. Add an item, and it’ll show up in the table view.
The best part: you don’t need a pull-to-refresh! The list updates in real time!
Removing Items From the Table View
The table view will synchronize on any change to your data. However, there’s nothing to update Firebase when the user decides not to get that pizza.
To notify the database of a deletion, you need to set a Firebase reference to delete an item when the user swipes it away.
Locate tableView(_:commit:forRowAt:)
. Right now, this method removes a grocery item from the local array using the index path’s row. It works, but there’s a better way.
Replace the existing implementation with:
if editingStyle == .delete {
let groceryItem = items[indexPath.row]
groceryItem.ref?.removeValue()
}
Firebase follows a unidirectional data flow model, so the listener in viewWillAppear(_:)
notifies the app of the grocery list’s latest value. Removing an item triggers a value change.
You use the index path’s row to retrieve the corresponding grocery item. Each GroceryItem
has a Firebase reference property named ref
and calling removeValue()
on that reference makes the listener you defined in viewWillAppear(_:)
fire. The listener has a closure attached that reloads the table view using the latest data.
Build and run. Swipe an item, tap delete and watch it vanish from both your app and in Firebase.
Nice work! Your items now delete in real time.