Creating a Backend for Your iOS App Using Firebase – SitePoint
Creating a Backend for Your iOS App Using Firebase
Firebase is a Backend as a Service platform that can help you quickly develop and deploy your application. It provides a myriad of features including Realtime Database, Authentication (with email and password, Facebook, Twitter, GitHub and Google), Cloud Messaging, Storage, Hosting, Remote Config, Test Lab, Crash Reporting, Notification, App Indexing, Dynamic Links, Invites, AdWords and AdMob.
In this article, we’ll create a plain To Do app that will demonstrate how to save and retrieve data from Firebase, how to authenticate users, set read/write permissions on the data and validate the data on the server.
Getting Began
To get began, very first download the repository that contains the starter project that we’ll use in the tutorial and then head over to Firebase and create an account if you don’t already have one.
When you run the downloaded project, you will see a Login view.
The Login view has a Register button that takes you to the Sign Up view when tapped; and on this view, there is a Sign Up form as well as a Login button that takes you back to the Login view.
To add the Firebase libraries to the project, we’ll use CocoaPods. Very first make sure that you have CocoaPods installed on your computer.
Open Terminal and navigate to the root of the downloaded project with cd Path/To/ToDo\ App (the \ escapes the whitespace in the directory name).
Create a Podfile with the following guideline.
Then open the Podfile with:
Modify the file’s content as shown below.
The Firebase library consists of different subspecs. In the above, we include the /Auth subspec which is used for authentication and the /Database subspec that is needed to work with the Firebase realtime database. The /Core subspec is required for your app to work with Firebase, but we don’t have to include it in the Podfile because the subspecs we added depend on it. Therefore, when we run pod install to fetch the project’s dependencies, the /Core library will be fetched as well.
Run pod install to fetch the project’s dependencies. After installation is finish, close the Xcode project and open ToDo App.xcworkspace.
Next, head over to the Firebase console and click on the Create a Fresh Project button. A dialog window will pop up for you to input the project’s details. Inject a name and country/region for the project.
The country/region represents the country/region of your organisation/company. Your selection also sets the suitable currency for your revenue reporting. After setting a name (I used ToDoApp) and region for the project, click on the Create Project button. A project will be created and its console opened. From the project’s console, click on the Add Firebase to your iOS App option. Then come in your iOS project data in the window that pops up.
We’ll leave the App Store ID field blank, but if you are integrating Firebase to an app that is on the App Store, you can find the ID in your app’s URL. In the example below, one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine is the App Store ID. https://itunes.apple.com/gb/app/yourapp/id123456789
Click on the Add App button and a GoogleService-Info.plist file will be downloaded to your machine. Stir the file into the root of your Xcode project and add it to all targets.
The plist file contains configuration settings that the iOS app needs to communicate with the Firebase servers. It contains such things as the URL to the Firebase project, the API key, e.t.c. In the previous version of Firebase, you had to store these by hand in your app’s code, but now the process has been simplified with the use of one file that contains the needed data.
If you are using versioning and storing your code in a public repository, you should consider not making the GoogleService-Info.plist publicly accessible. If it’s used beyond its thresholds, it will stop working and if you are on a paid plan, you wouldn’t want anyone manhandling that and running up your costs.
On the Firebase console, press Proceed to stir forward in the project setup.
We’ve already done the above steps, so click on Proceed.
Go after the instructions on the last page of the dialog and add the Firebase initialisation code to your AppDelegate class. This connects to Firebase when the app starts.
Add the following import to AppDelegate.swift
Then add the following to application(_: didFinishLaunchingWithOptions:) , before the come back statement.
On the Firebase console, click on Finish to accomplish the project setup.
Security and Rules
Before retrieving and saving data from the Firebase server, we’ll set up authentication and add rules that restrict access to data and validate user input before it’s saved.
Authentication
The Firebase API enables you to setup email/password, Facebook, Twitter, GitHub, Google and anonymous authentication. In our app, we’ll use email/password authentication.
To enable email/password authentication, select Authentication from the left panel of the Firebase console and navigate to the Sign-In Method tab. Enable the Email/Password authentication provider and click on Save.
Back in Xcode, modify LoginViewController.swift as shown.
In the above code, we very first check whether the user is signed in in viewDidAppear() . FIRAuth.auth()?.currentUser gives the authenticated user (if any). The user is represented by a FIRUser object. If they are signed in, we call signIn() which performs a segue that is already created in the storyboard file. This segue navigates to the ItemsTableViewController which is where our list of items will be shown.
didTapSignIn() sends the user’s input to Firebase for authentication. FIRAuth.auth()?.signIn(withEmail: password: completion:) is used to sign in a user using the email and password combination. Each of the authentication providers (e.g. Google, Facebook, Twitter, Github, e.t.c) uses a different method call.
When the user is successfully authenticated, signIn() is called, otherwise an error message will be shown to the user. If authentication fails, an NSError object is returned back from the server. It carries an error code that you can use to determine the cause of the error and give the user an suitable error message. In the above code, we give different error messages when the user comes in a non existent account or when they come in the wrong password. For other errors, we demonstrate the user a description of the error with error.localizedDescription . In a real app, you might not want to showcase this to the user. The message returned by error.localizedDescription is best suited for the developer when debugging, but for your users, you should use a better catch-all error message.
didRequestPasswordReset() is called when the Left behind Password button is tapped. Here, we create an alert box that the user can use to inject an email address where the Password Reset email will be sent. This is another of the many advantages suggested by Firebase. Password reset functionality has already been set up; you don’t have to code it up yourself.
If you look at the Firebase control under Authentication > Email templates, you can modify the email message sent to users for Email address verification, Password reset and Email address switch.
Note that in the code above, the alert is called in the main thread by using DispatchQueue.main.async and not in the background thread that treats the callback function. Any code that updates the app’s UI should be run in the main queue.
To test the password reset functionality, you can create a User on the Firebase console under Authentication > Users > Add User. Come in an email and password for the User (use a real email if you want to receive the password reset email).
Back in Xcode, run the project. You should be able to come in an email and have the password reset email sent to it.
With the Login functionality done, let us now add the Sign Up functionality. Modify SignUpViewController as shown.
In the above code, when the user taps on the Create Account button didTapSignUp() is called. Here, we take the user’s input and call FIRAuth.auth()?.createUser(withEmail: password: completion:) to attempt and create an account with the entered details. If signup fails, an error message is shown, otherwise signIn() is called which performs a segue to the ItemsTableViewController. This segue had already been created in the storyboard file of the starter project.
Run the app. You should be able to create an account and confirm its creation on the Firebase console. If you had previously logged in, and thus can’t get to the Login view, you can reset the simulator with Simulator > Reset Content and Settings.
By default, Firebase sets a rule on the accepted length of the password – it must be at least six characters long. You can set more rules on this, on the Firebase Console. Shortly, we’ll see how to set up rules. More on this here.
Authorization and Data Validation
The Firebase Realtime Database provides an expression-based rules language with JavaScript-like syntax to lightly define how your data should be structured, how it should be indexed, and when your data can be read from and written to. Combined with our authentication services, you can define who has access to what data and protect your users’ private information from unauthorized access.
We’ve set up authentication for our project, but we can make users data even more secure by defining some rules for it.
By default, Firebase has the following rules viewable by navigating to Database > Rules on the console.
The above security rules require users to be authenticated to read or write any data to the database. Modify the rules as shown and hit Publish.
In the above, auth != null && auth.uid == $uid has been set on the read and write permissions. With this rule, not only will the user need to be authenticated to read or write any data to the database, but they will also only have access to their own data.
Firebase stores data in JSON format. In our database, each user will have an array of to-do items named items . Each item will have a title . In the above, we add some validation that ensures that an item with an empty title won’t be saved.
Saving Data
With authentication and authorization done, we’ll now see how data can be saved and retrieved to and from Firebase.
Very first create a fresh file called Item.swift and modify it as shown. This will be the Item model class Each Item will have a title and ref which will hold a FIRDatabaseReference object. A FIRDatabaseReference represents a particular location in your Firebase Database and can be used for reading or writing data to that Firebase Database location.
Then modify ItemsTableViewController as shown.
In the above, we begin off by instantiating some variables in viewDidLoad() . We set user with the value of the logged in user and then set ref with a FIRDatabaseReference object. FIRDatabase.database().reference() gets a FIRDatabaseReference for the root of your Firebase Database. We then call startObservingDatabase() .
In startObservingDatabase() we set a listener for any switches to the database. Firebase data is retrieved by linking an asynchronous listener to a FIRDatabase reference. The listener is triggered once for the initial state of the data and again anytime the data switches. To add an event listener, we use the observeEventType() method to specify an event type and callback block. You can listen for the following types of events:
- FIRDataEventTypeValue – Read and listen for switches to the entire contents of a path.
- FIRDataEventTypeChildAdded – Retrieve lists of items or listen for additions to a list of items. Suggested use with FIRDataEventTypeChildChanged and FIRDataEventTypeChildRemoved to monitor switches to lists.
- FIRDataEventTypeChildChanged – Listen for switches to the items in a list. Use with FIRDataEventTypeChildAdded and FIRDataEventTypeChildRemoved to monitor switches to lists.
- FIRDataEventTypeChildRemoved – Listen for items being eliminated from a list. Use with FIRDataEventTypeChildAdded and FIRDataEventTypeChildChanged to monitor switches to lists.
- FIRDataEventTypeChildMoved – Listen for switches to the order of items in an ordered list. FIRDataEventTypeChildMoved events always go after the FIRDataEventTypeChildChanged event that caused the item’s order to switch (based on your current order-by method).
We listen for the FIRDataEventTypeValue event. You use the FIRDataEventTypeValue event to read the data at a given path, as it exists at the time of the event. This method is triggered once when the listener is linked and again every time the data, including any children, switches. The event callback is passed a snapshot containing all data at that location, including child data. If there is no data, the value of the snapshot returned is nil .
Note that the FIRDataEventTypeValue event is fired every time data is switched at the specified database reference, including switches to children. To limit the size of your snapshots, you should fasten only at the highest level needed for watching switches. For example, linking a listener to the root of your database is not recommended. In our code, we link the listener to /users//items which is the path that will hold a user’s items.
The listener receives a FIRDataSnapshot that contains the data at the specified location in the database at the time of the event in its value property. If no data exists at the location, the value is nil .
From the snapshot, we create Item objects set them to the items array that is used to populate the table view. We then reload the table view to reflect the data switch.
numberOfSectionsInTableView() , tableView(_: numberOfRowsInSection:) and tableView(_: cellForRowAtIndexPath) are the usual table view functions used to set the table view’s data.
The app contains an Add button in its navigation bar, which when tapped, calls didTapAddItem() . Here, we display the user an Alert box that they can use to add an item to the table view. We save the value added by the user to /users//items//title/ .
There are four methods for writing data to the Firebase Realtime Database:
- setValue – Write or substitute data to a defined path, such as users/<user-id>/<username> .
- childByAutoId – Add to a list of data. Every time you call childByAutoId , Firebase generates a unique key that can also be used as a unique identifier, such as user-posts/<user-id>/<unique-post-id> .
- updateChildValues – Update some of the keys for a defined path without substituting all of the data.
- runTransactionBlock – Update sophisticated data that could be corrupted by concurrent updates.
The child() function grabs the reference of a particular knot if it exists or creates it if it doesn’t exist. We use childByAutoId() to set a unique ID to every item that will be created. We then use setValue() to add the user’s input as the value for item’s title .
tableView(_: commitEditingStyle: forRowAtIndexPath:) makes the table view editable. With this, the user will be able to swipe on an item to delete it. We call removeValue() with a reference to the swiped on item. This eliminates the data at that location in the database. After deleting an item, we don’t have to make any switches to the table view. Reminisce that we set a listener to any switches made on the ../items/ path on the database, so the callback we set for this will be responsible for updating the table.
didTapSignOut() is called when the Sign Out button on the app’s navigation bar is tapped. Here we sign out the user and perform a segue back to the Login screen.
Run the app and you should be able to add an item.
Any item you add will be added to the table view.
Check the Firebase Console, and you will see the added data.
To delete an item, swipe on it to expose a Delete button.
That brings us to the end of the tutorial. We’ve seen how to save and retrieve data to and from the Firebase realtime database, and how to set up authentication and data authorization. The data saving we’ve looked at is ideal for elementary data types such as NSString , NSNumber , NSArray and NSDictionary . If you want to save files like pictures or documents to Firebase, then look into Firebase Storage.
The tutorial was a quick intro to using Firebase, it was not an exhaustive look into all that Firebase does. For this, be sure to read through the documentation. You can download the ended project here. Reminisce to add the GoogleService-Info.plist file generated from Firebase to the project.
You might have noticed some warnings in your project after adding the Firebase libraries, or on running the ended project if you downloaded it. The warning error message states Conflicting nullability specifier on come back types, ‘nullable’ conflicts with existing specifier ‘nonnull’ . This little bug came with the update of Firebase to Swift three and the issue has been filed. If you look at the message threads in that last link, the issue has been stationary, but switches won’t be seen in Firebase until the next release. The app still works despite this.