One of my favorite parts about working with the Cloud Functions for Firebase team is helping developers move logic from their mobile apps to a fully managed backend hosted by Firebase. With just a few lines of JavaScript code, they're able to unify logic that automatically takes action on changes to the contents of their Realtime Database. It's really fun to see what people build!
When Cloud Functions was first announced at Cloud Next 2017, there was just one trigger available for all types of changes to the database. This trigger is specified using the onWrite() callback, and it was the code author's responsibility to figure out what sort of change occurred. For example, imagine your app has a chat room, and you want to use Firebase Cloud Messaging to send a notification to users in that room when a new message arrives. To implement that, you might write some code that looks like this:
onWrite()
exports.sendNotification = functions.database .ref("/messages/{messageId}").onWrite(event => { const dsnap = event.data // a DeltaSnapshot that describes the write if (dsnap.exists() && !dsnap.previous.exists()) { // This is a new message, not a change or a delete. // Send notifications with FCM... } })
Note that you have to check the DeltaSnapshot from the event object to see if new data now exists at the location of the write, and also if there was no prior data at that location. Why is this necessary? Because onWrite() will trigger on all changes to data under the matched location, including new messages, updated messages, and deleted messages. However, this function is only interested in new messages, so it has to make sure the write is not an update or a delete. If there was an update or delete, this function would still get triggered and return immediately. This extra execution costs money, and we know you'd rather not be billed for a function that doesn't do useful work.
The good news is that these extra checks are no longer necessary with Cloud Functions database triggers! Starting with the firebase-functions module version 0.5.9, there are now three new types of database triggers you can write: onCreate(), onUpdate(), and onDelete(). These triggers are aware of the type of change that was made, and only run in response to the type of change you desire. So, now you can write the above function like this:
onCreate()
onUpdate()
onDelete()
exports.sendNotification = functions.database .ref("/messages/{messageId}").onCreate(event => { // Send notifications with FCM... })
Here, you don't have to worry about this function getting triggered again for any subsequent updates to the data at the same location. Not only is this easier to read, it costs you less to operate.
Note that onWrite() hasn't gone away. You can still keep using it with your functions for as long as you like.
If you've previously used onWrite() to add or change data at a location, you're aware that the changes you made to that data during onWrite() would trigger a second invocation of onWrite() (because all writes count as writes, am I right?).
Consider this function that updates the lastUpdated property of a message after it's written:
exports.lastUpdate = functions.database .ref("/messages/{messageId}").onWrite(event => { const msg = event.data.val() msg.lastUpdated = new Date().getTime() return event.data.adminRef.set(msg) })
This seems OK at first, but there's something very important missing. Since this function writes back to the same location where it was triggered, it will effectively trigger another call to onWrite(). You can see here that this will cause an infinite loop of writes, so it needs some way to bail out of the second invocation.
It turns out that onUpdate() function implementations share this concern. One solution in this specific case could be to check the existing value of lastUpdated and bail out if it's not more than 30 seconds older than the current date. So, if we want to rewrite this function using onUpdate(), it could look like this:
exports.lastUpdate = functions.database .ref("/messages/{messageId}").onUpdate(event => { const msg = event.data.val() const now = new Date().getTime() if (msg.lastUpdated > now - (30*1000)) { return } msg.lastUpdated = now return event.data.adminRef.set(msg) })
This function now defends against infinite loops with a little extra logic.
To start using these new triggers, be sure to update your firebase-functions module to 0.5.9. And for more information about these updates, check out the documentation right here.
Building a great game is tough. You have to make it easy to pick up, challenging, a little bit addicting, and, of course, fun. Not to mention the technical effort involved in designing all the infrastructure that will power your blockbuster game. Our goal with Firebase is to take care of that last piece, so that you can focus on all of the elements that make your game unique.
At Google I/O this year, we decided to put our platform to the test and build a game with Firebase. The result was AppShip3000 and we're pretty happy with how it turned out.
AppShip3000 is a 3 person, collaborative, rocketship-flying, trivia game. The players work together to control a rocket ship that is flying upwards and quickly running out of fuel. Asteroids rain down from the top of the screen, while the crew answers Firebase and Google Cloud trivia to reach higher heights.
The catch is that players can only see either the question or one of the multiple choice answers, never both. They have to talk (or yell) to each other to figure out which player has the right answer before time runs out. Similarly, one player sees asteroids before the others and has to shout out where they'll be, so the entire team can move the rocket. All players have to move the joystick in that same direction to successfully avoid losing fuel from an asteroid collision.
Now, let's take a look at how Firebase powers AppShip3000. First off, we use Firebase Authentication for the sign-in experience. A player opens a progressive web app (PWA) on her phone and logs in using her Google account. Creation of an account is an Authentication Trigger for a Cloud Function that generates a custom button sequence (or launch code). This launch code is used to associate the account with the game controller for the duration of the game, so that when the game finishes, all stats are automatically associated with her account.
When a game finishes, Realtime Database is used to immediately update the leaderboard in the PWA. At I/O, we also had giant screens reflecting the leaderboards. Realtime Database is also used to manage the trivia questions in the game. This allows for on-the-fly updates to questions that can be written after one game and pushed to production in time for the next game.
We also generate an image of the rocket's flight path after each game and store it in Cloud Storage for Firebase. This action triggers another Cloud Function that sends a notification to the player, using Firebase Cloud Messaging. The notification lets the player know about the souvenir image and prompts her to share it with friends.
We had a blast building AppShip3000. Using Firebase to handle most of the infrastructure allowed us to focus on building a unique game that (we think) turned out to be a lot of fun to play. If you're interested in using Firebase in your own game, check our our C++ and Unity documentation. And to learn more about how Firebase and Google Cloud were used to manage the data pipeline of AppShip3000, check out our I/O talk. Can't wait to see what you build!
Last April, we announced the general availability of the Firebase Admin SDK for Python. The initial release of this SDK supported two important features related to Firebase Authentication: minting custom tokens, and verifying ID tokens. Now, we are excited to announce that database support is available in the Firebase Admin SDK for Python starting from version 2.1.0.
Due to the way it's implemented, there are several notable differences between this API and the database APIs found in our other Admin SDKs (Node.js and Java). The most prominent of these differences is the lack of support for realtime event listeners. The Python Admin SDK currently does not provide a way to add event listeners to a database reference in order to receive realtime update notifications. Instead, all data retrieval operations are provided as blocking methods. However, despite these differences there's a lot that can be achieved using this API.
The database module of the Python Admin SDK facilitates both basic data manipulation operations and advanced queries. To begin interacting with the database from a Python environment, initialize the SDK with the Realtime Database URL:
import firebase_admin from firebase_admin import credentials cred = credentials.Cert('path/to/serviceKey.json') firebase_admin.initialize_app(cred, { 'databaseURL' : 'https://my-db.firebaseio.com' })
Then obtain a database reference from the db module of the SDK. Database references expose common database operations as Python methods (get(), set(), push(), update() and delete()):
db
get(), set(), push(), update()
delete()
from firebase_admin import db root = db.reference() # Add a new user under /users. new_user = root.child('users').push({ 'name' : 'Mary Anning', 'since' : 1700 }) # Update a child attribute of the new user. new_user.update({'since' : 1799}) # Obtain a new reference to the user, and retrieve child data. # Result will be made available as a Python dict. mary = db.reference('users/{0}'.format(new_user.key)).get() print 'Name:', mary['name'] print 'Since:', mary['since']
In the Firebase Realtime Database, all data values are stored as JSON. Note how the Python Admin SDK seamlessly converts between JSON and Python's native data types.
To execute an advanced query, call one of the order_by_* methods available on the database reference. This returns a query object, which can be used to specify additional parameters. You can use this API to execute limit queries and range queries against your data, and retrieve sorted results.
order_by_*
from firebase_admin import db dinos = db.reference('dinosaurs') # Retrieve the five tallest dinosaurs in the database sorted by height. # 'result' will be a sorted data structure (list or OrderedDict). result = dinos.order_by_child('height').limit_to_last(5).get() # Retrieve the 5 shortest dinosaurs that are taller than 2m. result = dinos.order_by_child('height').start_at(2).limit_to_first(5).get() # Retrieve the score entries whose values are between 50 and 60. result = db.reference('scores').order_by_value() \ .start_at(50).end_at(60).get()
Take a look at the Admin SDK documentation for more information about this new API. Also check out our Github repo, and help us further improve the Admin SDK by reporting issues and contributing patches. In fact, it was your continuing feedback that motivated us to build and release this API in such a short period. Happy coding with Firebase!
Testing your application is a great way to help maximize its quality, and many of you know that Firebase Test Lab for Android has some useful tools for testing Android apps. If you're the type of engineer who likes to maximize test coverage by writing instrumented tests (and regular unit tests), you can send those to Test Lab for execution. Even if you don't like writing tests, you have some options. You can record instrumented tests by interacting with your app using Espresso Test Recorder in Android Studio. And there's almost no effort required at all to run a Robo test that automatically crawls your app.
These tests are helpful for data-driven apps because there are robust test frameworks that understand how to navigate the Android platform widgets used to receive input and display data on screen. However, most games don't work with platform widgets. Games typically take over the screen using their own UI elements, and provide their own touch controls. As a result, it's extremely difficult to write instrumented tests for testing games, and Robo test won't know how to navigate the game at all.
To help deal with these challenges with testing games, the Test Lab team has come up with a way for game developers to test their games effectively across many of the devices that Test Lab offers. It's a new type of test called a Game Loop Test, and it's available today in beta.
If you've seen arcade video games operate, you know that they're always showing something on screen, typically some automated demo of the game, called "attract mode". With Firebase Test Lab, game developers can now use this concept of attract mode to construct test scenarios, and Test Lab will arrange for those scenarios to be invoked in sequence. This gives developers the opportunity to test a wide variety of game levels and situations in a single test run on all the devices provided by Test Lab. If there are any problems with a scenario, you'll find out which ones are problematic, so you can focus your efforts on improving that specific case.
Even better, Test Lab now provides performance data with every report. Of particular interest to game developers is a graph of the rendered frame rate over time throughout the test, tied to a video of the device display. This helps you quickly identify the parts of your game that aren't rendering at an acceptable FPS.
In addition to FPS, there are other performance metrics available for all apps. You'll be able to see your app's CPU utilization, memory usage, and network ingress and egress. Notice how you can jump directly to the point in the video where you're observing performance problems.
If you're a game developer, now is a great time to check out Firebase Test Lab for Android to see what it can do to help the quality of your game.
We've all been in this situation before: you work tirelessly to build a great app, you spend countless hours beta testing and fixing bugs, and you even put together a comprehensive marketing plan in time for your app's launch. Months of anticipation finally culminate when you submit your app to the app store… but instead of celebrating the new release, you find out that your app didn't meet the right criteria for listing so it got rejected 😱.
There's nothing quite as deflating as tripping at the finish line.
App store guidelines can be complicated and hard to remember because they're constantly changing, but developers still need to follow them in order to get their apps approved. Sometimes, these guidelines get overlooked or lost in the shuffle of all the other important pre-launch tasks. To help you avoid unnecessary delays and rejection, we're excited to introduce fastlane precheck for iOS apps! You can count on us to remember the rules so you can submit your app with confidence.
Often, apps get rejected from app stores because of simple, solvable issues with their metadata (such as title, description, changelog, keywords, etc.). Once rejected, you have to resubmit your app and it can take days before it's reviewed again. This wastes time and can throw off your schedule.
Instead of risking rejection or painstakingly auditing your own metadata, you can now use fastlane precheck to save you the time and hassle. fastlane will review your app for the most common causes of rejection 👍.
For example, we'll check your metadata to ensure it follows iOS app store rules such as:
If we detect any issues, we'll flag them for you so you can resolve them before you submit your app to the app store. And since fastlane can automatically download your app's metadata, this whole process takes only seconds!
fastlane precheck is the first new tool we've added to our app automation toolkit since joining Google - and we have even more exciting updates planned! Now that we've settled into our new home, we can't wait to move fastlane forward with our passionate community of iOS and Android developers.
In the meantime, install fastlane or update your version of fastlane (just run `fastlane update_fastlane`) to try precheck and submit your app with confidence and peace of mind 🚀.