Introduction to Firebase: Building a Simple Social App in Swift

In this day and age, it is practically impossible to build a fully self-contained app that users will really like. As such, iOS developers are reaching out beyond the tools and resources provided by Apple for access to data and storage. A fantastic option for developers is the backend-as-a-service, or BaaS.

Among the best and most popular BaaSs is Google’s Firebase. Actually, Firebase is positively fantastic in performance, implementation, and upkeep. The key to Firebase’s immediate value is its realtime database that stores data in JSON. Any change in the database is immediately synced across all clients, or devices, that use the same database. In other words, Firebase is ridiculously fast; everything is updated instantly.

Firebase also offers user authentication along with storage, and all data is transferred via a secure SSL connection. For authentication, we can choose from any combination of email and password, Facebook, Twitter, GitHub, Google, or a custom solution.

In addition to iOS, Firebase has SDKs for Android and JavaScript. All platforms can share the same database, and Firebase scales apps automatically.

It’s tough to imagine Firebase being an affordable option with all of these features. So here’s the catch…

Nope, there is no catch. At the time of writing this tutorial, Firebase is free for up to 100 simultaneous connections. That’s a ton. 100 open network connections can support a pretty popular app. For $49 per month, the cap disappears.

About FirebaseJokes

Today, we’ll be building a Joke posting app using Firebase. In the app, we will let users create an account and login using email and password. Users will be able to post a joke, and the table view will instantly update. The update will also immediately occur if another user posts a new joke. For fun, we’ll also add a voting function to the app, so the funniest jokes can receive the credit that they deserve.

Here’s a list of the features that we will implement into FirebaseJokes:

As we progress through the app, we will travel, logically, from point to point to ensure that the implementation of Firebase makes sense. This is a great product, and it’s easy and fun to use. Building FirebaseJokes will be time well spent.

Time to Checkout Firebase

Let’s head over to Firebase and signup for a Firebase account, or login if you already have an account. The SIGN UP WITH GOOGLE option is a breeze. After signing up, we’re going to skip the 5 Minute Tutorial, as it is intended for JavaScript applications. We will, however, checkout an iOS version in a bit.

To get a look at what Firebase is all about, click Manage App in My First App. This new area is known as Firebase Forge. It is a very cool graphical debugger, and it’s worth taking the suggested tutorial. The Forge tutorial will guide you through creating keys, values, and even children using the plus symbol. Sort of has a JSON look to it, doesn’t it? To exit the brief Forge tutorial, click Dashboard in the upper-left corner of the screen.

Create a New App

It’s time to create FirebaseJokes. To the left of My First App, click the faded box to create a new app. For the APP NAME, enter “Jokes”. In the APP URL field, enter “jokes-your-name”, with “your-name” being your personal name. This field must be unique, as it is the actual url for your app. Finally, tap CREATE NEW APP and then the Manage App button.

Here we are – our very own Forge screen. Here, we’ll actually be able to see the data instantly update as is does in our app. We can also enter app data directly into Forge. To get an idea of how our app will work, from a data perspective, we’ll enter some data manually.

Click the Plus symbol in the jokes-your-name row.

Enter “jokes” into the name field.

Click the Plus symbol in the new jokes row.

Enter a random number into the name field.

Click the Plus symbol in the new random number row.

Enter “jokeText” into the name field.

Enter “What did one computer say to the other? 11001001010101” into the value field.

This is an example of how a joke will look. We’ll be adding a bit more to “jokes”, and we also need “users”, but it will look a lot like this. It’s fun and a good practice to peek back at Forge from time to time to look at the data created by an app.

One point I would like to point out is that all Firebase database data is stored as JSON objects. Unlike Parse, there are no tables or records. When we add data to Firebase’s database, it actually becomes a key in the existing JSON structure. For example, the data you just created looks as follows:

1

2

3

4

5

6

7

{

"jokes":{

"e32e223r44":{

"jokeText":"What did one computer say to the other? 11001001010101"

}

}

}

Now that you have some basic understanding of Firebase database data, let’s move on.

But before we move on to user authentication, let’s delete the data that we created, as we’ll be doing it all programmatically from within the app.

For FirebaseJokes, we will be using Email and Password authentication. To enable this feature, click Login & Auth in the left-side panel in Forge. In Email & Password, click the box to Enable Email & Password Authentication. This is also a good opportunity to browse. Just below the box that we clicked is information on password recovery. Also, it is worth looking through the other authentication options.

Installing Firebase SDK and Configuring the Base URL

