Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4396

Firebase Tutorial for Android: Getting Started

$
0
0

Firebase Tutorial for Android: Getting Started

Nearly all projects we work on every day use a REST service. It’s a concept where you use a certain set of methods (GET, POST, DELETE…) to send data, receive data or both.

And REST is great, but sometimes you just can’t find a (back)end to all the challenges! More often than not, the variable names are in a different style than you prefer or the paths to the requests are confusing…

Then comes Google, introducing Google Firebase and saying: “Why would you work with other people when you can write a backend of your own?” And you start thinking: “IS THAT POSSIBLE??”, suddenly you’re inspired. You cannot keep your mind straight, and all you want to do is create backends all day.

Firebase works similar to hosting, so to speak. You pick what services you like, connect them to your application and bam, you’re ready to take over the world. Firebase’s backend is just another service. It provides a JSON like database in which you store and read data. It doesn’t really follow the REST standard as it has no REST methods (GET, POST…). Here, you’re working directly with the database.

You won’t be using all of Firebase in this tutorial. Instead, we’ll focus on Firebase’s Realtime Database to create a mobile friendly backend. Your goal for this tutorial is to build a joke-sharing social network application, using the Authentication and Realtime Database services. So let’s heat it up. :]

Note: This Firebase tutorial assumes that you’re familiar with the basics of Android development and REST services. If you’re new to Android, please check out the Beginner Android series and other Android tutorials.

Getting started

Before doing anything, download the starter and final projects by clicking on the Download Materials button at the top or the bottom of the tutorial. Once you’ve downloaded the starter project, open it up in Android Studio 3.1.1 or later so we can do an overview of the materials to get you caught up.

Most of the code is already pre-baked for you. It’s better to focus entirely on Firebase in the tutorial, rather than the rest of the application. Don’t worry, you’ll be guided through everything in the starter project.

Tasting the pre-baked goodies

A picture showing the project structure: common, di, firebase, model, presentation and ui packages.

The project setup is simple.

  • The common package contains all the extensions and global functions you’ll use for showing errors, validating data and handling the UI events like clicks.
  • The di package contains the Dependecy Injection setup. I’ve chosen Dagger because it’s the standard and most people will feel at home using it. If you’re looking for a Kotlin alternative to Dagger, check out Koin. It’s simpler than Dagger, but still not at its 1.0 version milestone.

    Note: If you want to learn more about Dagger and how it works, Ray Wenderlich has you covered. Checkout the this tutorial to learn more about Dagger with Kotlin.

  • The ui package contains all the Activities and Fragments you’ll use. And the model package has model classes for the app data.

The presentation and firebase packages are the ones you’ll be working with. The projects uses an MVP pattern for the application. Splitting the project with this package structure allows you to focus on the Firebase implementation in this tutorial instead of getting lost in the pre-baked code.

A hot package

There are two parts of the Firebase implementation you’ll be finishing up, the FirebaseDatabaseManager and the FirebaseAuthenticationManager. Both are in subpackages of the firebase.

The former will take care of all-things-user, like logging a user in or out and registering a new user. The latter will be your man(ager) in the middle, and will read data from the database and store new data when it comes in.

Authentication will be done using an email and a password. Reading the data on the other hand will be done multiple ways. You’ll listen for single data events, for individual updates and for general updates. Don’t worry if that doesn’t make sense, it’ll be explained in further sections. :]

Rev it up!

In order to start working, you need to add Firebase specific dependencies. However, to be able to add them you also need something called a google-services.json file. It’s a JSON file which contains all the configuration data Firebase uses internally. To get it, you need a Firebase project, let’s create one!

Visit the Firebase website. You should see something similar to this:

Firebase landing page, with an overview of the site and services

Login using a Google account if you haven’t already and if you want, take a quick tour around the landing page. When ready, click the Go to Console button in the upper right. You’ll now create a new Firebase project. You can do it by clicking on the Add project card like below:

Add project card

Choose your project name. In this tutorial you’ll use the name “Why so serious”, and choose your current country like so:

Firebase Add project window with the Why so serious project name

Accept any terms if needed and click Create Project. After finishing the first step, you should see a success message:

Firebase project created success message

Click Continue.

You should see the project dashboard now. Next you need to add an Android application to the project, so click on the Add Firebase to your Android app card:

Firebase dashboard with three options, adding an iOS, Android or a Web app

Fill in the package name com.raywenderlich.android.whysoserious from the starter project and click Register app. The SHA key can be empty for now, you need it only when signing an APK.

