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:
firebase serve
This local server will respect all of your app's configuration rules defined in firebase.json.
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:
data:get
data:set
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:
npm install -g firebase-tools
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
UIViewController
(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.