Syncing data in an Ionic app using Firebase

I released my most popular mobile app, Hours Worked Time Tracker, over five years ago. Over those five years I have received a lot of feature requests. By far the most requested feature is the ability to backup and sync data. The app has always saved data on each device using a local database. Earlier this week I released version 3.4 which includes the ability to sync data using an account for Hours Worked Pro subscribers. I relied on Firebase to add this feature. This guide will cover the steps necessary to add a similar feature to your Ionic mobile application.

Before we dive in, here is a quick demo of the feature in Hours Worked. In this demo I am logged into the same Hours Worked account on two separate devices. Observe how quickly data syncs between the two devices. I was really excited to get this feature working!

Get everything set up on Firebase

The first step is to create a new Firebase project from the Firebase console. After setting up your new project you should be taken to the Firebase console. Eventually we will utilize the Firebase Authentication and Realtime Database. While in the console we should enable the Email/Password provider inside the Sing-in method options for Firebase Authentication as seen in the image below.

Get everything set up in Ionic

Inside of our Ionic project we need to download the AngularFire library. As shown in the AngularFire Documentation there are two different ways to download the library: manually or via Bower. Then we need to initialize the Firebase SDK as seen in the docs. Below is a snapshot of what we have added to our index.html file so far. The specific keys shown in your Firebase project settings should be used here.

Set up authentication

To simplify authentication methods in our code we can first create a factory to generate the $firebaseAuth instance. I added the following factory to my apps.js file.

.factory("Auth", ["$firebaseAuth",
  function($firebaseAuth) {
    return $firebaseAuth();
  }
])

Next we need to create a view that allows users to create an account. I opted to use a simple modal view. Here is the template file for this modal.

<ion-modal-view>
    <ion-header-bar class="bar-positive">
        <h1 class="title">Create Account</h1>
        <div class="buttons">
            <a class="button button-clear" ng-click="closeCreateModal()">Done</a>
        </div>
    </ion-header-bar>
    <ion-content>
        <ion-list type="card item-text-wrap">
            <label class="item item-input">
                <input type="email" placeholder="Your Email Address" ng-model="hoursWorkedAccount.email">
            </label>
            <label class="item item-input">
                <input type="password" placeholder="password" ng-model="hoursWorkedAccount.password">
            </label>
        </ion-list>
        <button class="button button-full button-positive" ng-click="createAccount()">
            Create Account
        </button>
    </ion-content>

</ion-modal-view>

Now we need to make the createAccount() method in our controller. After injecting the Auth dependency into our controller we can write the method. Here is my createAccount() method for Hours Worked.

$scope.createAccount = function() {
  var details = {
    'email': $scope.hoursWorkedAccount.email,
    'password': $scope.hoursWorkedAccount.password
  };

  Auth.$createUserWithEmailAndPassword(details.email, details.password)
    .then(function(firebaseUser) {
      console.log("User " + firebaseUser.uid + " created successfully!");
      $rootScope.firebaseAccount.loggedIn = true;
      $scope.closeCreateModal();
    }).catch(function(error) {
      var alertPopup = $ionicPopup.alert({
        title: "Error creating account",
        template: error
      });
    });
}

As shown, Firebase returns a promise from $createUserWithEmailAndPassword that is either resolved or rejected. Reference their documentation for more information.

Create methods to save data

So now that users are able to create an account we need to provide a way to save the data to their account. The following example, as well as the rest of the tutorial, will reference the $firebaseObject service. Both the Auth factory and $firebaseObject will need injected as a dependency to your controller. The following snippet performs these tasks:

  • Uses the Auth factory to determine which user is signed in.
  • Creates a reference to the specific users data in the Firebase realtime database.
  • Loads the hours firebase object within the users data.
  • Updates this object with the hours that were saved in our app as $rootScope.hours.
  • Saves the data back to Firebase.
var firebaseUser = Auth.$getAuth();

if (firebaseUser) {
  var userid = firebaseUser.uid;
  var refUser = firebase.database().ref(userid);
  var refUserHours = refUser.child('hours');
  var objUserHours = $firebaseObject(refUserHours);

  objUserHours.$loaded().then(function() {
      
    angular.forEach($rootScope.hours, function(value, key) {
      objUserHours[key] = value;
    });

    objUserHours.$save().then(function(ref) { 
      console.log("hours saved to Firebase");
    });
  }
}

Now you should be able to see your data inside of Firebase for the first time as shown in this screenshot.

Sync data to Firebase

Manually saving your data to Firebase by overriding the existing object is a good way to backup data but not ideal for keeping data in sync. Thankfully Firebase offers a handy method called $bindTo to sync data between the realtime database and the Ionic app using three way data binding. The sample method shown below syncs the same objUserHours object with $rootScope.hours. It also registers an event listener to catch any changes to the data.

objUserHours.$bindTo($rootScope, "hours").then(function() {
  console.log("now any changes to $rootScope.hours will immediately update Firebase!");

  objUserHours.$watch(function() {
    console.log("change detected...");
  });
}

After binding the Firebase object to your application scope, every change made locally will instantly sync to Firebase. Very handy!

This introduction should get you well on your way to syncing data from an Ionic app to Firebase. When developing the feature for Hours Worked I also had to consider other factors:

  • How to resolve differences in local data and “account” data on initial load.
  • Update the local database when changes are detected in the data.
  • Give users the ability to sign in and out of their account, as well as a “forgot password” feature.

If you would like to see how I handled these features, or have any questions at all, please email john at appsbyjohn.com. If you would like to see syncing in action, sign into the same Hours Worked account on multiple devices. The app is available for iOS and Android.