Now follow the instructions on the Firebase page to add the google-services.json file to the project and click Next.

Add the required library dependencies to your app module build.gradle file:

ext {
  //Add the playServices version
  playServices = "15.0.0"
}
dependencies {
  //Add the following lines
  implementation "com.google.firebase:firebase-core:$playServices"
  implementation "com.google.firebase:firebase-auth:$playServices"
  implementation "com.google.firebase:firebase-database:$playServices"
}
//Add to the bottom of the file
apply plugin: 'com.google.gms.google-services'

And add the following to the project-level build.gradle:

buildscript {
  dependencies {
    //Add this line
    classpath 'com.google.gms:google-services:3.3.1'
  }
}

Hit Sync Now to sync your Gradle files.

Finally you can Build and Run the project! You should see something like this:

A welcome screen with a greeting message, a login and a register button.

Woohoo! Now you can start working on the juicy parts of this app.

Planning our stand-up

By the end of this tutorial you’ll have an app which serves other people the best jokes the internet has ever read! To achieve this, you first need to be able to manage users. The first thing on your plate is to finish up the authentication part of the app.

After the users make their way to the app, you’ll start patching things together with the realtime database. You’ll store the users in the database so that each user can have a list of his/her favorite jokes. Each joke will have a title, description (the joke itself), the author’s name and id.

To model these entities, you’ll use everything from the model package. You only need a few data classes to achieve this.

So the user will be able to sign up for the application, create jokes and like other jokes. It’s not much, but it’s enough for you to learn how to use Firebase as a mini backend.

Hiring comedians

What good is a joke app if there are no comedians – absolutely pun. Good thing you aren’t hiring me! You really need people who actually have a sense of humor. Recruit them and sign them up through Firebase Authentication!

Start by having a look at the RegisterActivity screen and it’s content.

Register Activity

It has a few input fields you need to create a unique user. Since you’re building an authentication service using an email and a password, the user needs to provide them while signing up. Additionally, each user will have a username that you’ll display next to each joke.

In order for your application to use email registration, you need to enable the “email sign-in” method in the Firebase console.

Open up the Firebase Console, click on the Authentication option on the left under Develop and choose the Sign-in Method tab. Now, select the Email/Password provider and enable it.

Yay! You can now proceed to the implementation. :]

Preparing the stage

To connect to Firebase’s authentication service, first, go to the di package and add a class called FirebaseModule.kt to the module package and provide the FirebaseAuth and FirebaseDatabase dependencies in the module:

@Module
@Singleton
class FirebaseModule {

  @Provides
  fun firebaseAuth(): FirebaseAuth = FirebaseAuth.getInstance()

  @Provides
  fun firebaseDatabase(): FirebaseDatabase = FirebaseDatabase.getInstance()
}

Next, add the module to the InteractionModule.kt list of includes, like so:

@Module(includes = [FirebaseModule::class])
@Singleton
abstract class InteractionModule { ...

Now, if you’re using either FirebaseAuth or FirebaseDatabase, you have them provided in the Dagger graph.

Next, open up the FirebaseAuthenticationManager.kt file in the firebase.authentication package and add a FirebaseAuth property to the constructor like this:

class FirebaseAuthenticationManager @Inject constructor(
private val authentication: FirebaseAuth) : FirebaseAuthenticationInterface

You’ve setup everything to use Firebase-related services, way to go! Next you’ll connect the actual authentication process, so keep up the good work. :]

Gathering the crowd

At last, head over to the register method in the FirebaseAuthenticationManager. It should look something like this:

override fun register(email: String, password: String, userName: String, onResult: (Boolean) -> Unit) {
      
}

The registration process will go like this: once the user’s data is valid and they press the Sign up button, you’ll try to create a unique user with the provided email.

If the email is already in use, Firebase will return an error. If it isn’t, an empty user will be created. Since you’re also collecting a username, you’ll need to create a UserProfileChangeRequest which edits a user to include a username.

After you finish all that, you still need to create a user in the database, since the Authentication and Realtime Database are separate services, and don’t share user objects. You’ll do that later on in the tutorial.

Add the following to the body of your register method:

//1
authentication.createUserWithEmailAndPassword(email, password).addOnCompleteListener {
  //2
  if (it.isComplete && it.isSuccessful) {
    authentication.currentUser?.updateProfile(UserProfileChangeRequest
        .Builder()
        .setDisplayName(userName)
        .build())
    //3
    onResult(true)
  } else {
    onResult(false)
  }
}

