Scratching the Firebase services with your iOS app

Deyan Aleksandrov
12 min readApr 12, 2018

It was one of those sunny days, when I was back home and finally had the time to plan the foodie app from my bucket list. For better or worse, it was not only the app design that I needed to work on but also:

  • Authentication.
  • Database where I can save and retrieve stuff — data, info, images.
  • A searchable food API.

Probably, I needed a serious someone to join my little team of just myself. Or, could I do it alone? More or less 😃. Short answer — yes! Because I found a decently priced searchable food API on one hand, and Firebase on the other. Long answer? Keep reading!

But What is Firebase?

Firebase is a unified platform for mobile developers (yes, both Android and iOS) that provides a whole bunch of awesome features some of which are Realtime Database, Crash Reporting, Analytics, Authentication and etc. It is best to check out the whole list at their official website.

Article Roadmap

The last article I wrote included a project named Unicorner which was about receiving remote notifications with images of unicorns fellow Unicorner users saw and photographed. This time, the project will be about taking pictures of unicorns and uploading them to the database. The data from the database will then be fed to a list where the uploaded pictures and info can be seen. Notifications will not be sent but we can work on them in another article, right?

Anyways, this is the planned article workflow:

  • Firebase account registration and iOS app configuration.
  • Setting up the Demo project in Xcode.
  • Write some code for communicating with the Firebase Realtime Database and Storage.

Setting up a Firebase Account

This is quite simple actually. Go to this link first and then click the Sign In button at the top right of the screen. The sign-in can be done with a gmail account (most people have one) or by creating a new account following the registration steps.

When done, go to the Firebase Console by tapping Go To Console, on the top right again. And this is to be seen:

Welcome-to-Firebase Screen

I already have one app project that I am working on in my Firebase Console. To add another one, just tap Add project and see this modal come up:

Add-a-Project Modal Screen

The name I have chosen for my project is UnicornUploader and the region I chose is US. Choose as you prefer, and then click Create Project.

Wait a bit while the project gets created and then, voila:

Project-is-Ready Modal Screen

After tapping Continue, you will be taken to your Project Overview page:

Project Overview Screen

This is the place where all the goodies that Firebase provides can be accessed. Indeed, in this article I am to focus on three only — Authentication, Database and Storage. You make sure to check them all out when the time allows.

Add Firebase to iOS app

Time to add Firebase to my iOS app by selecting Add Firebase to your iOS app:

Register-iOS-App Modal Screen

I have already put in the iOS Bundle ID of my demo project, you do the same for yours, and then tap Register App.

After some loading, Xcode instructions come up, better follow them! Download that GoogleService-Info.plist file and add it to the Xcode project:

Download-GoogleService-Info.plist-File Modal Screen

After tapping Continue, step 3 includes instructions on how to add Firebase to the project in the form of a Cocoapod dependency:

Add-Firebase-SDK Modal Screen

No worries, if you are using my demo project, I have done the Cocoapod set-up for all the dependencies needed. Do make sure to use the .xcworkspace file though. Click Continue again and see the last step of the Firebase set-up:

Add-Initialization-Code Modal Screen

Do that if using your own project. If using mine, no worries, again, I have added the set-up configuration in the AppDelegate file too. Click Finish and see this not-so-interesting window showing project’s app info:

Firebase-App-Activity Window

Additional Firebase Configuration

For the purpose of the demo project, there is a few things more that need being taken care of.

The first one is the Authentication Configuration. On the Firebase Console to the left, tap on Authentication:

Authentication Console Screen

You will then see this:

Set up Sign-In Method Screen

After clicking on the blue button, a whole list of Authentication Options is presented. Awesome!

Authentication Options Screenshot

For the purpose of simplicity, I am going to enable the Anonymous option only. Click Save and you are all-set for the demo project.

Enable Anonymous Guest Accounts Screenshot

Next thing that needs setting up is the Realtime Database. On the Firebase Console to the left, tap Database this time to see this:

Database-Get-Started Screen

Click the Get Started button for the Realtime Database cause that is the one needed for the demo:

Security Rules for the Realtime Database Screen

Before I enable the Realtime Database, I need to decide on the security rules. Well, for now I will choose the Start in test mode since I will only be testing. I can go ahead and click Enable and be done with the Database part for now.

Last but not least, I also need to enable the Firebase Storage. Again, on the Firebase Console to the left, tap on Storage this time and then tap on the Get Started button to see this modal window pop up:

Storage Default Security Rules Screen

You can see what the default security rules say — all writes and reads are allowed but when authenticated. So, change this piece of code:

allow read, write: if request.auth != null;

To allow for using the storage even when not authenticated:

allow read, write: if request.auth == null;

It is definitely counter-interactive, so make sure that is not how you handle authentication for a real app!

Working with the Firebase Realtime Database

