Firebase is a powerful platform that provides various services and tools to help you build, improve and grow your Swift apps. Whether you need authentication, database, storage, analytics or anything in between, Firebase has you covered. In this article, I will show you how to use Firebase to add some awesome features to your Swift apps with minimal code and effort.
Firebase and Swift
What is Firebase?
Firebase is a collection of cloud-based services that can be integrated into your apps using SDKs or APIs. Firebase offers a wide range of functionalities, such as:
- Authentication: Firebase Authentication lets you easily implement secure sign-in and sign-up flows for your users using various methods, such as email/password, phone number, social providers (Google, Facebook, Twitter, etc.), or custom tokens.
- Database: Firebase Realtime Database and Cloud Firestore are scalable and flexible NoSQL databases that store and sync data across devices in real time. You can use them to store and query user data, app state, chat messages, game scores and more.
- Storage: Firebase Storage lets you store and serve user-generated content, such as images, videos, audio and other files. You can use it to upload and download files securely with built-in authentication and authorization.
- Analytics: Firebase Analytics gives you insight into how your users are using your app, what features they like or dislike, where they come from and more. You can use it to track events, user properties, conversions and funnels.
- And many more: Firebase also offers other services such as Cloud Functions (serverless backend code), Cloud Messaging (push notifications), Crashlytics (crash reporting), Remote Config (dynamic app configuration), A/B Testing (experimentation), Performance Monitoring (app performance measurement) and more.
How to Add Firebase to Your Swift App
Adding Firebase to your Swift app is easy and straightforward. You just need to follow these steps:
- Create a Firebase project in the Firebase console.
- Register your app with your Firebase project by providing your app’s bundle ID.
- Download the
**GoogleService-Info.plist**
file from the console and add it to your Xcode project. Add Firebase SDKs to your app using one of the following methods:
CocoaPods: Add
**pod 'Firebase/Analytics'**
to your**Podfile**
and run**pod install**
. This will install the core Firebase SDK and the Analytics SDK. You can add other Firebase pods as needed.- Swift Package Manager: Add
**https://github.com/firebase/firebase-ios-sdk.git**
as a package dependency in Xcode and select the Firebase products you want to use. Carthage: Add
**binary "<https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json>"**
to your**Cartfile**
and run**carthage update --platform iOS**
. This will install the core Firebase SDK and the Analytics SDK. You can add other Firebase binaries as needed.Initialize Firebase in your app by adding
**import Firebase**
at the top of your**AppDelegate.swift**
file and**FirebaseApp.configure()**
in the**application(_:didFinishLaunchingWithOptions:)**
method.
That’s it! You have successfully added Firebase to your Swift app. Now you can start using Firebase services in your app.
How to Use Firebase Services in Your Swift App
To demonstrate how to use some of the Firebase services in your Swift app, I will create a simple chat app that lets users sign in with their email and password, send and receive messages in real time, and upload and download images from Firebase Storage.
Authentication
To enable email/password authentication in my app, I need to do the following:
- Enable the email/password sign-in method in the Firebase console under Authentication > Sign-in method.
- Create a sign-in/sign-up screen in my app that lets users enter their email and password and perform the corresponding actions using the
**Auth**
class from the Firebase SDK.
Here is some example code for signing in a user:
import UIKit
import Firebase
class SignInViewController: UIViewController {
// UI outlets
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var signInButton: UIButton!
@IBOutlet weak var signUpButton: UIButton!
// Auth reference
let auth \= Auth.auth
// Sign in button action
@IBAction func signInButtonTapped(_ sender: UIButton) {
// Get the email and password from the text fields
guard let email \= emailTextField.text, !email.isEmpty,
let password \= passwordTextField.text, !password.isEmpty else {
// Show an alert if the fields are empty
showAlert(title: "Error", message: "Please enter your email and password.")
return
}
// Sign in the user with Firebase Auth
auth.signIn(withEmail: email, password: password) { result, error in
if let error \= error {
// Show an alert if there is an error
showAlert(title: "Error", message: error.localizedDescription)
} else {
// Go to the chat screen if the sign in is successful
self.performSegue(withIdentifier: "goToChat", sender: self)
}
}
}
// Sign up button action
@IBAction func signUpButtonTapped(_ sender: UIButton) {
// Get the email and password from the text fields
guard let email \= emailTextField.text, !email.isEmpty,
let password \= passwordTextField.text, !password.isEmpty else {
// Show an alert if the fields are empty
showAlert(title: "Error", message: "Please enter your email and password.")
return
}
// Create a user with Firebase Auth
auth.createUser(withEmail: email, password: password) { result, error in
if let error \= error {
// Show an alert if there is an error
showAlert(title: "Error", message: error.localizedDescription)
} else {
// Go to the chat screen if the user creation is successful
self.performSegue(withIdentifier: "goToChat", sender: self)
}
}
}
// Helper function to show an alert
func showAlert(title: String, message: String) {
let alert \= UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction \= UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
With this code, I can let users send messages to the database using Firebase Realtime Database. I can also access the messages from the database using **database.child("messages").observe**
or **database.child("messages").observeSingleEvent**
.
Database
To store and sync messages in real time in my app, I need to do the following:
- Create a database instance in the Firebase console under Realtime Database > Create database. I can choose either the US or Europe location and either the locked mode or test mode security rules. For this demo, I will choose the test mode which allows anyone to read and write to the database. However, this is not recommended for production apps and you should always use proper security rules to protect your data.
- Create a chat screen in my app that lets users send and receive messages using the
Database
class from the Firebase SDK.
Here is some example code for sending and receiving messages:
import UIKit
import Firebase
class ChatViewController: UIViewController {
// UI outlets
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var messageTextField: UITextField!
@IBOutlet weak var sendButton: UIButton!
// Auth reference
let auth \= Auth.auth()
// Database reference
let database \= Database.database().reference()
// Messages array
var messages \= [Message]()
override func viewDidLoad() {
super.viewDidLoad()
// Set up table view data source and delegate
tableView.dataSource \= self
tableView.delegate \= self
// Register custom cell nib file
tableView.register(UINib(nibName: "MessageCell", bundle: nil), forCellReuseIdentifier: "MessageCell")
// Load messages from database
loadMessages()
}
// Send button action
@IBAction func sendButtonTapped(_ sender: UIButton) {
// Get the message text from the text field
guard let messageText \= messageTextField.text, !messageText.isEmpty else {
// Do nothing if the text field is empty
return
Storage
To upload and download images in my app, I need to do the following:
- Create a storage bucket in the Firebase console under Storage > Get started. I can choose either the US or Europe location and either the default or custom security rules. For this demo, I will choose the default rules which allow anyone to read and write to the storage bucket. However, this is not recommended for production apps and you should always use proper security rules to protect your files.
- Create an image picker screen in my app that lets users select an image from their photo library or camera and upload it to the storage bucket using the
**Storage**
class from the Firebase SDK.
Here is some example code for uploading an image:
import UIKit
import Firebase
class ImagePickerViewController: UIViewController {
// UI outlets
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var uploadButton: UIButton!
// Auth reference
let auth \= Auth.auth()
// Storage reference
let storage \= Storage.storage().reference()
// Image picker controller
let imagePickerController \= UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
// Set up image picker controller delegate and source type
imagePickerController.delegate \= self
imagePickerController.sourceType \= .photoLibrary
}
// Upload button action
@IBAction func uploadButtonTapped(_ sender: UIButton) {
// Present the image picker controller
present(imagePickerController, animated: true, completion: nil)
}
}
// Extension to conform to the image picker controller delegate and navigation controller delegate protocols
extension ImagePickerViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// Method to handle when an image is selected or cancelled
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
// Dismiss the image picker controller
picker.dismiss(animated: true, completion: nil)
// Get the selected image from the info dictionary
guard let image \= info[.originalImage] as? UIImage else {
// Show an alert if there is no image
showAlert(title: "Error", message: "No image selected.")
return
}
// Set the image view to display the selected image
imageView.image \= image
// Get the current user's email
guard let senderEmail \= auth.currentUser?.email else {
// Show an alert if the user is not signed in
showAlert(title: "Error", message: "You are not signed in.")
return
}
// Generate a unique file name for the image using the sender email and current timestamp
let fileName \= "\\(senderEmail)_\\(Date().timeIntervalSince1970).jpg"
// Create a reference to the image file in the storage bucket under the "images" folder
let imageRef \= storage.child("images/\\(fileName)")
// Convert the image to JPEG data with 80% quality
guard let imageData \= image.jpegData(compressionQuality: 0.8) else {
// Show an alert if there is an error
showAlert(title: "Error", message: "Unable to convert image to JPEG data.")
return
}
// Upload the image data to the storage reference
imageRef.putData(imageData, metadata: nil) { metadata, error in
if let error \= error {
// Show an alert if there is an error
showAlert(title: "Error", message: error.localizedDescription)
} else {
// Show an alert if the upload is successful
showAlert(title: "Success", message: "Image uploaded successfully.")
}
}
}
// Method to handle when the image picker controller is cancelled
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
// Dismiss the image picker controller
picker.dismiss(animated: true, completion: nil)
}
// Helper function to show an alert
func showAlert(title: String, message: String) {
let alert \= UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction \= UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
With this code, I can let users upload images to the storage bucket using Firebase Storage. I can also access the images from the storage bucket using **storage.child("images/\\(fileName)").getData**
or **storage.child("images/\\(fileName)").downloadURL**
.
Displaying Images in Messages
To display images in messages in my app, I need to do the following:
- Modify the
**Message**
class to include an optional**imageURL**
property that stores the URL of the image file in the storage bucket. - Modify the
**MessageCell**
class to include an optional**imageView**
that displays the image if it exists. - Modify the
**ChatViewController**
class to handle sending and receiving messages with images.
Here is some example code for displaying images in messages:
// Message class
class Message {
// Properties
let text: String
let sender: String
let date: Double
let imageURL: String?
// Initializer
init(text: String, sender: String, date: Double, imageURL: String? \= nil) {
self.text \= text
self.sender \= sender
self.date \= date
self.imageURL \= imageURL
}
}
// Message cell class
class MessageCell: UITableViewCell {
// UI outlets
@IBOutlet weak var messageLabel: UILabel!
@IBOutlet weak var messageImageView: UIImageView!
// Configure cell with message object
func configure(with message: Message) {
// Set the message label text to the message text
messageLabel.text \= message.text
// Check if the message has an image URL
if let imageURL \= message.imageURL {
// Download the image data from the URL
URLSession.shared.dataTask(with: URL(string: imageURL)!) { data, response, error in
if let data \= data {
// Create an image from the data
let image \= UIImage(data: data)
// Update the UI on the main thread
DispatchQueue.main.async {
// Set the message image view image to the image
self.messageImageView.image \= image
// Show the message image view
self.messageImageView.isHidden \= false
}
}
}.resume()
} else {
// Hide the message image view if there is no image URL
messageImageView.isHidden \= true
}
}
}
// Chat view controller class
class ChatViewController: UIViewController {
// UI outlets
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var messageTextField: UITextField!
@IBOutlet weak var sendButton: UIButton!
// Auth reference
let auth \= Auth.auth()
// Database reference
let database \= Database.database().reference()
// Storage reference
let storage \= Storage.storage().reference()
// Image picker controller
let imagePickerController \= UIImagePickerController()
// Messages array
var messages \= [Message]()
override func viewDidLoad() {
super.viewDidLoad()
// Set up table view data source and delegate
tableView.dataSource \= self
tableView.delegate \= self
// Register custom cell nib file
tableView.register(UINib(nibName: "MessageCell", bundle: nil), forCellReuseIdentifier: "MessageCell")
// Set up image picker controller delegate and source type
imagePickerController.delegate \= self
imagePickerController.sourceType \= .photoLibrary
// Load messages from database
loadMessages()
}
// Send button action
@IBAction func sendButtonTapped(_ sender: UIButton) {
// Check if the message text field is empty
if messageTextField.text?.isEmpty ?? true {
// Present the image picker controller
present(imagePickerController, animated: true, completion: nil)
} else {
// Get the message text from the text field
guard let messageText \= messageTextField.text else {
// Do nothing if the text field is nil
return
}
// Get the current user's email
guard let senderEmail \= auth.currentUser?.email else {
// Show an alert if the user is not signed in
showAlert(title: "Error", message: "You are not signed in.")
return
}
// Create a message object with the message text, sender email and current timestamp
let message \= Message(text: messageText, sender: senderEmail, date: Date().timeIntervalSince1970)
// Convert the message object to a dictionary
let messageDict \= [
"text": message.text,
"sender": message.sender,
"date": message.date
] as [String : Any]
// Save the message dictionary to the database under the "messages" node with a unique key
database.child("messages").childByAutoId().setValue(messageDict) { error, reference in
if let error \= error {
// Show an alert if there is an error
showAlert(title: "Error", message: error.localizedDescription)
} else {
// Clear the text field if the message is saved successfully
self.messageTextField.text \= ""
}
}
}
}
// Helper function to show an alert
func showAlert(title: String, message: String) {
let alert \= UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction \= UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
// Extension to conform to the table view data source and delegate protocols extension ChatViewController: UITableViewDataSource, UITableViewDelegate {
// Method to return the number of rows in the table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
// Method to return the cell for each row in the table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get the cell from the reusable pool
let cell \= tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessageCell
// Get the message object for the current row
let message \= messages[indexPath.row]
// Configure the cell with the message object
cell.configure(with: message)
// Return the cell
return cell
}
}
// Extension to conform to the image picker controller delegate and navigation controller delegate protocols extension ChatViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// Method to handle when an image is selected or cancelled
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
// Dismiss the image picker controller
picker.dismiss(animated: true, completion: nil)
// Get the selected image from the info dictionary
guard let image \= info[.originalImage] as? UIImage else {
// Show an alert if there is no image
showAlert(title: "Error", message: "No image selected.")
return
With this code, I can let users send and receive messages with images using Firebase Storage and Realtime Database. I can also display the images in the table view using a custom cell with an image view.
Loading Messages from Database
To load messages from the database in my app, I need to do the following:
- Create a
**loadMessages**
function in the**ChatViewController**
class that fetches the messages from the database and updates the table view. - Call the
**loadMessages**
function in the**viewDidLoad**
method of the **ChatViewController
**class.
Here is some example code for loading messages from database:
// Chat view controller class
class ChatViewController: UIViewController {
// UI outlets
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var messageTextField: UITextField!
@IBOutlet weak var sendButton: UIButton!
// Auth reference
let auth \= Auth.auth()
// Database reference
let database \= Database.database().reference()
// Storage reference
let storage \= Storage.storage().reference()
// Image picker controller
let imagePickerController \= UIImagePickerController()
// Messages array
var messages \= [Message]()
override func viewDidLoad() {
super.viewDidLoad()
// Set up table view data source and delegate
tableView.dataSource \= self
tableView.delegate \= self
// Register custom cell nib file
tableView.register(UINib(nibName: "MessageCell", bundle: nil), forCellReuseIdentifier: "MessageCell")
// Set up image picker controller delegate and source type
imagePickerController.delegate \= self
imagePickerController.sourceType \= .photoLibrary
// Load messages from database
loadMessages()
}
// Load messages from database
func loadMessages() {
// Observe the "messages" node for any changes
database.child("messages").observe(.value) { snapshot in
// Clear the messages array
self.messages \= []
// Loop through the snapshot children (each child is a message)
for child in snapshot.children {
// Cast the child as a DataSnapshot
guard let childSnapshot \= child as? DataSnapshot else {
// Continue to the next child if the cast fails
continue
}
// Get the message dictionary from the child snapshot value
guard let messageDict \= childSnapshot.value as? [String : Any] else {
// Continue to the next child if the value is not a dictionary
continue
}
// Get the message properties from the message dictionary
guard let text \= messageDict["text"] as? String,
let sender \= messageDict["sender"] as? String,
let date \= messageDict["date"] as? Double else {
// Continue to the next child if any of the properties are missing or have wrong types
continue
}
// Get the optional image URL from the message dictionary
let imageURL \= messageDict["imageURL"] as? String
// Create a message object with the message properties and optional image URL
let message \= Message(text: text, sender: sender, date: date, imageURL: imageURL)
// Append the message object to the messages array
self.messages.append(message)
}
// Sort the messages array by date in ascending order
self.messages.sort { $0.date < $1.date }
// Reload the table view on the main thread
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
// Send button action
@IBAction func sendButtonTapped(_ sender: UIButton) {
// Check if the message text field is empty
if messageTextField.text?.isEmpty ?? true {
// Present the image picker controller
present(imagePickerController, animated: true, completion: nil)
} else {
// Get the message text from the text field
guard let messageText \= messageTextField.text else {
// Do nothing if the text field is nil
return
}
// Get the current user's email
guard let senderEmail \= auth.currentUser?.email else {
// Show an alert if the user is not signed in
showAlert(title: "Error", message: "You are not signed in.")
return
}
// Create a message object with the message text, sender email and current timestamp
let message \= Message(text: messageText, sender: senderEmail, date: Date().timeIntervalSince1970)
// Convert the message object to a dictionary
let messageDict \= [
"text": message.text,
"sender": message.sender,
"date": message.date
] as [String : Any]
// Save the message dictionary to the database under the "messages" node with a unique key
database.child("messages").childByAutoId().setValue(messageDict) { error, reference in
if let error \= error {
// Show an alert if there is an error
showAlert(title: "Error", message: error.localizedDescription)
} else {
// Clear the text field if the message is saved successfully
self.messageTextField.text \= ""
}
}
}
}
// Helper function to show an alert
func showAlert(title: String, message: String) {
let alert \= UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction \= UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
// Extension to conform to the table view data source and delegate protocols extension ChatViewController: UITableViewDataSource, UITableViewDelegate {
// Method to return the number of rows in the table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
// Method to return the cell for each row in the table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get the cell from the reusable pool
let cell \= tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessageCell
// Get the message object for the current row
let message \= messages[indexPath.row]
// Configure the cell with the message object
cell.configure(with: message)
// Return the cell
return cell
}
}
// Extension to conform to the image picker controller delegate and navigation controller delegate protocols extension ChatViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// Method to handle when an image is selected or cancelled
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
// Dismiss the image picker controller
picker.dismiss(animated: true, completion: nil)
// Get the selected image from the info dictionary
guard let image \= info[.originalImage] as? UIImage else {
// Show an alert if there is no image
showAlert(title: "Error", message: "No image selected.")
return
}
// Get the current user's email
guard let senderEmail \= auth.currentUser?.email else {
// Show an alert if the user is not signed in
showAlert(title: "Error", message: "You are not signed in.")
return
}
// Generate a unique file name for the image using the sender email and current timestamp
let fileName \= "\\(senderEmail)_\\(Date().timeIntervalSince1970).jpg"
// Create a reference to the image file in the storage bucket under the "images" folder
let imageRef \= storage.child("images/\\(fileName)")
// Convert the image to JPEG data with 80% quality
guard let imageData \= image.jpegData(compressionQuality: 0.8) else {
// Show an alert if there is an error
showAlert(title: "Error", message: "Unable to convert image to JPEG data.")
return
}
// Upload the image data to the storage reference
imageRef.putData(imageData, metadata: nil) { metadata, error in
if let error \= error {
// Show an alert if there is an error
showAlert(title: "Error", message: error.localizedDescription)
} else {
// Get the download URL of the image file
imageRef.downloadURL { url, error in
if let url \= url {
// Create a message object with the image URL, sender email and current timestamp
let message \= Message(text: "", sender: senderEmail, date: Date().timeIntervalSince1970, imageURL: url.absoluteString)
// Convert the message object to a dictionary
let messageDict \= [
"text": message.text,
"sender": message.sender,
"date": message.date,
"imageURL": message.imageURL
] as [String : Any]
// Save the message dictionary to the database under the "messages" node with a unique key
self.database.child("messages").childByAutoId().setValue(messageDict) { error, reference in
if let error \= error {
// Show an alert if there is an error
self.showAlert(title: "Error", message: error.localizedDescription)
} else {
// Show an alert if the message is saved successfully
self.showAlert(title: "Success", message: "Image sent successfully.")
}
}
}
}
}
}
}
// Method to handle when the image picker controller is cancelled
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
// Dismiss the image picker controller
picker.dismiss(animated: true, completion: nil)
}
}
Conclusion
In this article, I have shown you how to use Firebase to add some awesome features to your Swift apps with minimal code and effort. Firebase is a powerful platform that provides various services and tools to help you build, improve and grow your Swift apps. Whether you need authentication, database, storage, analytics or anything in between, Firebase has you covered. I hope you enjoyed this article and learned something new. Happy coding! 😊