Taking each commented section in turn:

  1. This method, like most Firebase methods, returns a Task. A Task is something that you can listen to, for the final result.
  2. Inside the lambda block addOnCompleteListener, you can check whether the result of creating a user was successful and/or completed by using the returned object’s properties.
  3. If the task is successful, you will add a username to the new user and call the onResult callback, saying the user has been successfully created. Any other case will just notify that you didn’t manage to create a user.

A session of comedy

Notice how you’re using the authentication.currentUser property. It’s important to know how the FirebaseAuth service works inside an Android application. As soon as you register or log in, the currentUser is available for use. Basically, Firebase caches your login up until the point where you log out or clear the application’s data. This is called a Session. Although not visible through code, it will be there until you close it.

After logging in once, there is no need to worry about the rest. If the currentUser doesn’t return null, then you’re logged in. It also contains information like the unique user id, email and username. Quite handy when you need it.

Finish up FirebaseAuthenticationManager.kt class by filling in the rest of the implementation as follows:

override fun login(email: String, password: String, onResult: (Boolean) -> Unit) {
    authentication.signInWithEmailAndPassword(email, password).addOnCompleteListener {
    onResult(it.isComplete && it.isSuccessful)
  }
}

override fun getUserId(): String = authentication.currentUser?.uid ?: ""
override fun getUserName(): String = authentication.currentUser?.displayName ?: ""

override fun logOut(onResult: () -> Unit) {
  authentication.signOut()

  onResult()
}

The code above is pretty simple. The getUserId and getUserName methods return the data from the current user. Furthermore, the logOut method closes the current Session and the login method tries to log the user in.

After adding this, you’re able to log out of the app in the Profile tab. Moreover you’re able to log back in at the login screen.

Breaking the ice

Build and Run the App. Open the register screen by clicking the Sign up button. Fill in the form and hit Sign up to create a new user. If everything goes well you should see the following screen:

Main Screen

You managed to register! Quickly open up the Firebase console again, navigate your way to the Authentication tab to see your new user at the Users tab. Right now there is only one user in the application and that is you.

Making punnections

Only having an email provider is very limiting for your app. Firebase, however, allows you to easily add a social provider as an authentication solution. It’s about as simple as flipping an extra switch in the console. In the same place where you enabled email sign-in, you can enable various other providers like Github, Google, Facebook, Twitter and more. This allows your users to connect multiple accounts to one FirebaseUser.

When a user connects multiple accounts, and logs in with any of them, they enter your application as a single user in the Authentication service. It’s super convenient for your users when your app support multiple login platforms.

Check out this link to see the official documentation for this process.

That concludes the Authentication section. Once again, good job! You’re ready for the next part – the Realtime Database.

Time to shine

Now that you have a line of comedians all waiting to do their own stand-ups, all you need is their jokes. Head over to the Firebase dashboard again, this time to the Database option under Develop. When you open this option, you should get to choose between the Cloud Firestore and Realtime Database like so:

Cloud Store and Realtime Databases

The difference is in the way each stores data and works with data. On one hand the Firestore is like an actual database, it stores files in documents and allows for efficient queries. On the other hand you have the Realtime Database which stores everything as a big JSON object which you read parts of.

For small applications, the latter is simpler to use. It also allows realtime updates (as the name says) which is handy when you’re building applications with feeds or chat rooms. This is the reason you will choose it for this tutorial. It’s not a complex application and it will benefit a lot from the realtime updates.

Blue pill or red pill?

Blue Pill or Red Pill

As mentioned above, choose the Realtime Database. You should see a popup asking you for the type of security rules:

Security Rules Screen

Choose either one, it doesn’t really matter which, since you’ll change the settings anyway. Head over to the Rules tab and paste this:

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

What these rules mean is that only people who have been authenticated can read or write data, i.e. only your logged in users. Once you’ve copied and pasted the snippet above, click the Publish button to save the rules. Once you switch back to the Data tab your database will be empty and look like this:

Rules Screen

Press any key to continue

Right now there is no data in the database as you’ve only created it. Once you start pushing the jokes it will be much better. However, to save data you need to understand how data is represented.

I’ve already mentioned that our database choice stores everything in a JSON object. This means that each piece of information has it’s own key. Once you request data at a certain key or “directory” (for example key “user”), you’ll receive what is called a DataSnapshot. A snapshot is the current state of that directory which holds everything under the key “user” in this example. You parse that snapshot to get the value, pretty straightforward! You’ll see how to do this in the next section when you start saving data.