Firebase Realtime Database is actually quite easy to work. The first thing to do after configuring the Firebase connection in the AppDelegate, is to make sure that Firebase is imported within the VC in question, obviously 😃. Then, establish a connection to the database, or in Firebase words — define and create a reference to your database by defining a property first, like so:

var ref: DatabaseReference!

And then, in your viewDidLoad method, instantiate the DatabaseReference to the database’s root:

ref = Database.database().reference()

Easy, right? Lets add another record of a unicorn to a list of unicorns. There it is:

ref.child("unicorns").child("Some unique ref id for unicorn").setValue(
[ "addedBy": "Tony",
"imagePath": "Some image path",
"seenAt": "Tokyo"]
)

What is done here is getting access to the “unicorns” reference in the database, then a unique reference for a unicorn is accessed, that is why a unique ID is needed. Uniqueness here helps avoid accidental overwriting. The setValue method is used to set the new value. Yeah, I have been saying “access a reference this and that” but it also means that if there is nothing to be accessed, it gets created. Sweet!

Then again, I have provided you with a super simple example. To see how it is all done the right away, do check out the official Firebase Realtime Database Documentation.

The other thing to pay attention to is structuring the data within the Firebase Realtime Database. You get to structure it like a JSON tree because all the data within is stored as JSON objects! The best instructions on how to do that can be found here.

But what would the structure for the current demo app would look like?

Well, for the Unicorn model, there are three properties — addedBy, imagePath, seenAt. Therefore, this is how a JSON object for a a list of two Unicorn objects would look like:

{
"unicorns": {
"unicorn1": {
"addedBy": "Peter",
"imagePath": "Some image path",
"seenAt": "London"
},
"unicorn2": {
"addedBy": "John",
"imagePath": "Some image path",
"seenAt": "New York"
}
}
}

Working with the Firebase Storage

Working with the Firebase Storage is quite similar and as easy. It all starts with a reference property:

var storageRef: StorageReference!

And in the viewDidLoad method, properly initialize the root StorageReference:

storageRef = Storage.storage().reference()

The storageRef can be used for a whole bunch of operations — create a reference to upload, download, or delete a file, etc. See the official documentation to check out all things possible. Within the demo project I will show how uploading and downloading an image is handled.

Demo project

Alright, let’s really get going. Since, I really want to concentrate on the Firebase part of this article, I have created a unicorn-uploaded starter project on GitHub with everything needed preloaded. So, go ahead, download it and unarchive it. Make sure you are on the master branch because the starter project is there, the finished project is under the implemented branch. Also, be certain that you open unicornUploader.xcworkspace it has the project with the needed Cocoapods installed already.

Next thing to do is to go ahead, build the project and take the time to check it out. It is quite simple actually, it only has two view controllers embedded in a navigation controller and a few special classes:

  • UnicornsTableViewController.swift — standard UITableViewController implementation, nothing special, to be populated with a list of all the uploaded unicorns.
  • UploadUnicornViewController.swift — standard UIImagePickerController implementation that is left purposefully unfinished. You can click buttons but nothing happens. This is also the place to be used for sending records to the Firebase Realtime Database.
  • In the Models folder, there is a Unicorn.swift with a Unicorn class.
  • In the Cells folder, there is a UnicornTableViewCell.swift file that contains the custom class for the cell used in the table view in the UnicornsTableViewController.swift.

The Unicorn model

I already showed you a simple way of writing to the database. What if the conversion of the model properties to JSON can be hidden? They can and writing to the database could look like this:

ref.child("unicorns").child("Some unique ref id for unicorn").setValue(unicorn.toAnyObject())

Official documentation says we can observe data like this(adjusted for a single unicorn example):

ref.observe(.value) { (snapshot) in
let unicorn = snapshot.value as? [String : AnyObject] ?? [:]
// ...
}

What if the conversion of the JSON to model properties can be hidden too? It can, and after tweaking the unicorn model do this instead of the above:

