The best way to build a hybrid app is to deal with the underlying details of Cordova as little as possible. For this, Ionic is your best friend. Ionic abstracts the difficult parts of hybrid development into an easy to use SDK.
But, there still is one area of difficulty. Social login. Logging in with a social provider requires a popup or a redirect. The problem is, this doesn’t exist in the native world. It’s okay though, there’s another way, and it’s easy.
For this tutorial, 80% of the battle is just setting up. The actual code writing part is much easier.
If this is your first rodeo with Ionic, here’s a few steps to get you up and running. If you’re a seasoned Ionic veteran, you can skip this section.
Make sure your machine’s version of Node.js is above 4.x. Using npm, download the following dependencies:
4.x
npm i -g ionic && cordova npm i -g ios-deploy
After the install is done, you’ll create a new project.
Using the Ionic CLI, create a new project.
ionic start firebase-social-login tabs
One the setup is done, add both iOS and Android platforms:
ionic platform add android ionic platform add ios
ionic plugin add cordova-plugin-inappbrowser ionic add angularfire
Now that everything is installed, let’s make sure the app is able to run.
To build for either iOS or Android run the following command:
ionic build android # or for ios ionic build ios
Then run the emulator/simulator:
ionic emulate android # or for ios ionic emulate ios
You should see the default project running on the emulated device.
If you want to run it in the browser, then you should totally use Ionic labs.
ionic serve --lab
Ionic labs is a nifty tool that iframes both the iOS and Android styles side-by-side. Which makes for awesome Firebase tutorials, by-the-way.
With the build setup done, let’s get Firebase up and running.
Open up www/index.html. Add the following scripts between Ionic and Cordova.
www/index.html
<!-- Firebase & AngularFire --> <script src="lib/firebase/firebase.js"></script> <script src="lib/angularfire/dist/angularfire.min.js"></script>
After the scripts have been added, open www/js/app.js.
www/js/app.js
Declare AngularFire in the dependency array:
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services', 'firebase'])
The Firebase setup is all taken care of. We’re ready to move on to the fun part, dependency injection!
Add the following two lines of code right below the module declaration:
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services', 'firebase']) .constant('FirebaseUrl', 'https://ionicle.firebaseio.com/') .service('rootRef', ['FirebaseUrl', Firebase])
The rootRef service is a neat trick to inject a reference into any Angular service, factory, controller, provider, or whatever. You’ll use this in the next step, when setting up authentication.
rootRef
Open up www/js/services.js, and add the following code:
www/js/services.js
function Auth(rootRef, $firebaseAuth) { return $firebaseAuth(rootRef); } Auth.$inject = ['rootRef', '$firebaseAuth'];
Make sure to declare this function as a factory in the services angular module:
services
.factory('Auth', Auth);
This style of code is based off the Angular Styleguide, check out the Github repo for more details.
In the code above, you’re simply injecting the rootRef and $firebaseAuth services. Since rootRef is a Firebase database reference, it is passed into the $firebaseAuth service, and returned from the function. The $inject property is a shorthand for declaring dependencies to work with minification.
$firebaseAuth
$inject
That’s all the authentication setup needed. It’s time to create an API key with a social provider.
Firebase authentication allows you to login users to your apps with social providers like Google, Twitter, Facebook, and Github.
This tutorial uses Google, but you can use another if you’d like.
Social login with Firebase requires you to get a set of API keys from a provider. See the Firebase documentation on User Authentication for more details on getting a key and setting it up in the dashboard.
Once you’ve added an API and a Secret key to the Firebase App Dashboard, let’s create the login page.
To create the login page, you need three things: a controller, a view, and a route.
To create the controller, open www/js/controllers.js and add the following snippet:
www/js/controllers.js
function LoginCtrl(Auth, $state) { this.loginWithGoogle = function loginWithGoogle() { Auth.$authWithOAuthPopup('google') .then(function(authData) { $state.go('tab.dash'); }); }; } LoginCtrl.$inject = ['Auth', '$state'];
Then register the controller with the module:
.controller('LoginCtrl', LoginCtrl);
The LoginCtrl will be used with controllerAs syntax. This means you attach methods to the controller using this rather than $scope. The controller has a single method, loginWithGoogle, that will move the user to the 'tab.dash' route once they’re authenticated.
controllerAs
this
$scope
loginWithGoogle
'tab.dash'
The view couldn’t be simpler. Underneath www/templates, create a login.html file and add the following code:
www/templates
login.html
<ion-view view-title="Login"> <ion-content> <div class="padding"> <button class="button button-block button-assertive" ng-click="ctrl.loginWithGoogle()"> Google </button> </div> </ion-content> </ion-view>
The view uses a few components from Ionic’s SDK, like the "assertive" button. When the button is tapped, the loginWithGoogle() method gets called.
loginWithGoogle()
That’s it for the view, let’s move onto the router.
Open www/js/app.js, and find the .config() function. The .config() function injects the $stateProvider, which is used to tell the app which controllers to use for which routes.
.config()
$stateProvider
Add the following route below:
.state('login', { url: '/login', templateUrl: 'templates/login.html', controller: 'LoginCtrl as ctrl' })
Notice that the route sets up the controller property to use controllerAs syntax. This is what allows you to use the ctrl variable in the login.html template.
ctrl
To use the InAppBrowser plugin, do absolutely nothing. Yep, by simply installing the plugin, everything is handled for you.
The problem is, that on mobile there is no analogous “popup” view. The authWithOAuthPopup() method uses window.open() to to open up a new popup window. When this happens, Cordova won’t know what to do. The InAppBrowser plugin fixes this by showing a web browser in your app when window.open() is called.
authWithOAuthPopup()
window.open()
So you can move on, because there’s nothing left to do here.
Build and run the app for the emulator/simulator. You should the a basic login view. Tap the button to login. A browser window should popup, and let you login with a social account. After the authentication process completes, the app should move onto the dashboard view.
Check out the completed app on Github. And, if you’re feeling generous, we would love a star. Feel free to fork the repo, and even send in a PR if you want.
If you’re running into issues, open up a question on Stackoverflow, we closely monitor the Firebase tag, or drop a post in our Google Group.
tns plugin add nativescript-plugin-firebase
"nativescript-plugin-firebase": "^1.2.0"
module.exports = { apiUrl: "https://incandescent-fire-8397.firebaseio.com/" };
viewModel.init = function(){ firebase.init({ url: config.apiUrl }).then( function (instance) { console.log("firebase.init done"); }, function (error) { console.log("firebase.init error: " + error); } ); };
var fetchModule = require("fetch");
var firebase = require("nativescript-plugin-firebase");
login()
register()
app/shared/view-models/user-view-model.js
viewModel.login = function() { return firebase.login({ type: firebase.loginType.PASSWORD, email: viewModel.get("email"), password: viewModel.get("password") }).then( function (response) { config.uid = response.uid return response; }); }; viewModel.register = function() { return firebase.createUser({ email: viewModel.get("email"), password: viewModel.get("password") }).then( function (response) { console.log(response); return response; } ); };
handleErrors()
app/views/register/register.js
exports.register = function() { user.register() .then(function() { dialogsModule .alert("Your account was successfully created.") .then(function() { frameModule.topmost().navigate("views/login/login"); }); }).catch(function(error) { dialogsModule.alert({ message: error, okButtonText: "OK" }); }); }
load()
app/shared/view-models/grocery-list-view-model.js
//to get the index of an item to be deleted and handle the deletion on the frontend function indexOf(item) { var match = -1; this.forEach(function(loopItem, index) { if (loopItem.id === item.key) { match = index; } }); return match; } function GroceryListViewModel(items) { var viewModel = new observableArrayModule.ObservableArray(items); viewModel.indexOf = indexOf; viewModel.load = function() { var onChildEvent = function(result) { var matches = []; if (result.type === "ChildAdded") { if (result.value.UID === config.uid) { viewModel.push({ name: result.value.Name, id: result.key }); } } else if (result.type === "ChildRemoved") { matches.push(result); matches.forEach(function(match) { var index = viewModel.indexOf(match); viewModel.splice(index, 1); }); } }; return firebase.addChildEventListener(onChildEvent, "/Groceries").then( function() { console.log("firebase.addChildEventListener added"); }, function(error) { console.log("firebase.addChildEventListener error: " + error); } ) };
/Groceries
UID
add()
viewModel.add = function(grocery) { return firebase.push( '/Groceries', { 'Name': grocery, 'UID': config.uid }); };
delete()
viewModel.delete = function(index) { var id = viewModel.getItem(index).id; return firebase.remove("/Groceries/"+id+""); };
Firebase Hosting has just been upgraded with new features and a new open source serving architecture!
Since Divshot joined Firebase, we've been hard at work integrating some of the best parts of Divshot's technology into Firebase Hosting. Today we're happy to roll out some results of that effort with support for clean URLs, capture redirects, and an open source foundation!
Note: To take advantage of these new features, you'll need version 2.2.0 or greater of firebase-tools. Upgrade by running npm install -g firebase-tools.
firebase-tools
npm install -g firebase-tools
Everyone likes nice, easily memorable URLs, but when you're deploying a static website that can be tough. Clean URLs allow you to optionally drop .html extensions from your files when serving them. For example an about.html file will be served from /about instead of /about.html.
about.html
/about
/about.html
To enable Clean URLs for your project, simply specify the option in your firebase.json:
firebase.json
{ "cleanUrls": true }
Redirects in Firebase Hosting just got a little smarter with capture groups! Now it's possible to insert segments or entire portions of the source path into the destination. For example:
{ "redirects": [ { "source": "/blog/:post*", "destination": "https://blog.myapp.com/:post*", "type": 301 }, { "source": "/users/:id/profile", "destination": "/users/:id/newProfile", "type": 301 } ] }
You can visit the Firebase Hosting documentation for more information about the new configuration options.
All of these improvements were made possible by migrating our Hosting infrastructure onto Superstatic, the open source static web server originally developed at Divshot. Superstatic 4.0 is used by Firebase Hosting's servers as well as the Firebase CLI: when you run firebase serve your site will run locally exactly as it will appear on Firebase Hosting after you deploy.
firebase serve
Going open source with Superstatic also means that Firebase Hosting features can easily be integrated into your existing local development workflow. For instance, here's how you might use it in your Gulpfile with BrowserSync:
var gulp = require('gulp'); var superstatic = require('superstatic'); var browserSync = require('browser-sync').create(); gulp.task('serve', function() { browserSync.init({ server: { middleware: [superstatic({stack: 'strict'})] } }); gulp.watch('public/*.html').on('change', browserSync.reload); });
The 'strict' stack option ensures that Superstatic will run with precisely the same set of features that are available in production on Firebase Hosting.
'strict'
We hope you enjoy the new capabilities of Firebase Hosting and as always, happy hacking!
Udacity, an online education provider, just launched a new programming course called: Firebase Essentials For Android, which is designed to introduce you to using Firebase on Android. The course is available for free to all registered Udacity users.
During the course, you will create a collaborative, realtime shopping application for Android while learning Firebase-related topics like setting up listeners, authenticating users, structuring data, and creating security rules.
The course is split into five lessons and estimated to take 40-50 hours to complete. Each lesson consists of instructor videos, quizzes and one or more programming assignments. The first two lessons are available now, while lesson three is getting its final polishing. The final two lessons will be available in January.
The Firebase Essentials For Android course was created by Udacity in collaboration with the Firebase team at Google.
Want to see more content like this? Let us know by tweeting us @Firebase or reaching out on the Firebase Talk Google Group.
With the latest Firebase CLI release, it can do much more than just hosting. The latest release gives you the power to read and write data from your Firebase database.
These new data commands simplify tasks like seeding, exporting, and even transferring data from one Firebase database to another. Now you can spend less time writing data seeding scripts and more time developing your app.
This article will cover a few tricks to do some common data operation tasks.
To save data from the command-line use the data:set command.
data:set
firebase data:set /messages messages.json -f my-firebase-db
The first argument is the path you want to write the data to and the second is the JSON file to read from. The last argument is the Firebase to execute the operation against. If you use a / it will save to the root and overwrite existing data. This is perfect for seeding your database.
/
firebase data:set / seed.json -f my-firebase-db
When your database loses its test data or things get out of whack, you can reseed your database with a simple JSON file.
You'll get asked if you really want to overwrite the data in your database. To skip this message and move with confidence you can provide the -y flag.
-y
firebase data:set / seed.json -f my-firebase-db -y
Most developers have a healthy fear of flags labeled -f. But with Firebase, -f is your friend.
-f
The -f flag enables you to specify which Firebase database you’re running the data command against.
firebase data:set /status status.json -f other-firebase-db
This command saves the contents of status.json into the status path of the other-firebase-db Firebase database.
status.json
other-firebase-db
The -f flag opens up a larger set of possibilities, like transferring data to another Firebase database.
Reading data from the CLI is also a simple one-liner.
firebase data:get /messages -f my-firebase-db
The data:get command works just like the data:set command. You can also provide a JSON file to store the data locally.
data:get
firebase data:get /messages > messages.json -f my-firebase-db
If your Firebase database is under 256mb, this is a great way to create a seed to work from.
firebase data:get / > seed.json -f my-firebase-db
You'll notice that your JSON comes back unformatted which isn't the best for human eyes. The data:get command allows you to pipe the result to another command like Python’s json.tool (which comes installed on standard OSX instances).
json.tool
firebase data:get /messages -f my-firebase-db | python -m json.tool
The | is the symbol for piping the output to another source. In this case the json.tool module consumes the piped results from the data:get command.
|
If you’re looking for something even more readable, try using the npm module prettyjson. This module formats JSON into a colored YAML format that’s easy to read from the command-line.
davideast: age: 27 happy: true name: David East erlichbachman: age: 34 happy: false name: Erlich Bachman
The prettyjson module isn't bundled with the CLI so you'll have to install it on your machine. But if you prefer another formatting module, the CLI will pipe the results there too.
prettyjson
npm install -g prettyjson
Transferring data from one Firebase database to another is again a simple one-liner.
firebase data:get / -f my-firebase-db | firebase data:set / -f another-firebase -y
This command is especially helpful if you need to move data to another environment. See our previous blog post for more tips on managing multiple environments with the CLI.
If the project directory contains a firebase.json file, the commands will default to the Firebase database in the JSON file. To create a firebase.json file just use the command:
firebase init
Now when you run commands within that directory you can omit the -f flag.
firebase data:get /messages messages.json
If you find yourself repeating a set of commands it’s probably time to make a bash function. Save your function to your .bash_profile and you’ll be able to access them from anywhere in your console.
.bash_profile
If you commonly transfer data between Firebase databases the function below makes it simple.
function transfer_to() { local master_db="${1}" local dest_db="${2}" local path="${3:-/}" firebase data:get "$path" -f "$master_db" | firebase data:set "$path" -f "$dest_db" -y }
To use the function just call the transfer_to command with the destination Firebase database.
transfer_to
transfer_to dev-firebase-db staging-firebase-db
Another useful function is for formatting data.
function formatted() { local db="${1}" local path="${2:-/}" firebase data:get "$path" -f "$db" | python -m json.tool }
The formatted function takes in a Firebase database and the specified path. The outputted JSON is piped to Python’s json.tool.
formatted
formatted my-firebase-db /users
You can check out the community project firebase-dot-files on GitHub to contribute your tricks.
The Firebase CLI is more than just hosting. How do you seed your Firebase database? Drop us a comment and let us know if you’re using the new CLI features in your workflow.