The keys can be custom made or generated by Firebase. Usually you’d just let Firebase generate the keys so you don’t have to worry about that. Once Firebase generates a key, and stores the data, the whole directory gets sorted by a timestamp, starting from the newest items first.

Also ordering is always ascending when you query items. Sadly, Realtime Database doesn’t support descending ordering, for example for the number of likes. Firestore was built in response to this, and supports more query options and also much more. However, that might be another tutorial!

Playing the game

You’ve finally reached the part where you’ll work with data. You’ll start off by finishing the user registration process. Remember how I said you’d have to store the user in the database after registration? Well, open up the FirebaseDatabaseManager under the di/firebase/database folder.

Add a private value to the constructor named database and of the type FirebaseDatabase. Your class definition should look like this:

class FirebaseDatabaseManager @Inject constructor(private val database: FirebaseDatabase) : FirebaseDatabaseInterface

And the following KEYS at the top above the class:

private const val KEY_USER = "user"
private const val KEY_JOKE = "joke"
private const val KEY_FAVORITE = "favorite"

You can now connect to the database and store or read data.

Update the createUser() to be the following to finish up the user creation:

override fun createUser(id: String, name: String, email: String) {
  val user = User(id, name, email)

  database
    .reference        // 1
    .child(KEY_USER)  // 2
    .child(id)        // 3
    .setValue(user)   // 4  
}

You’ll call this method right after signing up a user. Here’s what each line means:

  1. Get a reference to the database, which effectively points to the root of the tree/directory.
  2. From the root directory, open up the “user” directory
  3. Inside the “users” directory, open up the directory which matches the “id” of this particular user
  4. Store a new user in that directory by calling the setValue(user) method

And that’s how you store data! If you want to delete a value somewhere, just call the setValue(null).

So the database is like a big family. There are the parents, and each parent can have a bunch of children.

Build and run the app, logout from the Profile tab if you need to, and try to register a new user. You should see a new entry in the database with the new user’s data, right-away and even without refreshing the Firebase page! Now that’s Realtime! :]

I’ve created a couple of users myself and here is the result:

User Sample

Have a look at the structure of the database. The “user” key has two entries, each of which is a unique user. Each user on the other hand has multiple fields, representing its data.

Loading Firebase…

Now that you have a user in the database, why not read it back from the database and show it in their profile? To read data, and receive snapshots, you need to use Firebase’s Value Listeners. By adding a listener to a reference you get the values from the database. You can listen to data in three ways.

First, by calling addListenerForSingleValueEvent, you read the data only once. After you receive it, the listener is removed. This is great when you need to read something once to use it in your app.

Second, using the addValueEventListener method, you listen to a certain directory and all its changes. Even if the smallest thing changes (like a user’s name) you get the entire snapshot again. This is great for showing data that isn’t large but tends to change and can benefit from a realtime update, like a user’s profile.

Lastly, with the addChildEventListener method you subscribe to changes for each child in a directory. If you change any of them, remove them or move them, you get an event for each of the mentioned cases. More importantly, it will emit each of the children one by one, the first time you attach the listener. It’s great for things like chats and feeds where new items are added often.

You’ll use the addChildEventListener for all jokes, addValueEventListener for favorite jokes and the user profile and the addListenerForSingleValueEvent for changing the joke’s liked status.

The peak of the show

There are five methods to finish up in the FirebaseDatabaseManager before your social network is ready!

Add the following code to read the jokes and the profile.