ref.observe(.value) { (snapshot) in
if let unicorn = Unicorn(snapshot: snapshot as? DataSnapshot) {
// .... }
}

To achieve both of above, the Unicorn model should be changed to this:

Updated Unicorn Model to Handle Database Writes and Reads

The UploadUnicornViewController class

The first thing to do here is to get the references for the Realtime Database and for the Storage. Put these two lines before the picker property:

fileprivate var ref: DatabaseReference!
fileprivate var storageRef: StorageReference!

And then within the viewDidLoad method:

ref = Database.database().reference()
storageRef = Storage.storage().reference()

Alright, nothing too complicated so far! Still, before a new unicorn record is added to the Database, a unicorn’s image needs to be uploaded to the Storage first. The upload should allow for it to be easily cancelled if needed. No need unnecessary file taking space in the Storage! Therefore, let’s use a StorageUploadTask variable and put it below the reference properties:

fileprivate var storageUploadTask: StorageUploadTask!

One more property is needed named storageImagePath to keep track of the storage image path within the Storage.

fileprivate var storageImagePath = “”

Aaaand again, one more property is needed to help show and hide the network activity indicator:

fileprivate var showNetworkActivityIndicator = false {
didSet {
UIApplication.shared.isNetworkActivityIndicatorVisible = showNetworkActivityIndicator
}
}

Ok, time to make use of the Storage finally! Go to the imagePickerController(_:didFinishPickingMediaWithInfo:) method, the first method in the UploadUnicornViewController extension, delete it and replace it with this final implementation (comments added for clarification):

Image Picker Controller Final Implementation for imagePickerController(_:didFinishPickingMediaWithInfo:) Method

I think the implementation is quite straightforward — get the image picked, transform it to proper image data that can be handled by the Firebase Storage, give it a unique path to be accessed and proper content type and then upload it. The storageRef from before is used for the upload itself with the unique image path created — uniqueness is needed here again to avoid accidental overriding of images. The putData method works asynchronously to start the upload and actually returns a StorageUploadTask type which is assigned to the storageUploadTask property. The StorageUploadTask type, per documentation, in addition to starting uploads, can be used to pause, resume and cancel uploads. Then, the completion handler of the putData method either returns an error or a StorageMetadata object which can be safely ignored in the case but it does notify success.

Uploading a Unicorn Image to Storage

In the end of it all, call the uploadSuccess(_ storagePath:, _ storageImage:) method which looks like this on the inside:

Method to be Called When Image Uploaded Successfully to the Firebase Storage

First, set the image to the unicornImageView, update the storageImagePath property and enable the submit button (which we disabled in the viewDidLoad method). This completes the Firebase Storage uploading needed for the demo.

Moving on to recording a new unicorn to the Firebase Realtime Database, let’s first hook-up an outlet method for the submit button. I named mine didTapSubmit(_ sender:) and this is the code within it:

Recording a New Unicorn on Submit

Here, the rest of the properties for the unicorn object are taken first. Then, the function that will handle the writing to the database is called and passed the new unicorn instance. This is what the writeUnicornToDatabase(_ unicorn:) method looks like on the inside:

Write-to-Database-with-Unicorn Method

Pretty much the standard way of writing to the Firebase Realtime Database as discussed earlier. And again I used the same strategy for creating a unique reference as before — concatenating a unicorn property to the interval between 00:00:00 UTC on 1 January 2001 and the current date and time as an Integer.

Going back to the didTapSubmit(_ sender: UIButton) method, the last thing to be done there is pop the VC and go back to the initial VC when the new record of a unicorn is handled.

Adding a Unicorn Record to Firebase Realtime Database

The UnicornsTableViewController

The plan here is only to get the recorded unicorns and show them in a table and it all starts with getting a reference property to the Database:

var ref: DatabaseReference!

And then in the viewDidLoad method:

ref = Database.database().reference()

Next thing to do is set the unicorns array variable with the unicorns from the database. That can be done in the viewWillAppear method like so:

Updating Unicorns Array

Per our initial set-up, each table view cell gets its unicorn object updated as well when this piece of code executes. So, yeah, few more things to be handled within the UnicornTableViewCell next.

The UnicornTableViewCell

Here, the addedBy and the seenAt labels are already configured to get updated when the unicorn object gets updated. What is left is to set the unicornImageView image with the correct image from the Firebase Storage.

And again, it all starts with the usual Firebase Storage set-up — a reference and a task (a StorageDownload type this time):

var storageRef: StorageReference!
var storageDownloadTask: StorageDownloadTask!

Next thing, initialise the storageRef in the kind-of-equivalent of a viewDidLoad method but for a UITableViewCell class — the awakeFromNib method, and add it before the prepareForReuse method:

override func awakeFromNib() {
super.awakeFromNib()
storageRef = Storage.storage().reference()
}

Talking about the prepareForReuse method — it is the best place to add this piece of code there to avoid unnecessary downloads from finishing:

storageDownloadTask.cancel()

And yes, I know, no attempts to download anything are executed yet, let alone cancel them. For that, I have prepared a nice method named downloadImage(from storageImagePath:) which obviously takes in one property and that is the path to the image. Put it in place of the last TODO: left from before in the didSet for the the unicorn property:

Unicorn didSet Final Implementation

No worries, I did not forget, this is what the method looks on the inside, with relevant comments for clarification:

downloadImage Method Implementation

And demo project is all set 🎊. Lets run it!

Conclusion

But before that, feel free to check out all that Firebase has put out there for us, developers. Also, explore the completed project on GitHub if you struggle with finishing it up following the article. And thank you! 🙇

--

--