Firestack for rapidly building real-time, cross-platform serverless mobile apps

Firebase v3.0 and React Native make a great pair. react-native-firestack is a thin layer between the native JS Firebase libraries that adds support to the rest of the Google stack.

Announcing Firestack

Firebase is a fantastic, real-time Backend-as-a-Service (BaaS, for short) now run by Google. Earlier this year, Google announced a deep integration with the rest of their cloud platform, including a major update to the Firebase service.

Working with the latest iteration of the service with React Native, however, isn't fully supported. Being big fans of both React Native and Firebase, we've written a library to manage this gap and we're open-sourcing it today!

Firestack

Firestack aims to be a thin wrapper around the already fantastic Firebase JavaScript library, both providing a clean interface to the Firestack backend as well as adding support for the features the official library does not support.

This includes features up and down the Google Platform stack, including:

  • authentication
  • real-time database

    • With a FirebaseModule layer that works with callbacks, promises, and redux out-of-the-box
  • storage
  • real-time events
  • remote configuration

While coverage of the entire stack is intended, we are ensuring the major features are covered first, like authentication (for both email/password and oauth-based social logins).

While we are focused on the iOS platform first, plans are in the works to cover Android development (complete with a new Android phone to prove it) ASAP (contributions HIGHLY welcomed).

Although this post assumes you have some familiarity with React, React-Native, and Firebase, our upcoming step-by-step guide build an app store-ready cross-platform, serverless react-native mobile app using react-native-firestack.

Getting started

In order to build a react-native mobile app, we'll need to grab the react-native-cli tools. Let's install those globally using npm:

npm install -g react-native-cli

When we have the cli installed, we can create a bootstrap application. Let's create an application we'll call firebaseDemo using these cli tools.

react-native init firestackDemo

Now, the react-native-cli tools will install the dependencies it knows our application will need and nothing else. In order to install firestack, we'll need to install it as a dependency. We'll also include the latest firebase libraries using npm. In the directory bootstrapped from the react-native init command, let's install these dependencies:

cd firestackDemo
npm install --save react-native-firestack firebase

Next, we'll use the react-native link command to link our project with the firestack dependency:

react-native link react-native-firestack

Building the iOS app

Due to some intricasies of XCode, we do need to make some adjustments to the generated iOS app. To do that, let's open the ios/firestackDemo.xcodeproj file.

When XCode opens, find the Build Phases tab on the project target settings and add a Copy files build phase:

From there, make sure the Copy files build phase says Frameworks and click on the plus button:

Find the newly linked libFirestack.a library and add it to the build phase:

And finally, make sure the Code Sign on Copy checkbox is checked:

Lastly, due to some dependencies with XCode, firestack depends upon a build of iOS 8.0 or higher. We'll need to switch this in our application by finding the General tab, clicking on the Deployment target dropdown and changing it to 8.0:

And now our iOS app is ready to go!

Building the Android app

This process isn't yet complete. We'll update this post when we have Android support ready.

Running our app

Our demo application source is included in it's entirety at the end of this post. Feel free to following along with it or copy and paste it into your project at index.ios.js at the root directory (the firestackDemo/ directory created with the react-native init command).

For convenience, the included demo application looks for configuration files in a config/ directory at the root of the project.

mkdir config/

We've included a demo configuration files here (url: https://gist.github.com/auser/82c9c09e3cd2e40e89f4163b46afdaab). Grab these two files and add them to the config/ directory we just created.

We'll fill in these variables with the settings from your Firebase application. You can make a Firebase application by using the Firebase console. For more details from the Firebase team, check out their docs at https://firebase.google.com/docs/web/setup.

For the purposes of demonstration, we'll set up our app using redux, but the firebase module works with callbacks and promises and does not depend upon Redux.

Let's set up our application to use Redux as a global store. We'll need to include redux-thunk as a middleware layer. Let's install these dependencies using npm again:

npm i --save redux react-redux redux-thunk

And let's set up our basic redux application