To get the base url for our Firebase app, navigate back to Forge. The url for our Forge screen is the url for our app, so let’s copy it and paste is into BASE_URL in Constants.swift in Xcode.

1

2

3

importFoundation

letBASE_URL="https://jokes-matt-maher.firebaseio.com"

Now, it’s finally time to install Firebase SDK into our app. Before we do that, however, we need CocoaPods. If you haven’t installed CocoaPods, excellent instructions for installing CocoaPods can be found directly from CocoaPods.

Once CocoaPods is up and running, open Terminal. Run the following commands to initialize Cocoapods in your Xcode project:

1

2

cd<your-xcode-project-directory>

pod init

And then type the following command to open the Podfile in Xcode:

1

open-aXcode Podfile

Next, edit the file like this:

1

2

3

4

platform:ios,'8.0'

use_frameworks!

pod'Firebase','>= 2.5.0'

Then, run the following command to download the Firebase SDK:

1

pod install

Now it’s time for Xcode. Make sure you open the FirebaseJokes.xcworkspace file to start coding.

Using Firebase SDK

First, we’ll be doing a little bit of setup in our DataService.swift file to make things a little easier. Primarily, we need some good references!

To use Firebase, all you need to do is import the Firebase framework. This DataService class is a service class that interacts with Firebase. To read and write data, we need to create a Firebase database reference with the Firebase URL. The base URL is the URL of our Joke database. Later we’ll save our users and jokes as child nodes. To access the child nodes, you can just append the node name (e.g. users) to the base URL. For easy access, we also create references to the child nodes.

Note: Initializing a database reference doesn’t mean that you create an actual database connection to the Firebase server. Data is not fetched until a read or write operation is invoked.

Create a New User Account

CreateAccountViewController.swift is where we will start. As with all files that use Firebase, we’ll need to import it at the top.

1

2

3

4

importUIKit

importFirebase

