This post will teach you how to build a fully working presence system using Firebase.
Imagine you have some exciting news you really want to share with a friend. You jump on your favorite social network, open the friends list, and quickly scan for your friend's name. There's a red dot next to it indicating that she's busy - no matter - this is important! You double click to open a chat window...
A lot of us do this little routine every day. Knowing if somebody is currently online or offline is called "presence" and is a pretty basic feature in most collaborative applications (such as chat). What most of us don't realize, however, is that while the feature may appear simple, building the infrastructure to support it from scratch can be very difficult.
Have no fear! Firebase makes adding presence to your application very easy.
A presence system is simply a way of sharing your status with other users. At a basic level, this status could be something simple like whether a given user is currently online or offline. Firebase is great at doing exactly that - sharing state.
The naïve approach - Online / Offline State
The first step when building a presence system is for each client to be able to detect when it is online and offline. Firebase provides a convenient way to do this via the special .info/connected path. Great!
var amOnline = new Firebase('https://<demo>.firebaseio.com/.info/connected'); var userRef = new Firebase('https://<demo>.firebaseio.com/presence/' + userid); amOnline.on('value', function(snapshot) { if (snapshot.val()) { // User is online. userRef.set(true); } else { // User is offline. // WARNING: This won't work! See an explanation below. userRef.set(false); } });
Simple enough, right? The 'value' callback is automatically triggered by Firebase whenever the connection state changes (e.g. a user loses their internet connection) and we can set a boolean value for each user as appropriate.
This won't work in practice though - can you figure out why?
When the user goes offline, our callback will be triggered, but by then it's too late to notify other clients because we aren't connected to the network anymore.
Online / Offline State Done The Right Way
So what do we do? We can solve the offline problem by utilizing a special feature in Firebase known as onDisconnect - a function that tells the Firebase server to do something when it notices a client isn't connected anymore.
This is exactly what we want - we need to instruct the Firebase server to set the user's presence boolean to false when it detects that the client went offline:
false
var amOnline = new Firebase('https://<demo>.firebaseio.com/.info/connected'); var userRef = new Firebase('https://<demo>.firebaseio.com/presence/' + userid); amOnline.on('value', function(snapshot) { if (snapshot.val()) { userRef.onDisconnect().remove(); userRef.set(true); } });
Notice how much smaller this code is. The nice thing about onDisconnect() is that Firebase will automatically handle all of the nasty corner cases for you, like unclean disconnects.
onDisconnect()
A few points to note here:
We don't need to handle an else case. We simply use onDisconnect() to setup a trigger every time the user comes online.
else
The onDisconnect() call is before the call to set() itself. This is to avoid a race condition where you set the user's presence to true and the client disconnects before the onDisconnect() operation takes effect, leaving a ghost user.
set()
The call to onDisconnect().remove() is made every time the user comes online. This is because onDisconnect() operations are performed only once.
onDisconnect().remove()
We store the presence bit for a user independently on a top-level key instead of with the user record itself. This lets us quickly obtain a list of users who are currently online or offline without having to enumerate all user records. This is an example of denormalization, an important principle we've discussed earlier.
There's no special code in the callback for when the user goes offline. This isn't necessary as onDisconnect() will ensure that the presence bit will be updated when the client disconnects.
Hello, are you there? (Idle Status)
You're telling your friend the good news and are having a great conversation. 10 minutes later, you notice your friend isn't responding anymore. Perhaps she had to leave her desk... wouldn't it be great if you were notified somehow that the user had become "idle"?
It's easy to add features like this if we store text instead of a boolean. To detect the idle state of a user, we'll make use of a library called idle.js:
<script src='https://www.firebase.com/js/libs/idle.js'></script> <script> var amOnline = new Firebase('https://<demo>.firebaseio.com/.info/connected'); var userRef = new Firebase('https://<demo>.firebaseio.com/presence/' + userid); amOnline.on('value', function(snapshot) { if (snapshot.val()) { userRef.onDisconnect().set('☆ offline'); userRef.set('★ online'); } }); document.onIdle = function () { userRef.set('☆ idle'); } document.onAway = function () { userRef.set('☄ away'); } document.onBack = function (isIdle, isAway) { userRef.set('★ online'); } </script>
Last seen at...
Let's say you want to share the news with another friend, but notice he's currently offline. Well, there's a big difference between someone being offline for the last 5 minutes and 5 hours. I almost always want to know when a friend was last seen online.
You can make use of Firebase's server-side timestamp feature to implement this. We can store the boolean value true when a particular user is online but set it to a timestamp when the user disconnects via use of onDisconnect and Firebase.ServerValue.Timestamp:
true
onDisconnect
Firebase.ServerValue.Timestamp
var amOnline = new Firebase('https://<demo>.firebaseio.com/.info/connected'); var userRef = new Firebase('https://<demo>.firebaseio.com/presence/' + userid); amOnline.on('value', function(snapshot) { if (snapshot.val()) { userRef.onDisconnect().set(Firebase.ServerValue.TIMESTAMP); userRef.set(true); } });
In your UI, you can now obtain the last time a particular user was online:
var userRef = new Firebase('https://<demo>.firebaseio.com/presence/' + userid); userRef.on('value', function(snapshot) { if (snapshot.val() === true) { // User is online, update UI. } else { // User logged off at snapshot.val() - seconds since epoch. } });
Session History
I can always tell that a friend is having a busy day if he comes online for 10 minutes at a time and keeps going offline. Wouldn't it be cool if we could keep a log of every user's session and know how long each session lasted?
Firebase makes that easy too. We can simply create a new entry in the user's presence 'log' every time they come online by using the convenient push() function and storing the current timestamp. Then, by using an onDisconnect operation, we can record the end time as well:
push()
var amOnline = new Firebase('https://<demo>.firebaseio.com/.info/connected'); var userRef = new Firebase('https://<demo>.firebaseio.com/presence' + userid); amOnline.on('value', function(snapshot) { if (snapshot.val()) { var sessionRef = userRef.push(); sessionRef.child('ended').onDisconnect().set(Firebase.ServerValue.TIMESTAMP); sessionRef.child('began').set(Firebase.ServerValue.TIMESTAMP); } });
Now, by enumerating all the children of https://<my-firebase>.firebaseio.com/presence/<userid> you can display a record of all the user's past sessions. The time at which each session began is stored at https://<my-firebase>.firebaseio.com/presence/<userid>/<sessionid>/began.
https://<my-firebase>.firebaseio.com/presence/<userid>
https://<my-firebase>.firebaseio.com/presence/<userid>/<sessionid>/began
That's It!
You now have a fully working presence system. Don't forget to check out the presence example of the code listed in this blog post. We've also updated our documentation on managing presence to include information on the server timestamps feature. Let us know via email, our Google Group, or comment on this blog if you're building something similar or have found other ways to build a presence system with Firebase.
It’s been two months since we launched Firepad, our open-source collaborative code and rich-text editor built on Firebase. Our mission has always been to empower devs with powerful collaborative editing features at their fingertips. Today we’re excited to announce two new features that make Firepad even more powerful.
List Support
Ordered and unordered lists have been the #1 feature request since we launched Firepad. They have been on our roadmap since the beginning, but there were a number of technical challenges to be solved before we could add them. I'm happy to report these challenges are behind us, and we now have rich support for ordered and unordered lists with arbitrary nesting. Take a look:
HTML Import
Until now, there was no easy way to get existing HTML content into Firepad. This often made it difficult to integrate Firepad into your application if you had existing content stored as HTML. With the advent of list support, we’ve also added HTML import functionality. Just use the firepad.setHtml() method on the API!
What’s Next?
We’re continuing to invest in improving Firepad’s rich-text features as well as evolving the docs and API to make sure that Firepad can be easily integrated into new and existing apps by developers of any skill level. We’d love to hear what you want to see next! Email us or open an issue on GitHub.
If you're in town for WWDC, we'd love to have you over at the Firebase offices for our very first ever WWDC Office Hours. Earlier this year we joined the Apple ecosystem by taking the covers off of the Firebase Objective-C client and it's been a fun ride! (If you haven't already, take a look at our Xcode templates that makes it easy to get started with Firebase).
Come by and meet the Firebase team and other developers building realtime apps.
Firebase WWDC Office Hours Meetup
188 King St. PH8San Francisco CA,94107United Statesdirections
I'm happy to be representing Firebase all week at WWDC. If you're around and want to chat about apps and APIs, you can find me on Twitter @vikrum5000 or drop me an email at vikrum@firebase.com.
We love hackathons, so it shouldn't come as a surprise that we were at AngelHack SF this past weekend. The event was a great success - Firebase was the most-used API at the event, with 22 teams using it to power their hack. Picking winners for our sponsor prizes was really hard!
We spent Saturday morning chatting with various teams and introducing them to some of our newer tools that could help them build their hack. For instance, Firepad is a real-time collaborative text editor that's trivial to embed; and Feed the Fire is a service that makes it very easy to consume RSS or Atom feeds with just a few lines of client-side JavaScript.
We held a breakout session on Saturday evening, where participants could come in and ask questions about Firebase and get help with building their app. The session lasted a whole 90 minutes (much longer than we anticipated), and included building a chat app from scratch without any plugins as a challenge. That part only took 20 minutes, you can see the result here.
The teams started work at 12:30 PM on Saturday and many pulled all-nighters working until code freeze at 12:30 PM on Sunday. 24 hours isn't a lot of time, but we were blown away by some of the apps people managed to build. Here are the winners of our sponsor prizes:
"Get a second pair of eyes on your email". Our first prize went to BlingMail, a browser extension that makes it easy to work on email drafts collaboratively. They won an iPad Mini and Firebase credit worth $5000.
"Code with Friends". Our second prize went to Coderang, a suite of online tools that makes pair programming in various languages a breeze. The author won Firebase credit worth $2500.
"Real Time Professional Collaboration". Our third prize went to RealCollab, an online collaboration tool which includes video conferencing, shared document editing and a shared whiteboard. The team won Firebase credit worth $1000.
Last, but not least, the grand prize winner of AngelHack SF was Liveabetes, which also used Firebase to power its backend. All in all, AngelHack SF was a great success and the whole team had a great time. We look foward to seeing some of you at the next event!
Update (October 3, 2014): Firebase Simple Login has been deprecated and is now part of the core Firebase library. Use these links to find the updated documentation for the web, iOS, and Android clients.
This post was featured as a guest post on the PhoneGap blog.
Many developers have been using Firebase in their PhoneGap applications since our beta launch nearly a year ago. We know many of you encountered some rough edges, so we've put a lot of work into making sure the experience of developing with PhoneGap and Firebase is a smooth one.
Today, we're announcing that the Adobe PhoneGap / Apache Cordova platform is fully-supported by Firebase and works out of the box to offer the full suite of functionality that Firebase provides on the web.
Aligned Philosophies
At Firebase, we're big believers in the power and flexibility in writing applications entirely using client-side code without managing your own servers. PhoneGap embodies this same philosophy in enabling developers to write mobile applications for a broad suite of devices using only client-side JavaScript.
PhoneGap goes further by providing rich APIs for accessing native device features, such as geolocation services or the device camera, that previously required writing device-specific code in the native platform language.
Getting Started
With Firebase, you no longer need to store all of your data locally on the device itself or manually sync it with a remote server using AJAX and your own authentication logic. Firebase makes it easy to enable real-time data synchronization in your PhoneGap application with a simple JavaScript include.
Ready to start building? Head to our web quickstart guide.
What's New
Firebase Simple Login is now fully supported in PhoneGap with the release of PhoneGap 2.3.0, making use of the InAppBrowser for OAuth-based authentication. You can now use Facebook, Twitter, GitHub, and password-based authentication in your PhoneGap application purely with a script include, free from the hassle of configuring application domains or bundle IDs. To see it in action, clone our sample repo and head to the documentation for Firebase Simple Login.