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.
viewDidLoad
viewWillAppear
viewDidDisappear
override func viewDidLoad() { super.viewDidLoad() ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com") }
- (void)viewDidLoad { [super viewDidLoad]; self.ref = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com"]; }
UIViewController
class ViewController : UIViewController { var ref: Firebase! override func viewDidLoad() { super.viewDidLoad() ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com") } }
Firebase
nil
class ViewController : UIViewController { let ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com") }
class ViewController : UIViewController { // This won't compile :( let ref = Firebase(url: "https://my.firebaseio.com/\(myCoolProperty)") }
override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) ref.observeEventType(.Value) { (snap: FDataSnapshot!) in print (snap.value) } }
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) { NSLog(@"%@", snapshot.value); }]; }
class ViewController : UIViewController { var ref: Firebase! var handle: UInt! override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) handle = ref.observeEventType(.Value) { (snap: FDataSnapshot) in print (snap.value) } } }
@interface ViewController() @property (nonatomic, strong) Firebase *ref; @property FirebaseHandle handle; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.ref = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com"]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.handle = [self.ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) { NSLog(@"%@", snapshot.value); }]; } @end
FirebaseHandle
typealias
UInt
handle
override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) ref.removeObserverWithHandle(handle) }
-(void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [self.ref removeObserverWithHandle:self.handle]; }
A leaky listener is a listener that is consuming memory to store data that isn't displayed or accessed. This is especially an issue when navigating using a UINavigationController, since the root controller isn’t removed from memory when navigating to a detail controller. This means a root controller will continue to synchronize data if the listener isn't removed when navigating away. This action takes up needless bandwidth and memory.
UINavigationController
The thought of removing the listener might sound unappealing. You may think you need to keep your listener open to avoid downloading the same data again, but this is unnecessary. Firebase SDKs come baked in with caching and offline data persistence. These features keep the client from having to fetch recently downloaded data.
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? override init() { Firebase.defaultConfig().persistenceEnabled = true } }
@implementation AppDelegate - (instancetype)init { self = [super init]; if (self) { [[Firebase defaultConfig] setPersistenceEnabled:YES]; } return self; } @end
application:didFinishLaunchingWithOptions
Check out this gist to see the final version of the UIViewController.
We have two big feature announcements: Google Login, and an entirely new version of the Firebase command-line interface.
Many of you have asked for more security on your Firebase account. Starting today, Google Login is the default authentication for firebase.com. If you have an existing Firebase account you'll have the option of migrating to Google Login when you log in to your Firebase account. If you don't have a Firebase account yet, you'll be prompted to create one with Google Login.
This means you'll have access to new security features, like two-factor authentication and Google account recovery. As we work on integrating with more Google services, Google Login will ensure you get access to new features.
In May of 2014, we introduced Firebase Hosting - production-grade hosting for developers. With our CLI, developers could deploy static content with one command: firebase deploy. Since releasing the CLI, we've received a lot of feedback from our developer community and with today’s 2.0.0 CLI release, we're excited to add several new features. We'll outline the highlights below, and you can dive into the docs for all the details.
firebase deploy
You can now start a static web server for your Firebase content by running the command:
This local server will respect all of your app's configuration rules defined in firebase.json.
With today's release, you can read and write data from your Firebase database directly from the CLI. The new data:get command lets you specify a file to output the resulting JSON, or logs it to the command line. Similarly, with data:set you can import a JSON file to your Firebase database or write data directly from the CLI. Let's say we have the following new-users.json file that we'd like to import to our Firebase database at the top-level /users path:
new-users.json
/users
{ "alanisawesome": { "date_of_birth": "June 23, 1912", "full_name": "Alan Turing" }, "gracehop": { "date_of_birth": "December 9, 1906", "full_name": "Grace Hopper" } }
To write this to our database from the command line, we can now run:
firebase data:set /users new-users.json
In addition to reading and writing data, you can also run the commands data:update, data:push, and data:remove. See the documentation for details.
data:update
data:push
data:remove
All CLI commands can now be run programmatically by requiring the firebase-tools module via Node.js. Commands can also be run with a flag to output JSON. For instance, if you want to retrieve a list of the Firebases for your account:
var client = require('firebase-tools'); client.list().then(function(result) { console.log(result); });
These are just a few of the new features that shipped with CLI 2.0.0. Check out the docs for the full details.
We've been working hard on these new features and we're excited for you to try them out! To enable Google Login, log in to your Firebase account and follow the steps outlined in your account dashboard.
To install the latest version of firebase-tools, run:
Check out the documentation for a full list of new CLI features, and let us know what you think by submitting a GitHub issue or pull request.
Happy coding!
Today we're excited to announce that front-end web hosting service Divshot has joined Firebase!
Both teams share a passion for creating fantastic developer experiences, and now we're moving forward together toward that common goal. Divshot and Firebase have teamed up before: Firebase sponsored Divshot's Static Showdown hackathon and was used by more than 50 percent of the developers who participated.
As a cofounder of Divshot, I'm excited to bring the best parts of Divshot's technology to Firebase Hosting. We're launching a brand new Firebase command-line interface today with a local static web server powered by Divshot's open-source Superstatic library. Now you can develop locally with all of your rewrites, redirects, and other Firebase Hosting options. There's more on the way, so stay tuned!
Moving forward the team will be fully focused on making Firebase Hosting a fantastic developer experience, so Divshot will shut down existing products and services on Monday, December 14, 2015. For existing Divshot customers, we have a detailed migration guide to help you easily transition to Firebase Hosting. The transition should be quick and painless (especially because many of you are already Firebase developers!).
I want to thank every Divshot user for their support over the past three years. I’ve been blown away by your creativity and community spirit, and I can't wait to see what you all build next.
Fan-out is a great strategy when it comes to Firebase. Fan-out itself is the process duplicating data in the database. When data is duplicated it eliminates slow joins and increases read performance.
Not all is perfect with fan-out though. The difficult part of fan-out is keeping each duplicated piece up to date.
The latest release of the Firebase SDKs included multi-path updates. This introduced a technique called client-side fan-out.
With client-side fan-out, fanned-out databases can be both performant and consistent.
Multi-path updates allow the client to update several locations with one object. We call this client-side fan-out, because data is "fanned" across several locations. This proven fan-out technique is used by the giants of social messaging.
The fan-out object is the single object that "fans" out the data. To create a fan-out object, specify the deep path as the key of the object.
var updatedUser = { name: 'Shannon', username: 'shannonrules' }; var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com"); var fanoutObject = {}; fanoutObject['/users/1'] = updatedUser; fanoutObject['/usersWhoAreCool/1'] = updatedUser; fanoutObject['/usersToGiveFreeStuffTo/1'] = updatedUser; ref.update(fanoutObject); // atomic updating goodness
let updatedUser = ["name": "Shannon", "username": "shannonrules"] let ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com") let fanoutObject = ["/users/1": updatedUser, "/usersWhoAreCool/1": updatedUser, "/usersToGiveFreeStuffTo/1", updatedUser] ref.updateChildValues(updatedUser) // atomic updating goodness
Map updatedUser = new HashMap(); newPost.put("name", "Shannon"); newPost.put("username": "shannonrules"); Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/"); Map fanoutObject = new HashMap(); fanoutObject.put("/users/1", updatedUser); fanoutObject.put("/usersWhoAreCool/1", updatedUser); fanoutObject.put("/usersToGiveFreeStuffTo/1", updatedUser); ref.updateChildren(fanoutObject); // atomic updating goodness
Then save the object to a top-level location and the updates will be applied to every specific child. In this example we save the fanout object to the root.
The key concept is that this is an atomic operation. Since the update involves only one object the operation will either succeed or fail. This means there's no incomplete states to deal with.
Client-side fan-out can help keep fanned-out data structures consistent.
Let's say you're a big time social network. A fanned-out data structure is a great use case due to the large amount of users, posts, followers, and timelines. A user has a list of followers. Whenever a user makes a post it shows up in their follower's timelines. In a large data scenario, a query of all posts would be too slow to get a single user's timeline. The faster option is duplication.
A flat structure represents each user's timeline as a data structure.
{ "timeline": { "user2": { "-K-zOrtjiCGe7tgRk8DG": { "text": "I love emojis!", "uid": "user1" } }, "user3": { "-K-zOrtjiCGe7tgRk8DG": { "text": "I love emojis!", "uid": "user1" } } }, "followers": { "user1": { "user2": true, "user3": true } } }
This structure duplicates the post to each timeline. Both user2 and user3 are following user1. When user1 sends a post, it's written to each follower's timeline. With this structure any given user's timeline is returned with a single read.
user2
user3
user1
var postsRef = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/posts/user1'); postsRef.on('value', (snap) => console.log(snap.val()));
let postsRef = Firebase(url: 'https://<YOUR-FIREBASE-APP>.firebaseio.com/posts/user1') postsRef.observeEventType(.Value) { print($0) }
Firebase postsRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/posts/user1"); postsRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { System.out.println(snapshot.getValue()); } @Override public void onCancelled(FirebaseError firebaseError) { System.out.println("The read failed: " + firebaseError.getMessage()); } });
You've finally decided to include the highly-requested edit feature for posts. Updating a single post is not a simple process with a fanned-out structure. Each follower must receive the update. Before client-side fan-out the code was problematic. Below is what we had to do before client-side fan-out. For your safety, please don't copy and paste this code.
var postsRef = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/posts'); var queryRef = postsRef.orderByChild('uid').equalTo('user1'); queryRef.on('value', (snap) => snap.forEach((subSnap) => subSnap.ref().update({ title: 'New title' })));
let postsRef = Firebase(url: 'https://<YOUR-FIREBASE-APP>.firebaseio.com/posts') let queryRef = postsRef.queryOrderedByChild("uid").queryEqualToValue("user1") queryRef.observeEventType(.Value) { snapshot in for child in snapshot.children { let childSnap: FDataSnapshot = child as! FDataSnapshot childSnap.ref.updateChildValues([ "title": "New title"]) } })
Firebase postsRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/posts"); Query queryRef = ref.orderByChild("height").equalTo("user1"); queryRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { System.out.println(snapshot.getValue()); for (DataSnapshot childSnap : snapshot.getChildren()) { Map updatedPost = new HashMap(); updatedPost.put("title", "New title"); childSnap.getRef().updateChildren(); } } @Override public void onCancelled(FirebaseError firebaseError) { System.out.println("The read failed: " + firebaseError.getMessage()); } });
The problem with this approach is that this duplication process can fail if there's a lost connection. This failure leaves the data in an incomplete state that can only be fixed by a complicated rollback.
This is the type of problem solved by client-side fan-out.
Rather than execute multiple writes to the Firebase database, let's create one fan-out object to send.
To write to every timeline we need know the user’s uid and their followers. In this example, we pass the user’s uid, an array of the user’s followers, and the post as parameters.
function fanoutPost({ uid, followersSnaphot, post }) { // Turn the hash of followers to an array of each id as the string var followers = Object.keys(followersSnaphot.val()); var fanoutObj = {}; // write to each follower's timeline followers.forEach((key) => fanoutObj['/timeline/' + key + '/' + postId] = post); return fanoutObj; }
func fanoutPost(uid: String, followersSnaphot: FDataSnapshot, post: [String, AnyObject]) -> [String, AnyObject] { let followersData = followersSnaphot as! [String: AnyObject] let followers = followersData.keys.array var fanoutObj = [String, AnyObject] // write to each follower's timeline followers.forEach { fanoutObject["/timeline/\(key)"] = post } return fanoutObj }
public Map fanoutPost(String uid, DataSnapshot followersSnaphot, Map post) { Map fanoutObject = new HashMap<>(); for (DataSnapshot follower: followersSnaphot.getChildren()) { fanoutObject.put( "/timeline/" + follower.getKey(), post); } return fanoutObject; }
To get the followers, create a listener at the /followers/$uid location. Whether you're dealing with an Activity, UIViewController, or web page you need to synchronize the followers on load.
/followers/$uid
Activity
(function() { var followersRef = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/followers'); var followers = {}; followersRef.on('value', (snap) => followers = snap.val()); }());
class MyViewController : UIViewController { var followersRef: Firebase! var followersSnap: FDataSnapshot! override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated); followersRef = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/followers") followersRef.observeEventType(.Value) { self.followersSnap = $0 } } }
public class MainActivity extends AppCompatActivity { Firebase mFollowersRef; Map mFollowers; @Override public void onStart() { super.onStart(); mFollowersRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/followers"); mFollowersRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { mFollowers = (Map) dataSnapshot.getValue(); } @Override public void onCancelled(FirebaseError firebaseError) { } }) } }
When the user sends their post, loop through the follower's keys and create the fan-out object. The key is the timeline location with the follower's key as the child.
(function() { var followersRef = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/followers'); var followers = {}; followersRef.on('value', (snap) => followers = snap.val()); var btnAddPost = document.getElementById('btnAddPost'); var txtPostTitle = document.getElementById('txtPostTitle'); btnAddPost.addEventListener(() => { // make post var post = { title: txtPostTitle.value }; // make fanout-object var fanoutObj = fanoutPost({ uid: followersRef.getAuth().uid, followers: followers, post: post }); // Send the object to the Firebase db for fan-out rootRef.update(fanoutObj); }); // ... fanoutPost method would be below }());
class MyViewController : UIViewController { @IBOutlet var titleTextField: UITextField; var followersRef: Firebase! var followersSnap: FDataSnapshot! override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated); followersRef = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/followers") followersRef.observeEventType(.Value) { self.followersSnap = $0 } } @IBAction func addPostDidTouch(sender: UIButton) { // Make a post var post = ["title", titleTextField.text] // Make fan-out object fanoutPost(followersList.uid, followers: followersSnap, post: post) } // ... fanoutPost method would be below }
public class MainActivity extends AppCompatActivity { Firebase mFollowersRef; Map mFollowers; @Override public void onStart() { super.onStart(); mFollowersRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/followers"); mFollowersRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { mFollowers = (Map) dataSnapshot.getValue(); } @Override public void onCancelled(FirebaseError firebaseError) { } }) // Add click handler Button mPostButton = (Button) findViewById(R.id.postButton); TextView mPostTitle = (TextView) findViewById(R.id.postTitle); mPostButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Map post = new HashMap(); post.put("title", mPostTitle.getText()); fanoutPost(mFollowersRef.getAuth(), mFollowers, post); } }); } // ... fanoutPost method would be below }
And just like that we have a performant, atomic, and consistent data structure.
If you remember anything from this article, remember these three things:
How do you structure your data? Let us know if you've included the new mutli-path update feature.
Today we released new versions of our Android (2.4.0), iOS (2.4.0), and JavaScript (2.3.0) clients. You can read the individual changelogs for details (Android, iOS, web), but we wanted to expand on three highly requested features shipping with this release: atomic multi-location writes, ordering queries by deep paths, and iOS 9 bitcode support. Let's jump in!
Firebase has always supported updating multiple children of a node atomically. For example, let's assume the /user path in our Firebase database contains the following data:
/user
{ "user": { "name": "Samantha", "age": 25 } }
Using the pre-existing atomic update support, we can change the user's age and add a city without updating their name:
age
city
name
var ref = new Firebase("https://.firebaseio.com"); var userRef = ref.child("user"); var newUserData = { "age": 30, "city": "Provo, UT" }; ref.child("user").update(newUserData);
Firebase ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com"); Firebase userRef = ref.child("user"); Map newUserData = new HashMap(); newUserData.put("age", 30); newUserData.put("city", "Provo, UT"); userRef.updateChildren(newUserData);
Firebase *ref = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com"]; Firebase *userRef = [ref childByAppendingPath: @"user"]; NSDictionary *newUserData = @{ @"age": 30, @"city": "Provo, UT" }; [userRef updateChildValues: newUserData];
let ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com") let userRef = ref.childByAppendingPath("user") let newUserData = ["age": 30, "city": "Provo, UT"] userRef.updateChildValues(newUserData)
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com"); // Generate a new push ID for the new post var newPostRef = ref.child("posts").push(); var newPostKey = newPostRef.key(); // Create the data we want to update var updatedUserData = {}; updatedUserData["user/posts/" + newPostKey] = true; updatedUserData["posts/" + newPostKey] = { title: "New Post", content: "Here is my new post!" }; // Do a deep-path update ref.update(updatedUserData, function(error) { if (error) { console.log("Error updating data:", error); } });
Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com"); // Generate a new push ID for the new post Firebase newPostRef = ref.child("posts").push(); String newPostKey = newPostRef.getKey(); // Create the data we want to update Map newPost = new HashMap(); newPost.put("title", "New Post"); newPost.put("content", "Here is my new post!"); Map updatedUserData = new HashMap(); updatedUserData.put("users/posts/" + newPostKey, true); updatedUserData.put("posts/" + newPostKey, newPost); // Do a deep-path update ref.updateChildren(updatedUserData, new Firebase.CompletionListener() { @Override public void onComplete(FirebaseError firebaseError, Firebase firebase) { if (firebaseError != null) { System.out.println("Error updating data: " + firebaseError.getMessage()); } } });
Firebase *ref = [[Firebase alloc] initWithUrl: @"https://<YOUR-FIREBASE-APP>.firebaseio.com"]; // Generate a new push ID for the new post Firebase *newPostRef = [[ref childByAppendingPath:@"posts"] childByAutoId]; NSString *newPostKey = newPostRef.key; NSString *updateUserPath = [NSString stringWithFormat:@"users/posts/%@", newPostKey]; NSString *updatePostPath = [NSString stringWithFormat:@"posts/%@", newPostKey]; // Create the data we want to update NSMutableDictionary *updatedUserData = [[NSMutableDictionary alloc] init]; [updatedUserData setValue:[NSNumber numberWithBool:YES] forKey:updateUserPath]; [updatedUserData setValue:@{@"title": @"New Post", @"content": @"Here is my new post!"} forKey:updatePostPath]; // Do a deep-path update [ref updateChildValues:updatedUserData withCompletionBlock:^(NSError *error, Firebase *ref) { if (error) { NSLog(@"Error updating data: %@", error.debugDescription); } }];
let ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com") // Generate a new push ID for the new post let newPostRef = ref.childByAppendingPath("posts").childByAutoId() let newPostKey = newPostRef.key // Create the data we want to update let updatedUserData = ["users/posts/\(newPostKey)": true, "posts/\(newPostKey)": ["title": "New Post", "content": "Here is my new post!"]] // Do a deep-path update ref.updateChildValues(updatedUserData, withCompletionBlock: { (error, ref) -> Void in if (error) { print("Error updating data: \(error.description)") } })
Deep path updates let you write cleaner code and easily denormalize data across multiple nodes in your Firebase database.
Up until now, we have been able to order queries by direct children. Here's a snippet of the dinosaur data stored in our database of dinosaur facts:
{ "lambeosaurus": { "height" : 2.1, "length" : 12.5, "weight": 5000 }, "stegosaurus": { "height" : 4, "length" : 9, "weight" : 2500 } }
To read all dinosaurs ordered by height, we can order nodes by a common child key:
var ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs"); ref.orderByChild("height").on("child_added", function(snapshot) { var dinoName = snapshot.key(); var dinoData = snapshot.val(); console.log(dinoName + " was " + dinoData.height + " meters tall"); });
Firebase ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs"); Query queryRef = ref.orderByChild("height"); queryRef.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot snapshot, String previousChild) { DinosaurFacts facts = snapshot.getValue(DinosaurFacts.class); System.out.println(snapshot.getKey() + " was " + facts.getHeight() + " meters tall"); } // .... });
Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"]; [[ref queryOrderedByChild:@"height"] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { NSLog(@"%@ was %@ meters tall", snapshot.key, snapshot.value[@"height"]); }];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs") ref.queryOrderedByChild("height").observeEventType(.ChildAdded, withBlock: { snapshot in if let height = snapshot.value["height"] as? Double { println("\(snapshot.key) was \(height) meters tall") } })
This works great, but sometimes we want to order by descendants further down in our data tree. Let's say our dinosaur data actually looked like this:
{ "lambeosaurus": { "dimensions": { "height" : 2.1, "length" : 12.5, "weight": 5000 } }, "stegosaurus": { "dimensions": { "height" : 4, "length" : 9, "weight" : 2500 } } }
Previously there was no way to sort these dinosaurs based on any of their dimensions since they are stored too many levels deep. Now we can use a deep path query!
var ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs"); ref.orderByChild("dimensions/height").on("child_added", function(snapshot) { var dinoName = snapshot.key(); var dinoData = snapshot.val(); console.log(dinoName + " was " + dinoData.dimensions.height + " meters tall"); });
Firebase ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs"); Query queryRef = ref.orderByChild("dimensions/height"); queryRef.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot snapshot, String previousChild) { DinosaurFacts facts = snapshot.getValue(DinosaurFacts.class); System.out.println(snapshot.getKey() + " was " + facts.getHeight() + " meters tall"); } // .... });
Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"]; [[ref queryOrderedByChild:@"dimensions/height"] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { NSLog(@"%@ was %@ meters tall", snapshot.key, snapshot.value[@"dimensions"][@"height"]); }];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs") ref.queryOrderedByChild("dimensions/height").observeEventType(.ChildAdded, withBlock: { snapshot in if let height = snapshot.childSnapshotForPath("dimensions/height").value as? Double { println("\(snapshot.key) was \(height) meters tall") } })
The ability to order by deep paths gives you much more freedom in structuring your data.
The Firebase iOS client is now compiled with bitcode support, a new iOS 9 feature. Bitcode is an "intermediate" language that Apple uses to represent your iOS and watchOS apps. According to Apple, “including bitcode will allow [us] to re-optimize your app binary in the future without the need to submit a new version of your app to the store.” This enables you to ship smaller apps through app thinning, as well as design for future products, decreasing development time and increasing the number of platforms upon which your app runs.
On top of the new features mentioned above, we have also fixed a handful of bugs across each of the clients. Memory leaks, cache inconsistencies, and crashes have been tracked down and fixed in today's releases.
All of these new features and bug fixes are now available in version 2.4.0 of the mobile clients and version 2.3.0 of the JavaScript client. See the installation and setup guides for each platform (Android, iOS, web) to learn how to get started with the latest clients.
We'd love to hear what you think of this release. Post any feedback or submit bug reports in our Google Group. Lastly, if you've built anything, don't be shy! Share your apps with us on Twitter @Firebase. We're excited to see what you build.
Designed to help native mobile developers quickly and easily develop Firebase apps, FirebaseUI eliminates bloat in your application code by providing native bindings to common UI controls.
FirebaseUI adds support to quickly hook a Firebase database reference or query to a ListView, GridView, RecyclerView on Android or a UITableView or UICollectionView iOS.
ListView
GridView
RecyclerView
UITableView
UICollectionView
Start by creating an Android Firebase project or iOS Firebase project then follow our Android or iOS quickstarts below.
On Android, add the latest version of FirebaseUI to your build.gradle:
build.gradle
compile 'com.firebase:firebase-ui:0.2.0'
Resync your project and you're ready to continue.
More information, including a more in-depth installation walkthrough, can be found on the FirebaseUI-Android README.
Once we've installed FirebaseUI, create a FirebaseListAdapter which binds a Firebase database reference or query to a ListAdapter and provides developers an easy way to populate ListView views with developer specified model objects (more information on using Java Objects is available in the docs).
FirebaseListAdapter
ListAdapter
Firebase.setAndroidContext(this); Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com"); mAdapter = new FirebaseListAdapter<ChatMessage>(this, ChatMessage.class, android.R.layout.two_line_list_item, ref) { @Override protected void populateView(View view, ChatMessage object) { /* Populate view with contents of the model object */ } }; mListView.setListAdapter(mAdapter);
If you want more examples, check out the Android README, or see our Android chat app example. The Javadoc is available here.
On iOS, add the latest version of FirebaseUI to your podfile:
podfile
pod 'FirebaseUI', '~> 0.2.0'
If you're using Swift, make sure to add:
platform :ios, '8.0' use_frameworks!
pod install, then open the generated .xcworkspace.
pod install
.xcworkspace
More information, including a more in-depth installation walkthrough, can be found on the FirebaseUI-iOS README.
Once we've installed FirebaseUI, create a FirebaseTableViewDataSource. This datasource will provide cells and the associated FDataSnapshots (or a custom model object, a feature in beta) to populate UITableViewCells, a custom subclass, or even a custom XIB.
FirebaseTableViewDataSource
FDataSnapshots
UITableViewCells
XIB
Firebase *ref = [Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/chat"]; self.dataSource = [[FirebaseTableViewDataSource alloc] initWithRef:ref modelClass:[FDataSnapshot class] cellClass:[UITableViewCell class] cellReuseIdentifier:@"" view:self.tableView]; [self.dataSource populateCellWithBlock:^void(UITableViewCell *__nonnull cell, FDataSnapshot *__nonnull snap) { /* Populate cell with contents of the snapshot */ } }]; self.tableView.dataSource = self.dataSource;
let ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio-demo.com") self.dataSource = FirebaseTableViewDataSource(ref: ref, modelClass: FDataSnapshot.self, cellClass: UITableViewCell.self, cellReuseIdentifier: "", view: self.tableView) self.dataSource.populateCellWithBlock { (cell: UITableViewCell, obj: NSObject) -> Void in let snap = obj as! FDataSnapshot // Force cast to an FDataSnapshot /* Populate cell with contents of the snapshot */ } self.tableView.dataSource = self.dataSource
FirebaseUI can also be used to back a UICollectionView using the FirebaseCollectionViewDataSource.
FirebaseCollectionViewDataSource
If you want more examples, check out the iOS README, or see our iOS chat app example. The Appledoc is available here.
We're working on making mobile development as effortless as possible, so we're always looking for ways to improve development speed and reduce complexity. One major area that we hear a lot of developers struggle is around mobile authentication, which typically requires installation of third party SDKs and understanding lots of additional docs.
While we have login demo apps on Android and iOS that explain these processes, FirebaseUI 0.3 will incorporate a set of login helper classes and standard views to make adding authentication to your mobile apps simple.
Our long term strategy involves adding support for pagination, infinite scroll, and client side filtering. We'd also love to hear feature requests from you.
Do you want to see those features added faster? Are you excited about making Firebase apps easier to develop? Is writing quality code your passion?
We'd love to have your help building and maintaining these libraries. Please submit issues for bugs and feature requests on Android and iOS, and pull requests are always welcome.
Lastly, if you've built anything, don't be shy! Share your apps with us on Twitter @Firebase or in our Google Group--we're excited to see what you build.
Firebase is growing faster every day, and since joining Google last year, we've gained access to world-class infrastructure and scale. Today, we're announcing across-the-board price drops to lower the barrier to entry for Firebase, so developers everywhere can create extraordinary experiences for their users.
Here are the changes we've made:
We've heard from you that concurrents are confusing. So today, we're eliminating the need to worry about them… they're now unlimited for our Candle plan and above!
To prevent abuse, we're limiting simultaneous connections to a single Firebase to 10,000 by default, which is generally enough for applications with at least 100,000 daily active users. We can scale far beyond that limit, and users can contact us to permanently raise the cap with 24 hours notice.
Firebase is about giving people the tools to build extraordinary apps. Experimentation is core to what we stand for, and we believe people should be able to build non-trivial apps on Firebase for free.
Therefore, we are widening our free tier to:
In addition, you can now add a custom domain to your free app for only $5/month, with our new Spark plan. No more need to upgrade to the Candle plan just to get a custom domain!
Finally, we're increasing the resource limits on all our paid plans. The Candle, Bonfire, Blaze, and Inferno plans are all seeing big increases in database bandwidth and storage, up to 3x. We've also lowered overage fees to increase flexibility as your app scales.
Here's a full summary of the changes, and you can check out our new pricing page here:
It has been a pleasure building Firebase, and it's humbling to see the great things you build with us. We've got some great new features coming soon, and are more excited than ever for the future.
Happy Coding!
Today's EmberFire release includes new features that will make it even easier for Ember developers to connect their apps to Firebase. The EmberFire 1.5.0 release brings Ember Data 1.13.0 compatibility, integration with Firebase Authentication, and some updates for embedded records.
EmberFire is an adapter for using Firebase with Ember Data. It is packaged as an Ember CLI addon, which makes it incredibly easy to add Firebase as the backend for your Ember app. Using EmberFire, all of your models stored in Ember Data will be automatically synchronized with our remote Firebase database. Simply calling save() on a record will save it to the store and persist that data in your Firebase database.
save()
Ember Data has made it out of beta, which means we can count on a stable API. This is great news for EmberFire. In addition to 1.13.0 compatibility, we're working on staying up to date with the latest releases, and our tests are also passing for 2.0 canary!
You can now authenticate users in your EmberFire app with Facebook, Twitter, GitHub, and Google using our Torii adapter. Get started by enabling the authentication provider you’d like to use in your Firebase Dashboard, installing the Torii addon, and following the authentication section in our guide. Once you've set it up, you can authenticate a user inside your route or controller with the following snippet:
this.get("session").open("firebase", { provider: "twitter"}) .then(function(data) { console.log(data.currentUser); });
This snippet of code will authenticate a user with Twitter and log the returned user object to the console.
Many thanks to Matt Sumner for his work on this.
This release also changes the way embedded records are declared. If you have an embedded hasMany / belongsTo relationship in your EmberFire app, you should now follow the Ember Data conventions by configuring embedded record settings in a serializer. Visit the relationships section of our guide for details. Just be sure to use Ember Data 1.13.6+ for embedded records compatibility.
hasMany
belongsTo
If you're new to EmberFire, using it with your Ember CLI app is as simple as installing our addon:
ember install emberfire
Then, update your Firebase URL in config/environment.js and you're ready to persist your Ember Data records in your Firebase database. Check out our guide for a detailed walkthrough on all things EmberFire.
config/environment.js
We'd love to hear what you think of the latest release. Post any feedback in our Ember Google Group, or open an issue on GitHub. Contributions to EmberFire are always welcome, so feel free to submit a pull request if there's something you'd like to see added.
We're excited to see what you build with Firebase and Ember!
React and Firebase were made for each other. And with the introduction of ReactFire back in May 2014, we provided a simpler way to build apps using both technologies. Today we're announcing ReactFire 0.5.0 - the biggest update since that initial release. The update brings improved performance, a handful of bug fixes, and some extra features to simplify development.
For those who aren't familiar, ReactFire is a React mixin. The ReactFireMixin makes it dead simple to bind data stored in Firebase to a component's state. It handles both array-like and object-like data via the bindAsArray() and bindAsObject() methods, respectively.
ReactFireMixin
bindAsArray()
bindAsObject()
For example, say you have a list of messages stored in your Firebase database that looks like this:
{ "messages": { "-JqpIO567aKezufthrn8": { "uid": "barney", "text": "Welcome to Bedrock City!" }, "-JqpIP5tIy-gMbdTmIg7": { "uid": "fred", "text": "Yabba dabba doo!" } } }
You can bind the message data to your component's state by calling bindToArray() in your componentWillMount() method:
bindToArray()
componentWillMount()
var ExampleComponent = React.createClass({ mixins: [ReactFireMixin], getInitialState: function() { return { messages: [] }; }, componentWillMount: function() { var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/messages"); this.bindAsArray(ref, "messages"); }, render: function() { // ... } });
The message data is now bound to this.state.messages. Whenever messages are added, removed, updated, or moved in Firebase, those changes are reflected automatically in this.state.messages
this.state.messages
this.setState()
render()
render: function() { var messages = this.state.messages.map(function(message) { return ( <li key={ message['.key'] }>{ message.uid } says { message.text } </li> ); }); return <ul>{ messages }</ul> ; }
The 0.5.0 release is the first major upgrade to the ReactFireMixin since its initial release. The API is still just as minimal, but a lot has changed under the hood.
The initial implementation of bindAsArray() was quite simplistic. It had poor performance and did not properly handle complex Firebase queries. The new implementation provides improved performance (especially on large data sets) and works well with all Firebase queries. Best of all, no changes to your code are required to enable these improvements.
Another major change with 0.5.0 is the addition of the .key attribute. ReactFire now adds the .key attribute to every record it creates via the bindAsArray() and bindAsObject() methods. In previous versions, it was impossible to retrieve key names for bound items. The 0.5.0 release fixes this by including the key name by adding a .key attribute to each bound item.
.key
As an example, let's look again at the messages array from above. The resulting bound array stored in this.state.messages will be:
[ { ".key": "-JqpIO567aKezufthrn8", // New in version 0.5.0! "uid": "barney", "text": "Welcome to Bedrock City!" }, { ".key": "-JqpIP5tIy-gMbdTmIg7", // New in version 0.5.0! "uid": "fred", "text": "Yabba dabba doo!" } ]
The inclusion of .key makes it possible to easily write data back to Firebase to update, remove, or move a record in the array. All objects synced via bindAsObject() now include the .key attribute as well.
Finally, version 0.5.0 changes how primitive values (strings, numbers, booleans) are handled. As an example, say you have the following data in your Firebase database:
{ "foo": "bar" }
If you call bindAsObject() on the /foo/ path, previous versions bound the value as a string "bar". As of today's 0.5.0 release, the bound object now looks like this:
/foo/
"bar"
{ ".key": "foo", ".value": "bar" }
You have access to the key that was bound to (.key) as well as the bound value (.value). Primitive values bound via bindAsArray() behave in the same way with the primitive value being stored in .value. Non-primitive values (that is, regular JavaScript objects) have not changed and behave in the same in all versions, except for the addition of .key.
.value
There is a lot of excitement in the React community about ES6+, Relay, GraphQL, and ReactNative. Discussions about these topics are even ongoing in the ReactFire GitHub repo. We are following the new announcements and technologies closely. Since we use React in production at Firebase, we want the two technologies to work as well as possible together.
We've found that using the vanilla React and Firebase libraries together works great and somewhat replaces the need for an application architecture like Flux. Things like ReactFire simplify development further by providing a shorthand for a lot of common use cases. And community contributions like the recently released re-base library show how well Firebase can fit into new technologies like Relay. The future of a React + Firebase application tech stack is exciting and promising. The two technologies make it that much easier to build scalable apps in a fraction of the time they traditionally have taken. You can expect ReactFire to evolve alongside React over the course of the comings months.
re-base
The best place to get started with ReactFire is by heading over to the docs. We have a five minute quickstart, a guide which walks you through making an example app, and a full API reference with more examples of binding data via bindAsArray() and bindAsObject().
If you run across any bugs or have feature suggestions, please file them on GitHub. If you have questions about the API or how to build apps with ReactFire, post them on the Firebase Google Group.
We are excited to see what you create!
If you're like me, you're really excited about React Native, a new way to build (native!) mobile applications with JavaScript. Today, I'm happy to announce that thanks to the efforts of our developer community, Firebase and React Native now work together.
React is a view library created and maintained by Facebook. I love React for its declarative API, which makes managing changes in your views dead simple.
When you build your application's views with React, it automatically updates the UI of your application when it observes state changes in your data model. Conveniently, Firebase databases deliver a stream of realtime updates to your data model. With Firebase and React, it's effortless to create components that automatically update in realtime. The two pair beautifully together.
If you're not familiar with React — and for more details on using it with Firebase — our very own Jacob Wenger wrote this excellent introduction to Firebase and React, which explains the basics of React in more detail.
React Native is a new framework that helps you build applications for native platforms primarily with JavaScript and React. React Native for iOS was released earlier this year. The React Native components in your iOS app render directly to real, native UIKit components — it's not a web app running on a mobile device.
Here's an example of some JavaScript you might write to create a simple view, from the React Native homepage. If you're familiar with React, you'll notice how instead of declaring HTML DOM elements like <div>s and <p>s, you declare components that map directly to standard iOS platform components.
<div>
<p>
var React = require('react-native'); var { TabBarIOS, NavigatorIOS } = React; var App = React.createClass({ render: function() { return ( <TabBarIOS> <TabBarIOS.Item title="React Native" selected={true}> <NavigatorIOS initialRoute={{ title: 'React Native' }} /> </TabBarIOS.Item> </TabBarIOS> ); }, });
Did I mention this is just JavaScript? React Native also has support for ES6 syntax (shown above), and you can write your app with Flow, Facebook's static type checker for JavaScript.
Other bonuses of React Native include support for npm modules, debugging in Chrome Developer Tools, and instant reload in XCode (as in, hit Cmd-R and your app reloads in a few seconds instead of having to do full XCode rebuilds). It's a wonderful development experience.
Cmd-R
Although React Native is still in beta, it's currently being used in production in some of Facebook's apps, including its Groups app.
In short, I'm excited about React Native because it lets me write native mobile apps with the technologies I use every day as a web developer.
If you'd like to start getting your hands dirty with React Native, the official tutorial is an excellent place to start.
Once you've created your React Native app, just head to your nearest terminal and enter these magic characters to install Firebase via npm:
npm install firebase --save
Then, in your JavaScript code just softly whisper var Firebase = require('firebase'); and all the power of the Firebase JavaScript SDK and React Native will be yours!
var Firebase = require('firebase');
If you have any questions, bug reports, general commentary, or if you just want to say hi, you can find me on Twitter at @KanYang.
Thanks to Harrison Harnisch, a developer in our community, for helping to get support for WebSockets merged into React Native!
Have you ever had an app suddenly stop working when you lost your internet connection? Or perhaps you've opened an app you were using just minutes prior, only to find you have to re-download all of your data?
As a developer, you've likely tried to solve these pain points in your own apps, only to find the work required to be daunting. Wouldn't it be great if your database could just handle this for you automatically?
Today we're announcing a set of features for our iOS and Android SDKs that will allow you to build apps that work smoothly offline!
Our new SDKs support persisting your synchronized data to disk, so it's available immediately when your app starts. You can enable disk persistence with one line of code:
Firebase.getDefaultConfig().setPersistenceEnabled(true);
[Firebase defaultConfig].persistenceEnabled = YES;
Firebase.defaultConfig().persistenceEnabled = true
Our new SDKs now also let you specify data to be prefetched and kept up to date so that it will be available offline later if you need it. This keepSynced feature can be enabled on a per-path basis with one line of code:
keepSynced
ref.keepSynced(true);
[ref keepSynced:YES];
ref.keepSynced(true)
The Firebase database uses synchronization rather than request / response to move data between client and server. All read and write operations happen against a local, on-device version of your database first. Then, data is pushed and pulled from our servers behind the scenes. This design has allowed the Firebase database to compensate for network latency -- local write operations are reflected by local reads before being acknowledged by the server.
It turns out, though, that this design is also essential to a high quality offline experience. By sending all reads and writes through a local version of the database, the Firebase SDK maintains the freedom to serve that data from the best source available -- whether that’s from the network or local disk. Later, when a network connection becomes available, our SDK automatically commits local operations to the server and pulls in the latest remote updates.
We designed Firebase for offline support from the very beginning, and we've been working on it for years. Citrix has been beta testing our offline support for its iOS app Talkboard since 2013. When you draw on a canvas in Talkboard, any updates you make while offline will be saved to disk and synchronized later when you reopen the app.
We've been working on these features for a long time, and we couldn't be more excited to see what you build with them! Getting started is easy -- just check out the offline section of our guides for iOS or Android. We also have offline drawing examples for iOS and Android that can help you get started quickly.
As you're coding, we'd love to hear what you think! Please share your feedback in our Google Group or mention @Firebase on Twitter.
Many of our developers are using Firebase with a server to perform tasks like background processing, integrating with third party APIs, or handling advanced authentication requirements. Today, we're introducing Firebase Queue, a fault-tolerant multi-worker job pipeline built on Firebase.
If you're writing your server code in Node, Firebase Queue makes it easy to handle background jobs with a worker queue. We're already using it in our private backups feature, where we reliably handle hundreds of jobs per day across multiple machines.
Once you've installed firebase and firebase-queue, you can get started by creating a new Queue and passing it your Firebase reference and a processing function. This processing function specifies the data transformation that the worker performs and when called, will be passed the following four parameters:
data
progress()
resolve()
reject()
Below is a simple example of what an instance of the queue looks like:
var Queue = require('firebase-queue'), Firebase = require('firebase'); var queueRef = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/queue'); var queue = new Queue(queueRef, function(data, progress, resolve, reject) { // Read and process task data console.log(data); // Update the progress state of the task setTimeout(function() { progress(50); }, 500); // Finish the job asynchronously setTimeout(function() { resolve(); }, 1000); });
When your Queue runs, the data will be stored in Firebase under a /queue/tasks path and removed when complete. For an item in your queue called example1, the data in Firebase would look like the following:
/queue/tasks
example1
{ "queue": { "tasks": { "example1": { "_owner": "worker_1", // The worker process who owns this task "_progress": 88, // A number from 0 to 100 indicating the item’s status in the queue "_state": "in_progress", // Indicates whether the job has errored, is in progress or finished "_state_changed": 1430937731946, // Timestamp when the state was last updated "your": "data" // Original data in your task } } } }
You can push an object with some data to the /queue/tasks location in your Firebase using any Firebase client or the REST API. Workers listening at that location will automatically pick up and process the job:
Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/queue/tasks"); Map task = new HashMap(); task.put("your", "data"); ref.push().setValue(task);
Firebase *ref = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/queue/tasks"]; [[ref childByAutoId] setValue:@{@"your": @"data"}];
var ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/queue/tasks") ref.childByAutoId().setValue(["your": "data"])
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/queue/tasks"); ref.push({"your": "data"});
curl -X POST -d '{"your": "data"}' https://<YOUR-FIREBASE-APP>.firebaseio.com/queue/tasks.json"
If you don't want to use the default spec, you can define your own. You can also chain several specs together to create custom jobs. More details on customizing and chaining jobs can be found in the Firebase Queue README.
We have a few examples to help you get started with Firebase Queue. This gist creates a new Queue with 10 workers and a custom task task_1. In this example, the queue sanitizes chat messages to make sure they don’t contain any banned words.
task_1
When you try out firebase-queue in your app I'd like to hear what you think. Send a pull request, file an issue, or reach out to me on Twitter @drTriumph.
Ionic is an HTML5 mobile app SDK that makes it extremely simple to build native-feeling iOS and Android apps with JavaScript. It’s built on top of Cordova and uses AngularJS for its core functionality. I love learning new frameworks, and I’ve been thoroughly impressed with how easy it is to get up and running with Ionic. Since Ionic uses Angular, it’s incredibly simple to wire up with Firebase as the backend.
This screencast will help you dive into mobile web development with Ionic in less than 8 minutes. It assumes you are familiar with the basics of Angular and walks you through building a cross-platform collaborative grocery list application. Check it out below:
I’d like your feedback! If there are features you’d like to see in an upcoming screencast, send me your suggestions on Twitter at @SRobTweets.
Uri is a Firebase Expert based in Tel Aviv, Israel. He works at WatchDox, organizes the Tel Aviv Google Developers Group, and is an avid Angular fan.
I built an open-source project called firebase-server to implement end-to-end tests in my own application. With firebase-server, my end-to-end tests are now running 40% faster and I no longer depend on an Internet connection for running the tests in development.
Because Firebase is a cloud service, it poses some challenges for performing automated tests. For unit tests, using a mock for the Firebase client library will work well. If you are writing a web application, you can quickly mock just the methods you use with either Sinon or Jasmine, or use MockFirebase, an open-source Firebase mock.
When it comes to writing end-to-end tests, however, you usually want to test the system as a whole, including the integration with the real Firebase client library to verify that the realtime synchronization between clients is functioning correctly. In addition, the end-to-end testing code runs outside the context of your application and you should make some assertions about the data it saves to Firebase.
Before building firebase-server, I used the Firebase client library in my end-to-end tests to talk directly to the Firebase cloud service. The issues with this method were that I needed an internet connection to run the tests and my tests ran slowly if my internet connection was lagging.
Frustrated with my end-to-end tests occasionally timing out, I started looking for a solution. MockFirebase would solve the problem only if my testing and app code lived within the same process, and even then, I would not be testing the real Firebase client library.
I decided that the best solution would be to put MockFirebase into a separate process, and make the Firebase client library connect to that process. This is when firebase-server was born!
firebase-server is a simple Node module which speaks the Firebase Wire Protocol, and can be used as a proper substitute for the real Firebase server in your e2e testing scenarios. You can also use it to develop code against Firebase in times when you don't have an internet connection.
First, install the package from npm:
npm install --save-dev firebase-server
Next, you can launch the firebase server by doing the following:
var FirebaseServer = require('firebase-server'); new FirebaseServer(5000, 'test.firebase.localhost', { /* You can put your initial data model here, or just leave it empty */ });
Before you can connect to your Firebase, there is one limitation of the client library we need to work around: it requires the hostname of your firebase to include exactly two dots. You can work around this by adding a hosts file entry pointing 'test.firebase.localhost' to the local IP 127.0.0.1.
'test.firebase.localhost'
127.0.0.1
Add the following line to your hosts file:
# Your hosts file can be found in: # Linux: /etc/hosts # Mac: /private/etc/hosts # Windows: c:\Windows\System32\Drivers\etc\hosts 127.0.0.1 test.firebase.localhost
For more information about editing your hosts file, see this page.
Now, everything is ready and you can switch to your local Firebase server. Simply change the connection string from https://myFirebase.firebaseio.com to ws://test.firebase.localhost:5000. For instance:
https://myFirebase.firebaseio.com
ws://test.firebase.localhost:5000
var client = new Firebase('ws://test.firebase.localhost:5000'); client.on('value', function(snap) { console.log('Got value:', snap.val()); });
If you do not wish to edit your hosts file, you can also use ws://127.0.1:5000. This trick seems to work inside Chrome and Firefox. With Node.JS, you can use another trick: overriding the constructor of the faye-websocket Client. Check out the firebase-server unit testing code for an example.
ws://127.0.1:5000
I'd love to hear any suggestions you have on firebase-server. You can file an issue or submit a pull request on GitHub, or send me your feedback on Twitter, I'm @UriShaked.