//1
override fun listenToJokes(onJokeAdded: (Joke) -> Unit) {
  database.reference.child(KEY_JOKE)
      .orderByKey()
      .addChildEventListener(object : ChildEventListener {
        override fun onCancelled(p0: DatabaseError?) = Unit
        override fun onChildMoved(p0: DataSnapshot?, p1: String?) = Unit
        override fun onChildChanged(p0: DataSnapshot?, p1: String?) = Unit
        override fun onChildRemoved(p0: DataSnapshot?) = Unit

        override fun onChildAdded(snapshot: DataSnapshot?, p1: String?) {
          snapshot?.getValue(JokeResponse::class.java)?.run {
            if (isValid()) {
              onJokeAdded(mapToJoke())
            }
          }
        }
      })
}
//2
override fun getFavoriteJokes(userId: String, onResult: (List<Joke>) -> Unit) {
  database.reference
      .child(KEY_USER)
      .child(userId)
      .child(KEY_FAVORITE)
      .addValueEventListener(object : ValueEventListener {
        override fun onCancelled(error: DatabaseError?) = onResult(listOf())

        override fun onDataChange(snapshot: DataSnapshot?) {
          snapshot?.run {
            val jokes = children.mapNotNull { it.getValue(JokeResponse::class.java) }

            onResult(jokes.map(JokeResponse::mapToJoke))
          }
        }
      })
}
//3
override fun getProfile(id: String, onResult: (User) -> Unit) {
  database.reference
      .child(KEY_USER)
      .child(id)
      .addValueEventListener(object : ValueEventListener {
        override fun onCancelled(error: DatabaseError?) = Unit

        override fun onDataChange(snapshot: DataSnapshot?) {
          val user = snapshot?.getValue(UserResponse::class.java)
          val favoriteJokes = snapshot?.child(KEY_FAVORITE)?.children
              ?.map { it?.getValue(JokeResponse::class.java) }
              ?.mapNotNull { it?.mapToJoke() }
              ?: listOf()


          user?.run { onResult(User(id, username, email, favoriteJokes)) }
        }
      })
  }
}

Let’s go through the logic behind the implementation:

  1. When listening to jokes, add a child listener to the “joke” directory. On each child, we parse the joke and add it to the list. Notice how you parse the data, by calling getValue(class) the snapshot is parsed to whatever data model you want.
  2. Favorite jokes will be stored on each user’s profile. Since the queries in the database are limited, you cannot request all jokes by ids. This is why jokes are stored on the user. You’ll read favorite jokes from each of the users’ profile directory. Every time that directory changes, you get an event, since you need to know which jokes are liked in order to show the appropriate icon in the list. The same goes for the profile, as you’re showing the number of favorite jokes there.
  3. To look up a profile, you have to call the child(KEY_USER) to enter the “users” directory and then child(id) to find a specific user. However there is more to a profile than just the user part. Since lists are actually HashMaps in Firebase, you have to manually parse each item. For that reason there is the somewhat ugly block of code for mapping all the children.

Closing up

In order to add a new joke, create a child inside the “jokes” directory, and set the value to the joke object:

override fun addNewJoke(joke: Joke, onResult: (Boolean) -> Unit) {
  val newJokeReference = database.reference.child(KEY_JOKE).push()
  val newJoke = joke.copy(id = newJokeReference.key)

  newJokeReference.setValue(newJoke).addOnCompleteListener { onResult(it.isSuccessful && it.isComplete) }
}

By calling push, you generate a new key like mentioned before. Set the value to the new joke and add a listener to be notified about completion.

Changing whether the joke is in your favorites or not is a bit trickier. You first have to know if it’s in your favorites, and then remove it if it already is, or add if it isn’t:

override fun changeJokeFavoriteStatus(joke: Joke, userId: String) {
  val reference = database.reference
      .child(KEY_USER)
      .child(userId)
      .child(KEY_FAVORITE)
      .child(joke.id)

  reference.addListenerForSingleValueEvent(object : ValueEventListener {
    override fun onCancelled(error: DatabaseError?) {}

    override fun onDataChange(snapshot: DataSnapshot?) {
      val oldJoke = snapshot?.getValue(JokeResponse::class.java)

      if (oldJoke!=null) {
        reference.setValue(null)
      } else {
        reference.setValue(joke)
      }
    }
  })
}

By listening for the value of a child, you can tell that the child doesn’t exist if the value is null – or in your case, that a joke isn’t a favorite. Same goes the other way around, if it’s not null, you know the child exists.

Build and Run the app. Go and add a new joke. You should see it in the all jokes list:

Finished App

It is not yet added to your favorite jokes, click on the heart icon to add it to favorites. Go back to the Firebase console and the Database/Data tab, and you’ll see the favorite joke in the data. If you click on the heart icon again and switch back to the console, it should be gone. The power of realtime updates! :]

Take a look at the Firebase dashboard, your database should have some data in it now.

Where to go from here

Congratulations on creating your joke-sharing social app! Wasn’t it just puntastic! You can download the final project by using the link at the top or bottom of this tutorial.

There is much more to Firebase than the Auth/Database services, try exploring every aspect of it by following the documentation.

Also check out the Firestore, the upgraded version of the database we used.

You can also read the Joe Birch article series about each of the services.

And most importantly, feel free to leave comments and suggestions in the section below! Your feedback means a lot! :]

The post Firebase Tutorial for Android: Getting Started appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4396

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>