// Redux setup
import { applyMiddleware, combineReducers, createStore } from 'redux'
import { Provider, connect } from 'react-redux'
import thunk from 'redux-thunk'
// ...
let reducers = {}
const middleware = applyMiddleware(thunk);
const store = createStore(combineReducers(reducers), {}, middleware)

FirestackModule

Firestack ships with several ways to work with Firebase. Since we use Redux pretty heavily, the easiest way to work with Firestack is to use the new FirestackModule, which you can use to listen to all updates on a store using either Redux or callbacks (both will be called).

import Firestack, { FirestackModule } from 'react-native-firestack'

To create a FirestackModule, import it from Firestack and pass the firestack instance to the module along with a firebase ref string path to the FirestackModule:

const inst = new FirestackModule('events', {firestack})

To be as flexible as possible, you can set the store either on the firestack module or the firestack instance (needed when using Redux):

// To make the dispatch available to just this instance
inst.setStore(store);
// Make the store visible to all `FirestackModule` instances
firestack.setStore(store); 

That's it. Now you just need to tell Firestack that you want to listen for subscription updates to the ref passed on the instance using the FirestackModule instance. In our demo-case, this means listening on the path at events.


  componentWillMount() { 
    inst.listen(); 
  }

Lastly, we'll need to insert the instance reducer into the store. The FirestackModule instance exports a reducer property, which we can use directly in our createStore() and combineReducers() combination.

Check out the sample application at the end of this article for more details on how to handle this specifically.


const store = createStore(combineReducers(reducers), {}, middleware)

That's it. Now our redux store is hooked up to the events. Once the component has been mounted, the Firebase events will flow through the events reducer and update the events leaf of the store. We can treat these updates as regular updates to the Redux store with or without Firebase in the middle. Pretty snazzy, right?

Running the iOS app

In your XCode window, press "Run" (or in the terminal run react-native run-ios) and watch your app update!

The setup is rather simple, but a complete, serverless sample app might look like the example at the bottom of the page.

Over the next several posts, we'll walk through, from start to finish how to build a complete app, from the very beginning through building an application using Firebase/Google as a backend, for both iOS and Android. We'll walk through setting up authentication, handling file storage, dealing with real-time events, in-app billing, etc. We are very excited about this library and will be updating it regularly, along with this post series.

Stay tuned and don't forget to checkout the repo and the README for more details, including all the API available. Contributions are eagerly accepted. We hope to make it a fantastic library for building production applications from the get-go. Feel free to make an issue or check out the source and try contributing (especially if you've written Android applications).


import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

const env = require('./config/environment')

import Firestack from 'react-native-firestack'
import { FirestackModule } from 'react-native-firestack'

// Redux setup
import { applyMiddleware, combineReducers, createStore } from 'redux'
import { Provider, connect } from 'react-redux'
import thunk from 'redux-thunk'

const firestack = new Firestack(env.firestack);
const inst = new FirestackModule('events', {firestack})

let reducers = {
  events: inst.reducer
}
const middleware = applyMiddleware(thunk);
const store = createStore(combineReducers(reducers), {}, middleware)

// firestack.setStore(store);
inst.setStore(store); // important

class firestackDemo extends Component {
  componentWillMount() { 
    inst.listen(); 
  }

  render() {
    const {events} = this.props;
    const items = events.items;
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Event count: {items.length}
        </Text>
      </View>
    );
  }
}

const ConnectedfirestackDemo = connect(state => {
  return {
    events: state.events
  }
})(firestackDemo)

class RootComponent extends Component {
  render() {
    return (
      <Provider store={store}>
        <ConnectedfirestackDemo />
      </Provider>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('firestackDemo', () => RootComponent)

Contact us

If you’re stuck, have further questions, feel free to reach out to us by:


Ari Lerner

Hi, I'm Ari. I'm an author of Fullstack React and ng-book and I've been teaching Web Development for a long time. I like to speak at conferences and eat spicy food. I technically got paid while I traveled the country as a professional comedian, but have come to terms with the fact that I am not funny.

Connect with Ari on Twitter at @auser.