{"id":298,"date":"2017-07-16T18:17:55","date_gmt":"2017-07-16T18:17:55","guid":{"rendered":"http:\/\/appsbyjohn.com\/learn\/?p=298"},"modified":"2017-07-16T18:19:30","modified_gmt":"2017-07-16T18:19:30","slug":"syncing-data-in-an-ionic-app-using-firebase","status":"publish","type":"post","link":"https:\/\/appsbyjohn.com\/learn\/syncing-data-in-an-ionic-app-using-firebase\/","title":{"rendered":"Syncing data in an Ionic app using Firebase"},"content":{"rendered":"<p>I released my most popular mobile app, <a href=\"https:\/\/hoursworkedapp.com\/\" title=\"Hours Worked Time Tracker mobile app\">Hours Worked Time Tracker<\/a>, 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.<\/p>\n<p><!--more--><\/p>\n<p>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!<\/p>\n<p><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/9XIRmVQShJs\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<h2>Get everything set up on Firebase<\/h2>\n<p>The first step is to create a new Firebase project from the <a href=\"https:\/\/console.firebase.google.com\/\" title=\"Firebase Console\">Firebase console<\/a>. 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.<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/signin_methods.png\" alt=\"\" width=\"836\" class=\"left_align size-full wp-image-314\" srcset=\"https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/signin_methods.png 1672w, https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/signin_methods-300x176.png 300w, https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/signin_methods-768x450.png 768w, https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/signin_methods-1024x600.png 1024w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<h2>Get everything set up in Ionic<\/h2>\n<p>Inside of our Ionic project we need to download the AngularFire library. As shown in the <a href=\"https:\/\/github.com\/firebase\/angularfire#downloading-angularfire\" title=\"Download instructions for angularfire\">AngularFire Documentation<\/a> there are two different ways to download the library: manually or via Bower. Then we need to initialize the Firebase SDK as seen in <a href=\"https:\/\/github.com\/firebase\/angularfire#downloading-angularfire\" title=\"Initialize angularfire sdk\">the docs<\/a>. 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.<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/Screen-Shot-2017-07-15-at-4.49.25-PM.png\" alt=\"\" width=\"499\" class=\"left_align size-full wp-image-301\" srcset=\"https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/Screen-Shot-2017-07-15-at-4.49.25-PM.png 998w, https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/Screen-Shot-2017-07-15-at-4.49.25-PM-300x173.png 300w, https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/Screen-Shot-2017-07-15-at-4.49.25-PM-768x442.png 768w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<h2>Set up authentication<\/h2>\n<p>To simplify authentication methods in our code we can first create a factory to generate the <code>$firebaseAuth<\/code> instance. I added the following factory to my apps.js file.<\/p>\n<pre>\r\n<code>.factory(\"Auth\", [\"$firebaseAuth\",\r\n  function($firebaseAuth) {\r\n    return $firebaseAuth();\r\n  }\r\n])<\/code>\r\n<\/pre>\n<p>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.<\/p>\n<pre>\r\n<code>&lt;ion-modal-view&gt;\r\n    &lt;ion-header-bar class=\"bar-positive\"&gt;\r\n        &lt;h1 class=\"title\"&gt;Create Account&lt;\/h1&gt;\r\n        &lt;div class=\"buttons\"&gt;\r\n            &lt;a class=\"button button-clear\" ng-click=\"closeCreateModal()\"&gt;Done&lt;\/a&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/ion-header-bar&gt;\r\n    &lt;ion-content&gt;\r\n        &lt;ion-list type=\"card item-text-wrap\"&gt;\r\n            &lt;label class=\"item item-input\"&gt;\r\n                &lt;input type=\"email\" placeholder=\"Your Email Address\" ng-model=\"hoursWorkedAccount.email\"&gt;\r\n            &lt;\/label&gt;\r\n            &lt;label class=\"item item-input\"&gt;\r\n                &lt;input type=\"password\" placeholder=\"password\" ng-model=\"hoursWorkedAccount.password\"&gt;\r\n            &lt;\/label&gt;\r\n        &lt;\/ion-list&gt;\r\n        &lt;button class=\"button button-full button-positive\" ng-click=\"createAccount()\"&gt;\r\n            Create Account\r\n        &lt;\/button&gt;\r\n    &lt;\/ion-content&gt;\r\n\r\n&lt;\/ion-modal-view&gt;<\/code>\r\n<\/pre>\n<p>Now we need to make the <code>createAccount()<\/code> method in our controller. After injecting the <code>Auth<\/code> dependency into our controller we can write the method. Here is my <code>createAccount()<\/code> method for Hours Worked.<\/p>\n<pre>\r\n<code>$scope.createAccount = function() {\r\n  var details = {\r\n    'email': $scope.hoursWorkedAccount.email,\r\n    'password': $scope.hoursWorkedAccount.password\r\n  };\r\n\r\n  Auth.$createUserWithEmailAndPassword(details.email, details.password)\r\n    .then(function(firebaseUser) {\r\n      console.log(\"User \" + firebaseUser.uid + \" created successfully!\");\r\n      $rootScope.firebaseAccount.loggedIn = true;\r\n      $scope.closeCreateModal();\r\n    }).catch(function(error) {\r\n      var alertPopup = $ionicPopup.alert({\r\n        title: \"Error creating account\",\r\n        template: error\r\n      });\r\n    });\r\n}<\/code>\r\n<\/pre>\n<p>As shown, Firebase returns a promise from <code>$createUserWithEmailAndPassword<\/code> that is either resolved or rejected. Reference their <a href=\"https:\/\/github.com\/firebase\/angularfire\/blob\/master\/docs\/reference.md#firebaseauth\" title=\"angularfire auth documentation\">documentation<\/a> for more information.<\/p>\n<h2>Create methods to save data<\/h2>\n<p>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 <a href=\"https:\/\/github.com\/firebase\/angularfire\/blob\/master\/docs\/reference.md#firebaseobject\" title=\"firebaseobject angularfire service\">$firebaseObject<\/a> service. Both the <code>Auth<\/code> factory and <code>$firebaseObject<\/code> will need injected as a dependency to your controller. The following snippet performs these tasks:<\/p>\n<ul>\n<li>Uses the Auth factory to determine which user is signed in.<\/li>\n<li>Creates a reference to the specific users data in the Firebase realtime database.<\/li>\n<li>Loads the <b>hours<\/b> firebase object within the users data.<\/li>\n<li>Updates this object with the hours that were saved in our app as <b>$rootScope.hours<\/b>.<\/li>\n<li>Saves the data back to Firebase.<\/li>\n<\/ul>\n<pre>\r\n<code>var firebaseUser = Auth.$getAuth();\r\n\r\nif (firebaseUser) {\r\n  var userid = firebaseUser.uid;\r\n  var refUser = firebase.database().ref(userid);\r\n  var refUserHours = refUser.child('hours');\r\n  var objUserHours = $firebaseObject(refUserHours);\r\n\r\n  objUserHours.$loaded().then(function() {\r\n      \r\n    angular.forEach($rootScope.hours, function(value, key) {\r\n      objUserHours[key] = value;\r\n    });\r\n\r\n    objUserHours.$save().then(function(ref) { \r\n      console.log(\"hours saved to Firebase\");\r\n    });\r\n  }\r\n}<\/code>\r\n<\/pre>\n<p>Now you should be able to see your data inside of Firebase for the first time as shown in this screenshot.<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/firebase_object.png\" alt=\"\" width=\"614\" class=\"left_align size-full wp-image-322\" srcset=\"https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/firebase_object.png 922w, https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/firebase_object-300x149.png 300w, https:\/\/appsbyjohn.com\/learn\/wp-content\/uploads\/2017\/07\/firebase_object-768x382.png 768w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<h2>Sync data to Firebase<\/h2>\n<p>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 href=\"https:\/\/github.com\/firebase\/angularfire\/blob\/master\/docs\/reference.md#bindtoscope-varname\" title=\"angularfire bindTo\">a handy method called <code>$bindTo<\/code><\/a> to sync data between the realtime database and the Ionic app using three way data binding. The sample method shown below syncs the same <code>objUserHours<\/code> object with <code>$rootScope.hours<\/code>. It also registers an event listener to catch any changes to the data.<\/p>\n<pre>\r\n<code>objUserHours.$bindTo($rootScope, \"hours\").then(function() {\r\n  console.log(\"now any changes to $rootScope.hours will immediately update Firebase!\");\r\n\r\n  objUserHours.$watch(function() {\r\n    console.log(\"change detected...\");\r\n  });\r\n}<\/code>\r\n<\/pre>\n<p>After binding the Firebase object to your application scope, every change made locally will instantly sync to Firebase. Very handy!<\/p>\n<p>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:<\/p>\n<ul>\n<li>How to resolve differences in local data and &#8220;account&#8221; data on initial load.<\/li>\n<li>Update the local database when changes are detected in the data.<\/li>\n<li>Give users the ability to sign in and out of their account, as well as a &#8220;forgot password&#8221; feature.<\/li>\n<\/ul>\n<p>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 <a href=\"https:\/\/itunes.apple.com\/us\/app\/hours-worked\/id536786928?ls=1&#038;mt=8\" title=\"Hours Worked time tracker for the iPhone\">iOS<\/a> and <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.ionicframework.hoursworked529737&#038;hl=en\" title=\"Hours Worked Time Tracker app for Android\">Android<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/appsbyjohn.com\/learn\/syncing-data-in-an-ionic-app-using-firebase\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Syncing data in an Ionic app using Firebase&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-298","post","type-post","status-publish","format-standard","hentry","category-hybrid-app-development"],"_links":{"self":[{"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/posts\/298","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/comments?post=298"}],"version-history":[{"count":27,"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/posts\/298\/revisions"}],"predecessor-version":[{"id":330,"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/posts\/298\/revisions\/330"}],"wp:attachment":[{"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/media?parent=298"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/categories?post=298"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/appsbyjohn.com\/learn\/wp-json\/wp\/v2\/tags?post=298"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}