classCreateAccountViewController: UIViewController{

In createAccount(), we will collect the text from the fields and attempt to use them to create a new user. This occurs with Firebase’s createUser() method. Update the method like this:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

@IBAction funccreateAccount(sender:AnyObject){

letusername=usernameField.text

letemail=emailField.text

letpassword=passwordField.text

ifusername!=""&&email!=""&&password!=""{

// Set Email and Password for the New User.

DataService.dataService.BASE_REF.createUser(email,password:password,withValueCompletionBlock:{error,result in

signupErrorAlert("Oops!",message:"Don't forget to enter your email, password, and a username.")

}

}

Assuming that there are no issues with the information entered by the prospective new user, the user will be created and logged into the app. For any missing information, we’ll display an alert. Therefore, insert the following method in the class:

To save data to the Firebase database, you can just call the setValue method. In the above code, it’ll save the user object to the users database reference under the given uid child node (e.g. /users/1283834/).

Other than saving the user to the Firebase database, we will be storing the uid for the user in NSUserDefaults. This will allow us to keep track of the current user.

Login a User

Before we dig too deep, let’s import Firebase into LoginViewController.swift. Here, we’ll check to see if someone is logged in already and attempt to log the user in if they are not.

In viewDidAppear(), we check if our stored “uid” is nil and if the user has an account. If the user passes, he or she can skip login. Otherwise, it’s time to punch in some credentials.

1

2

3

4

5

6

7

8

9

overridefuncviewDidAppear(animated:Bool){

super.viewDidAppear(animated)

// If we have the uid stored, the user is already logger in - no need to sign in again!

Firebase has built-in support for user authentication with email address and password. The tryLogin() method uses Firebase’s authUser() method to see if the email and password match a user account. If so, we save the “uid” and navigate into the app. If not, we alert them to try something else.

Now that the app is ready for user registration and sign on, it’s time to dive into the jokes.

The Joke Model

What is a joke? This philosophical question might be best answered later, or turned into a joke on FirebaseJokes. For us, we’ll be mapping out our Joke model.

Let’s navigate to Joke.swift and import Firebase. In our database, a joke will be represented by an odd-looking ID number. This auto-generated number will contain all of the joke’s properties, which are as follows.

jokeText

jokeVotes

username (the joke’s author)

We initialize a new joke in init(), where the joke’s id, or key, and the jokes data, in dictionary format, will be passed.

Adding New Jokes

In AddJokeViewController.swift, now would be a good time to import Firebase. Here, the user will enter a joke, and we will send it to where it needs to go to display, instantly, on all devices.

Starting in viewDidLoad(), we will get the current user’s username, so we can credit her or him as the author of the well-crafted new joke.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

overridefuncviewDidLoad(){

super.viewDidLoad()

// Get username of the current user, and set it to currentUsername, so we can add it to the Joke.

DataService.dataService.CURRENT_USER_REF.observeEventType(FEventType.Value,withBlock:{snapshot in

letcurrentUser=snapshot.value.objectForKey("username")as!String

print("Username: \(currentUser)")

self.currentUsername=currentUser

},withCancelBlock:{error in

print(error.description)

})

}

When saveJoke() is called, the newJoke dictionary is created using the text from the jokeField, 0 for the current number of votes, and the current user’s username as the author. These values are assigned to their respective ids and sent over to createNewJoke() in DataService for saving.

Declare the following variable in the AddJokeViewController class:

1

varcurrentUsername=""

And update the saveJoke method like this:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

@IBAction funcsaveJoke(sender:AnyObject){

letjokeText=jokeField.text

ifjokeText!=""{

// Build the new Joke.

// AnyObject is needed because of the votes of type Int.

letnewJoke:Dictionary<String,AnyObject>=[

"jokeText":jokeText!,

"votes":0,

"author":currentUsername

]

// Send it over to DataService to seal the deal.

DataService.dataService.createNewJoke(newJoke)

ifletnavController=self.navigationController{

navController.popViewControllerAnimated(true)

}

}

}

We use a dictionary object to temporarily hold the joke data. The actual saving of the joke is passed to the createNewJoke method of DataService. In DataService.swift, add the createNewJoke method:

1

2

3

4

5

6

7

8

9

10

11

12

funccreateNewJoke(joke:Dictionary<String,AnyObject>){

// Save the Joke

// JOKE_REF is the parent of the new Joke: "jokes".

// childByAutoId() saves the joke and gives it its own ID.

letfirebaseNewJoke=JOKE_REF.childByAutoId()

// setValue() saves to Firebase.

firebaseNewJoke.setValue(joke)

}

Again, you can save the joke with the Firebase method, setValue(). But note that we use the Joke database reference and call the childByAutoId method. When childByAutoId is invoked, Firebase generates a timestamp-based, unique ID for each joke. This ensures that each joke is associated with an unique ID.

Logout the Current User

Normally, this would be placed in Settings or a Profile area, but we’ll go ahead and enable the user to logout in AddJokeViewController.swift. Actually, maybe this ought to be a joke.

logout() uses Firebase’s unauth() method to logout a user. In addition to logging out, though, we need to remove the user’s “uid” from our storage and send them back to LoginViewController.

Forgetting to remove the user’s “uid” will cause issues when a new user tried to log into the app, and that is a good thing to avoid.

Displaying All of the Jokes

Finally it comes to the retrieval of data from Firebase. We’ll be listing all of the jokes in a UITableView located in JokesFeedTableViewController.swift. Here, we will import Firebase, to no surprise.

Beginning is viewDidLoad(), we set our observeEventType() method. Firebase data is retrieved by attaching an asynchronous listener to a database reference. This awesome method is not only called in viewDidLoad() upon navigation to JokesFeedTableViewController.swift, it is called whenever there is a change in the jokes side of our database.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

varjokes=[Joke]()

overridefuncviewDidLoad(){

super.viewDidLoad()

// observeEventType is called whenever anything changes in the Firebase - new Jokes or Votes.

// It's also called here in viewDidLoad().

// It's always listening.

DataService.dataService.JOKE_REF.observeEventType(.Value,withBlock:{snapshot in

// The snapshot is a current look at our jokes data.

print(snapshot.value)

self.jokes=[]

ifletsnapshots=snapshot.children.allObjectsas?[FDataSnapshot]{

forsnapinsnapshots{

// Make our jokes array for the tableView.

ifletpostDictionary=snap.valueas?Dictionary<String,AnyObject>{

letkey=snap.key

letjoke=Joke(key:key,dictionary:postDictionary)

// Items are returned chronologically, but it's more fun with the newest jokes first.

self.jokes.insert(joke,atIndex:0)

}

}

}

// Be sure that the tableView updates when there is new data.

self.tableView.reloadData()

})

}

A snapshot is provided by the method. Using this snapshot, we can build the array of jokes that we’ll be using to populate our tableView. For Firebase Jokes, we will be listing the newest jokes at the top. Since Firebase will return the jokes chronologically, by their creation time, we can just build the array backward.

As nice as it is to have a constantly up-to-date array of jokes, we should remember to reload the data in the tableView so everyone can see it.

The rest of our work is done between tableView:cellForRowAtIndexPath: and our custom cell, JokeCellTableViewCell.swift. In tableView:cellForRowAtIndexPath:, we send an individual joke to configureCell() in JokeCellTableViewCell.swift.

// observeSingleEventOfType() listens for the thumb to be tapped, by any user, on any device.

voteRef.observeSingleEventOfType(.Value,withBlock:{snapshot in

// Set the thumb image.

ifletthumbsUpDown=snapshot.valueas?NSNull{

// Current user hasn't voted for the joke... yet.

print(thumbsUpDown)

self.thumbVoteImage.image=UIImage(named:"thumb-down")

}else{

// Current user voted for the joke!

self.thumbVoteImage.image=UIImage(named:"thumb-up")

}

})

}

The UITapGestureRecognizer is set, programmatically, in awakeFromNib(). Also, we’ll be needing a Joke and Firebase reference.

1

2

3

4

5

6

7

8

9

10

11

12

13

varjoke:Joke!

varvoteRef:Firebase!

overridefuncawakeFromNib(){

super.awakeFromNib()

// UITapGestureRecognizer is set programatically.

lettap=UITapGestureRecognizer(target:self,action:"voteTapped:")

tap.numberOfTapsRequired=1

thumbVoteImage.addGestureRecognizer(tap)

thumbVoteImage.userInteractionEnabled=true

}

In voteTapped(), another listener waits for the tap. In this method, the vote is saved to the current user’s “votes” with a key matching the key id of the joke, and a value of true. This is sent via the voteRef path created in configureCell().

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

funcvoteTapped(sender:UITapGestureRecognizer){

// observeSingleEventOfType listens for a tap by the current user.

voteRef.observeSingleEventOfType(.Value,withBlock:{snapshot in

ifletthumbsUpDown=snapshot.valueas?NSNull{

print(thumbsUpDown)

self.thumbVoteImage.image=UIImage(named:"thumb-down")

// addSubtractVote(), in Joke.swift, handles the vote.

self.joke.addSubtractVote(true)

// setValue saves the vote as true for the current user.

// voteRef is a reference to the user's "votes" path.

self.voteRef.setValue(true)

}else{

self.thumbVoteImage.image=UIImage(named:"thumb-up")

self.joke.addSubtractVote(false)

self.voteRef.removeValue()

}

})

}

voteTapped() also relays the tap as a boolean value to addSubtractVote() in Joke.swift. A true value means that the user has voted for the joke; whereas, false means that the user has not yet voted for it.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// Add or Subtract a Vote from the Joke.

funcaddSubtractVote(addVote:Bool){

ifaddVote{

_jokeVotes=_jokeVotes+1

}else{

_jokeVotes=_jokeVotes-1

}

// Save the new vote total.

_jokeRef.childByAppendingPath("votes").setValue(_jokeVotes)

}

addSubtractVote(), in Joke.swift, uses the boolean value to add or subtract the vote from the joke. Then, Firebase’s setValue() method is used to update the vote in the Joke side of the database.

Test the App

Now you’re ready to test the app. Create a new user, and add a few jokes. You should be able to vote the jokes. And if you go up to the Firebase dashboard, you should see the users and jokes that have been created.

In Conclusion

We did it! This is a rather entertaining little app that users will love for its responsiveness. We’ve also gained valuable experience with Firebase.

There are a world of possibilities with Firebase as an iOS developer. Working through FirebaseJokes will serve as a good start, but it is just the beginning.

Checkout some other user authentication options, add features to FirebaseJokes, explore some chat functionality; literally, the possibilities are endless.

A bit of advice regarding images: Firebase does have relatively modest storage allowances, so images should be stored elsewhere. Storage won’t be an issue with text-based apps like ours, but it would be a good idea to use another service for larger files.

Here’s to having a great time incorporating Firebase into your future projects!

A lifelong hobbyist, Matt went pro as a mobile developer in the early 2010s and has developed primarily iOS applications since; many of which are published on the App Store. In addition to his own projects, Matt has assisted in the build and upkeep of half of a decade’s worth of applications for clients in the US and around the world. In addition to his development work, Matt has nearly 15 years of teaching experience and is a Berklee educated jazz musician.

Connect with Us

More Tutorials

Our Books

AppCoda is one of the leading iOS programming communities. Our aim is to teach everyone how to build apps with high quality and easy-to-read tutorials. Learn by doing is the heart of our learning materials.