The Firebase Test Lab team is pleased to announce that developers now have the ability to write Cloud Functions triggers that receive test results from Firebase Test Lab. Previously, developers had to manually check for test results, or write code to periodically poll the Test Lab API for test results. With Cloud Functions triggers, it becomes much easier to know immediately when a test finishes and get a summary of its results.
Firebase Test Lab enables you to run scripted and automated tests against your app on a wide variety of Android and iOS devices hosted in a Google data center. Cloud Functions now extends the capabilities of Test Lab by providing a fully managed backend to let developers write and deploy code that triggers when a test completes. Test Lab triggers written for deployment to Cloud Functions take the following form when using the Firebase SDK for JavaScript and deployed with the Firebase CLI:
exports.matrixOnComplete = functions.testLab.testMatrix().onComplete(testMatrix => { const matrixId = testMatrix.testMatrixId; switch (testMatrix.state) { case 'FINISHED': // Test finished with results // Check testMatrix.outcomeSummary for pass/fail break; case 'ERROR': // Test completed with an infrastructure error break; // check other possible status codes... } return null; });
You can use these triggers to programmatically notify your team of test results, for example, sending an email, posting a message to a Slack workspace, creating an issue in JIRA, as well as integrating with other team workflow tools.
Test Lab support appears in version 3.2.0 of the firebase-functions node module. Be sure to read the documentation to get more details for Test Lab triggers, and use the API reference to discover all the information available in the test matrix results. There is also a quickstart and sample code available in GitHub to help you get started. For discussion, join the Test Lab engineering team in the #test-lab channel on the Firebase Slack.
how to deal with asynchronous logic in Unity
The Firebase Unity SDK makes judicious use of asynchronous logic for many of its calls. Unity itself isn’t super resilient to threaded logic, with most of the classes and functions in the UnityEngine namespace just flat out throwing exceptions if invoked off of the main Unity thread. My goal with this post is to provide you the tools you need to not only safely use Firebase’s asynchronous function calls, but to do so in a way that best suits your own programming style and preferences. Ideally even giving you more confidence to thread other parts of your game to provide your players with the smooth and responsive gameplay they expect from a modern video game.
UnityEngine
Let’s get started with a very innocent looking demo script:
using Firebase; using Firebase.Auth; using UnityEngine; using UnityEngine.Assertions; public class FirebaseContinueWith : MonoBehaviour { // Start is called before the first frame update void Start() { Debug.Log("Checking Dependencies"); FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(fixTask => { Assert.IsNull(fixTask.Exception); Debug.Log("Authenticating"); var auth = FirebaseAuth.DefaultInstance; auth.SignInAnonymouslyAsync().ContinueWith(authTask => { Assert.IsNull(authTask.Exception); Debug.Log("Signed in!"); var successes = PlayerPrefs.GetInt("Successes", 0); PlayerPrefs.SetInt("Successes", ++successes); Debug.Log($"Successes: {successes}"); auth.SignOut(); Debug.Log("Signed Out"); }); }); } }
The first thing I do is ensure that Firebase’s dependencies are available on the player’s device with CheckAndFixDependenciesAsync. Note that I’m not really handling any failure cases in this example. This shouldn’t be an issue for this post, but you’ll want to do more than assert in your own games.
CheckAndFixDependenciesAsync
Next I use ContinueWith to create a continuation and I start signing in anonymously with SignInAnonymouslyAsync.
ContinueWith
SignInAnonymouslyAsync
When sign-in completes, I figure out how many times this script has run successfully before by reading PlayerPrefs. Then I increment this value, and write it back out before logging the new number of successes.
This is all super straightforward. I run it and… I just see the log “Signed In!” then nothing. What happened?
Firebase does a lot of work that’s dependent on I/O. This can either be out to disk, or even out to the network. Since you don’t want your game to lock up for potentially many seconds for network latency, Firebase uses Tasks to perform much of this I/O work in the background.
Whenever you continue from this work, you have to be careful to come back into your game in a graceful manner. I’ve done none of that here, and have just charged right into a shared resource managed by the UnityEngine in the form of a call to PlayerPrefs. This most likely raised an exception, but it even got lost in the background thread! What can you do to fix it?
PlayerPrefs
C# has the concept of a TaskScheduler. When you say ContinueWith, rather than just letting it continue on whatever thread the task completed on, you can use a TaskScheduler to force it onto a specific thread. So, I can modify the example to cache the TaskScheduler on which Start() was called. Then I pass that into the ContinueWith statement to be able to safely change the state of objects in my game:
TaskScheduler
Start()
Debug.Log("Checking Dependencies"); var taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(fixTask => { Assert.IsNull(fixTask.Exception); Debug.Log("Authenticating"); var auth = FirebaseAuth.DefaultInstance; auth.SignInAnonymouslyAsync().ContinueWith(authTask => { Assert.IsNull(authTask.Exception); Debug.Log("Signed in!"); var successes = PlayerPrefs.GetInt("Successes", 0); PlayerPrefs.SetInt("Successes", ++successes); Debug.Log($"Successes: {successes}"); auth.SignOut(); Debug.Log("Signed Out"); }, taskScheduler); });
Since Start executes on the Unity main thread, I grab the scheduler with TaskScheduler.FromCurrentSynchronizationContext(). This way I can get back to the main thread later by passing the scheduler into my second ContinueWith statement. Now whatever work I do in that ContinueWith block will be done in sequence with the game rather than in parallel with it, preventing any threading issues.
TaskScheduler.FromCurrentSynchronizationContext()
When I run the script, I can see that I finally have one success (and that this script hasn’t succeeded before).
This pattern is really common so Firebase provides an extension method named ContinueWithOnMainThread that does all of that hard work for you. If you’re using a newer version of the Firebase Unity SDK, you can write the above as simply:
ContinueWithOnMainThread
Debug.Log("Checking Dependencies"); FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(fixTask => { Assert.IsNull(fixTask.Exception); Debug.Log("Authenticating"); var auth = FirebaseAuth.DefaultInstance; auth.SignInAnonymouslyAsync().ContinueWithOnMainThread(authTask => { Assert.IsNull(authTask.Exception); Debug.Log("Signed in!"); var successes = PlayerPrefs.GetInt("Successes", 0); PlayerPrefs.SetInt("Successes", ++successes); Debug.Log($"Successes: {successes}"); auth.SignOut(); Debug.Log("Signed Out"); }); });
A word of caution as well. In C#, you can safely assume that anything in your ContinueWith block hasn’t been garbage collected. The same doesn’t hold true with Unity’s design. If you were to access any fields of this MonoBehaviour or its encompassing GameObject after OnDestroy is invoked, you would want to check that this hasn’t become null. Due to the way Unity implemented this as well, you cannot do so with the ?? operator.
MonoBehaviour
GameObject
OnDestroy
this
??
Hopefully I’ve shed a little light on what’s happening in these tasks and continuations in Unity. You may also be a little frustrated now. What should be a simple block of code where we fix dependencies, sign on, then do work has become this ugly mess of nested statements that just becomes harder to read as we chain more steps into the logic. If only there were a better way!
Although the goal of tasks is to perform operations in parallel, so much logic in programming is sequential. Since continuations get hard to read, C# provides a mechanism in async/await syntax to represent this sequential logic. To use this mechanism, I’ll rewrite the Start method like this:
Start
async void Start() { Debug.Log("Checking Dependencies"); await FirebaseApp.CheckAndFixDependenciesAsync(); Debug.Log("Authenticating"); var auth = FirebaseAuth.DefaultInstance; await auth.SignInAnonymouslyAsync(); Debug.Log("Signed in!"); var successes = PlayerPrefs.GetInt("Successes", 0); PlayerPrefs.SetInt("Successes", ++successes); Debug.Log($"Successes: {successes}"); auth.SignOut(); Debug.Log("Signed Out"); }
The first thing you’ll notice is that I denote Start as async. This tells the C# compiler “this function will perform work in the background. Do something else whilst it finishes up.”
Then, I replace ContinueWith with the await keyword. If I were doing anything with the Task’s result, I could store the result in a variable.
await
Task
This reads much better, but why doesn’t my code break like the very first sample? It turns out that async functions will always return to the thread they’re awaited on. This way you don’t have to be as careful about thread safety in functions where you do this. In fact, by default, async functions that are awaited will typically execute on the thread that called them unless the developer explicitly did something else.
async
There is one downside compared to the ContinueWith sample though: the code following CheckAndFixDependenciesAsync will execute on the Unity main thread rather than potentially running on a background thread. In practice, this won’t be much of an issue. It could be a behaviour of note if you’re doing some significant amount of work between calls to await. Be aware as well that this code is very similar to the continuation example above. Just like how Unity may clean up your underlying MonoBehaviour before ContinueWith executes, Unity may clean it up when the call to await completes. If you access any member fields after a call to await, you should check to ensure this is not yet null.
Unity has the concept of coroutines, which used to be the preferred method of performing asynchronous work across multiple frames. The interesting bit about Coroutines is that they’re not really asynchronous, behind the scenes they simply generate IEnumerators which are evaluated on the main thread.
Unity has some special yield instructions such as WaitForEndOfFrame and WaitForSeconds, allowing you to jump around to different moments in your game’s time. I choose to implement a new CustomYieldInstruction to wait for a task to complete. I’ve even seen some developers convert something like this into an extension method on the Task class itself!
using System.Threading.Tasks; using UnityEngine; public class YieldTask : CustomYieldInstruction { public YieldTask(Task task) { Task = task; } public override bool keepWaiting => !Task.IsCompleted; public Task Task { get; } }
I can now use yield return on a Task, such as the Tasks typically used for Firebase, to make my asynchronous logic read sequentially. Inside it, I wait for a task to complete with a standard continuation. If I were actually doing something with the result of a Task, I’d have to build this class out a little more. For the time being, this will work to illustrate my basic point.
yield return
I can then reimplement my async/await logic using coroutines like this:
using System.Collections; using System.Collections.Generic; using Firebase; using Firebase.Auth; using UnityEngine; public class FirebaseCoroutine : MonoBehaviour { void Start() { StartCoroutine(DoWork()); } private IEnumerator DoWork() { Debug.Log("Checking Dependencies"); yield return new YieldTask(FirebaseApp.CheckAndFixDependenciesAsync()); Debug.Log("Authenticating"); var auth = FirebaseAuth.DefaultInstance; yield return new YieldTask(auth.SignInAnonymouslyAsync()); Debug.Log("Signed in!"); var successes = PlayerPrefs.GetInt("Successes", 0); PlayerPrefs.SetInt("Successes", ++successes); Debug.Log($"Successes: {successes}"); auth.SignOut(); Debug.Log("Signed Out"); } }
My Start function now just immediately calls a function called DoWork, which is my coroutine.
DoWork
Since I’m not doing anything with the return value of the async function calls, I just allocate temporary YieldTask objects and return them in the DoWork coroutine.
YieldTask
Now there are some important pros and cons to consider if you use this type of logic. This will have a performance hit as not only does the work between each yield return call execute on the main thread, but the property keepWaiting is queried every frame. On the other hand coroutines only exist for as long as a MonoBehaviour hasn’t been destroyed. This means that those caveats I mentioned above with having to check for null after an await or inside a ContinueWith don’t apply to coroutines!
keepWaiting
Sometimes the performance characteristics of coroutines don’t match up exactly to what you want. Remembering that a CustomYieldInstruction is queried every frame, you may end up in a state where Unity is performing many checks against against the keepWaiting property. In this case, it may be beneficial to queue these actions on Unity thread manually by adding work to a queue when it’s ready to be processed. Note that this is effectively how ContinueWithOnMainThread works and you should use that method when possible.
With that in mind, let’s look at an example of how I’ve implemented an action queue:
using System; using System.Collections.Generic; using System.Linq; using Firebase; using Firebase.Auth; using UnityEngine; using UnityEngine.Assertions; public class FirebaseQueue : MonoBehaviour { private Queue<Action> _actionQueue = new Queue<Action>(); void Start() { Debug.Log("Checking Dependencies"); FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(fixTask => { Assert.IsNull(fixTask.Exception); Debug.Log("Authenticating"); var auth = FirebaseAuth.DefaultInstance; auth.SignInAnonymouslyAsync().ContinueWith(authTask => { EnqueueAction(() => { Assert.IsNull(authTask.Exception); Debug.Log("Signed in!"); var successes = PlayerPrefs.GetInt("Successes", 0); PlayerPrefs.SetInt("Successes", ++successes); Debug.Log($"Successes: {successes}"); auth.SignOut(); Debug.Log("Signed Out"); }); }); }); } public void EnqueueAction(Action action) { lock (_actionQueue) { _actionQueue.Enqueue(action); } } void Update() { while (_actionQueue.Any()) { Action action; lock (_actionQueue) { action = _actionQueue.Dequeue(); } action(); } } }
This starts much like all of my continuation based examples. Unlike those, I call EnqueueAction to perform work on the main thread. I would highly recommend breaking this into two different MonBehaviours so you don’t forget to call EnqueueAction, but I’m compressing this for illustrative reasons.
EnqueueAction
MonBehaviour
The EnqueueAction function locks the Queue and adds some nugget of logic in the form of a C# Action into a list of logic to be executed later. If you get really clever, you may be able to just replace all of this with a lockless thread safe queue.
Finally, in Update, I execute every enqueued Action. It is very important to NOT execute the action with the _actionQueue locked. If the Action itself enqueues another Action, you’ll end up in a deadlock.
Update
Action
_actionQueue
Similar to the coroutine, this does involve checking whether or not the queue is empty every frame. Using what you’ve learned above about task schedulers and coroutines, I’m confident that you could reduce this burden with little effort if this becomes an issue.
Finally, there is a hip concept running around many programming circles known as reactive programming. Game developers I talk to tend to either love this or hate it, and there is enough public discourse that I won’t spend this post trying to turn you for or against this paradigm.
Reactive programming tends to favor logic that can come in streams -- that is logic that you would typically register for an event or query something every frame for -- and where you’ll perform functional operations on the streams as they flow through your game. For the purpose of staying consistent with the rest of this post, I’ll use it with the current example with the note that I’m not giving reactive programming its chance to shine.
So, with all that said, first I import UniRx from the Unity Asset Store. Then I have to make sure that there’s a MainThreadDispatcher in my scene:
MainThreadDispatcher
Now I can write my logic in UniRx form:
using Firebase; using Firebase.Auth; using UniRx; using UnityEngine; public class FirebaseRx : MonoBehaviour { // Start is called before the first frame update void Start() { Debug.Log("Checking Dependencies"); FirebaseApp.CheckAndFixDependenciesAsync().ToObservable().Subscribe(status => { Debug.Log("Authenticating"); var auth = FirebaseAuth.DefaultInstance; auth.SignInAnonymouslyAsync().ToObservable().ObserveOnMainThread().Subscribe(user => { Debug.Log("Signed in!"); var successes = PlayerPrefs.GetInt("Successes", 0); PlayerPrefs.SetInt("Successes", ++successes); Debug.Log($"Successes: {successes}"); auth.SignOut(); Debug.Log("Signed Out"); }); }); } }
What’s interesting about UniRx is that I can compose behaviour to form complex interactions. A brief example of this is the call ObserveOnMainThread, which guarantees that the following Subscribe executes on the main thread.
ObserveOnMainThread
Subscribe
For an example like this one, I would not pull in the complexities of UniRx but it’s useful to put it on your radar. If you were instead trying to build game logic around realtime database updates or periodically invoking cloud functions based on streams of events in game, you could do worse than combining UniRx and Zenject to quickly build a robust system around asynchronous logic.
I hope that I’ve not only given you some tools to help understand Firebase’s asynchronous API, but have empowered you to deal with them in a way that best suits your own game and coding style. I would strongly encourage you to create a small project using each of the techniques I’ve outlined here to really get a feel for the shape of each solution, and encourage you to think about ways you might improve your game’s performance using threads elsewhere. I’ve personally gotten some great mileage out of background tasks when dealing with peer to peer communication in games as well as processing and annotating screenshots without halting gameplay. I’ve even found that sometimes things like enemy AI doesn’t actually need to finish processing every frame, and it can sometimes be perfectly fine to let it run for a bit in the background over the course of a few frames.
Hey, there Firebase developers. Did you hear the exciting news? Last month at Google I/O, we announced support for collection group queries in Cloud Firestore! Let's dig into this new feature a little more, and see if we answer some of your burning questions…
Q: So, what are collection group queries and why should I care?
In Cloud Firestore, your data is divided up into documents and collections. Documents often point to subcollections that contain other documents, like in this example, where each restaurant document contains a subcollection with all the reviews of that restaurant.
In the past, you could query for documents within a single collection. But querying for documents across multiple collections wasn't possible. So, for instance, I could search for all reviews for Tony's Tacos, sorted by score, because those are in a single subcollection.
But if I wanted to find reviews for all restaurants where I was the author, that wasn't possible before because that query would span multiple reviews collections.
reviews
But with collection group queries, you're now able to query for documents across a collection group; that is, several collections that all have the same name. So I can now search for all the reviews I've written, even if they're in different collections.
Q: Great! So how do I use them?
The most important step in using a collection group query is enabling the index that allows you to run a query in the first place. Continuing our example, if we want to find all reviews that a particular person has written, we would tell Cloud Firestore, "Go index every author field in every single reviews collection as if it were one giant collection."
author
You can do this manually by going to the Firebase Console, selecting the "Index" tab for Cloud Firestore, going to the "Single Field" section, clicking the "Add exemption" button, specifying you want to create an exemption for the "reviews" collection with the "author" field and a "collection group" scope, and then enabling ascending and/or descending indexes.
But that's a lot of steps, and I tend to be pretty lazy. So, instead, I like enabling collection group indexes the same way I enable composite indexes. First, I'll write the code for the collection group query I want to use and attempt to run it. For example, here's some sample code I might write to search for all reviews where I'm the author.
var myUserId = firebase.auth().currentUser.uid; var myReviews = firebase.firestore().collectionGroup('reviews') .where('author', '==', myUserId); myReviews.get().then(function (querySnapshot) { // Do something with these reviews! })
Notice that I'm specifying a collectionGroup() for my query instead of a collection or document.
collectionGroup()
When I run this code, the client SDK will give me an error message, because the collection group index hasn't been created yet. But along with this error message is a URL I can follow to fix it.
Following that URL will take me directly to the console, with my collection group index ready to be created.
Once that index has been created, I can go ahead and re-run my query, and it will find all reviews where I'm the author.
If I wanted to search by another field (like rating), I would need to create a separate index with the rating field path instead of the author field.
rating
Q: Any gotchas I need to watch out for?
Why, yes! There are three things you should watch out for.
First, remember that collection group queries search across all collections with the same name (e.g., `reviews`), no matter where they appear in my database. If, for instance, I decided to expand into the food delivery service and let users write reviews for my couriers, then suddenly my collection group query would return reviews both for restaurants and for couriers in the same query.
This is (probably) not what I want, so the best thing to do would be to make sure that collections have different names if they contain different objects. For example, I would probably want to rename my courier review collections something like courier_reviews.
courier_reviews.
If it's too late to do that, the second best thing would be to add something like an isCourier Boolean field to each document and then limit your queries based on that.
isCourier
Second, you need to set up special security rules to support queries. You might think in my example that if I had a security rule like this:
I would be able to run this collection group query. After all, all of my review documents would fall under this rule, right? So why does this fail?
Well if you've seen our video on Cloud Firestore security rules, you would know that when it comes to querying multiple documents, Cloud Firestore needs to prove that a query would be allowed by the security rules without actually examining the underlying data in your database.
And the issue with my collection group query is that there's no guarantee it will only return documents in the restaurants → reviews collection. Remember, I could just as easily have a couriers → reviews collection, or a restaurant → dishes → reviews collection. Cloud Firestore has no way of knowing unless it examines the results of the data set.
So the better way to do this is to declare that any path that ends with "reviews" can be readable based on whatever security rules I want to implement. Something like this:
Note that this solution requires using version 2 of the security rules, which changes the way recursive wildcards work.
Third, keep in mind that these collection group indexes are counted against the 200 index exemptions limit per database. So before you start creating collection group indexes willy-nilly, take a moment and ask yourself what queries you really want to run, and just create indexes for those. You can always add more later.
Q: Can I do collection group queries for multiple fields?
Yes. If you're doing equality searches across multiple fields, just make sure you have an index created for each field with a collection group scope.
If you're combining an equality clause with a greater-than-or-less-than clause, you'll need to create a composite index with a collection group scope. Again, I find it's best to just try to run the query in the code and follow the link to generate the index. For instance, trying to run a collection group query for all reviews that I wrote with a rating of 4 or higher gave me a URL that opened this dialog box.
Q: It still seems like I could do all of this in a top-level collection. How are collection group queries better?
So this question is based on the idea that one alternative to creating collection group queries is to not store data hierarchically at all, and just store documents in a separate top level collection.
For instance, I could simply keep my restaurants and my reviews as two different top-level collections, instead of storing them hierarchically.
With this setup, I can still search for all reviews belonging to a particular restaurant…
As well as all reviews belonging to a particular author…
And you'll notice that with the separate top level collection, I no longer need to use one of my 200 custom indexes to create this query.
So, why go with the subcollection setup? Are collection group queries needed at all? Well, one big advantage to putting documents into subcollections is that if I expect that I'll want to order restaurant reviews by rating, or publish date, or most upvotes, I can do that within a reviews subcollection without needing a composite index. In the larger top level collection, I'd need to create a separate composite index for each one of those, and I also have a limit of 200 composite indexes.
Also, from a security rules standpoint, it's fairly common to restrict child documents based on some data that exists in their parent, and that's significantly easier to do when you have data set up in subcollections.
So when should you store things in a separate top level collection vs. using subcollections? If you think you have a situation where you're mostly going to be querying documents based on a common "parent" and only occasionally want to perform queries across all collections, go with a subcollection setup and enable collection group queries when appropriate. On the other hand, if it seems like no matter how you divide up your documents, the majority of your queries are going to require a collection group query, maybe keep them as a top level collection.
But if that's too hard to figure out, I would say that you should pick the solution that makes sense to you intuitively when you first think about your data. That tends to be the correct answer most of the time.
Hope that helps you get more comfortable with collection group queries! As always, if you have questions, feel free to check out our documentation, or post questions on Stack Overflow.
This week, we’re returning to Google I/O for the 4th year in a row to share how we’re making Firebase better for all app developers, from the smallest one-person startup to the largest enterprise businesses. No matter how many times we take the stage, our mission remains the same: to help mobile and web developers succeed by making it easier to build, improve, and grow your apps. Since launching Firebase as Google’s mobile development platform at I/O 2016, we’ve been continuously amazed at what you’ve built with our tools. It is an honor to help you on your journey to change the world!
For example, in Uganda, a start-up called Teheca is using Firebase to reduce the mortality rate of infants and new mothers by connecting parents with nurses for post-natal care. Over in India where smartphones are quickly replacing TVs as the primary entertainment source, Hotstar, India’s largest video streaming app, is using Firebase with BigQuery to transform the viewing experience by making it more social and interactive. Here’s how they’re doing it, in their own words:
Stories like these inspire us to keep making Firebase better. In fact, we’ve released over 100 new features and improvements over the last 6 months! Read on to learn about our biggest announcements at Google I/O 2019.
New translation, object detection and tracking, and AutoML capabilities in ML Kit
Last year, we launched ML Kit, bringing Google's machine learning expertise to mobile developers in a powerful, yet easy-to-use package. It came with a set of ready-to-use on-device and cloud-based APIs with support for custom models, so you could apply the power of machine learning to your app, regardless of your familiarity with ML. Over the past few months, we’ve expanded on these by adding solutions for Natural Language Processing, such as Language Identification and Smart Reply APIs. Now, we’re launching three more capabilities in beta: On-device Translation API, Object Detection & Tracking API, and AutoML Vision Edge.
The On-device Translation API allows you to use the same offline models that support Google Translate to provide fast, dynamic translation of text in your app into 58 languages. The Object Detection & Tracking API lets your app locate and track, in real-time, the most prominent object in a live camera feed. With AutoML Vision Edge, you can easily create custom image classification models tailored to your needs. For example, you may want your app to be able to identify different types of food, or distinguish between species of animals. Whatever your need, just upload your training data to the Firebase console and you can use Google’s AutoML technology to build a custom TensorFlow Lite model for you to run locally on your user's device. And if you find that collecting training datasets is hard, you can use our open source app which makes the process simpler and more collaborative.
Customers like IKEA, Fishbrain, and Lose It! are already using ML Kit’s capabilities to enhance their app experiences. Here’s what they had to say:
"We’re working with Google Cloud to create a new mobile experience that enables customers, wherever they are, to take photos of home furnishing and household items and quickly find that product or similar in our online catalogue. The Cloud Vision Product Search API provided IKEA a fast and easy way to index our catalogue, while ML Kit’s Object Detection and Tracking API let us seamlessly implement the feature on a live viewfinder on our app. Google Cloud helps us make use of Vision Product Search and we are very excited to explore how this can help us create a better and more convenient experience for our customers.” - Susan Standiford, Chief Technology Officer of Ingka Group, a strategic partner in the IKEA franchise system and operating IKEA in 30 markets.
“Our users are passionate about fishing, so capturing and having access to images of catches and species information is central to their experience. Through AutoML Vision Edge, we’ve increased the number of catches logged with species information by 30%, and increased our species recognition model accuracy from 78% to 88%..”- Dimitris Lachanas, Android Engineering Manager at Fishbrain
“Through AutoML Vision Edge, we were able to create a highly predictive, on-device model from scratch. With this improvement to our state-of-the-art food recognition algorithm, Snap It, we’ve increased the number of food categories our customers can classify in images by 21% while reducing our error rate by 36%, which is huge for our customers.” - Will Lowe Ph.D., Director of Data Science & AI, Lose It!
Performance Monitoring now supports web apps
Native mobile developers have loved using Firebase Performance Monitoring to find out what parts of their app are running slower than they expect, and for which app users. Today, we’re excited to announce that Performance Monitoring is available for web apps too, in beta, so web developers can understand how real users are experiencing their app in the wild.
By pasting a few lines of code to their site, the Performance Monitoring dashboard will track and visualize high level web metrics (like page load and network stats) as well as more granular metrics (like time to first paint and first input delay) across user segments. The Performance Monitoring dashboard will also give you the ability to drill down into these different user segments by country, browser, and more. Now, you can get deep insight into the speed and performance of your web apps and fix issues fast to ensure your end users have a consistently great experience. By adding web support to one of our most popular tools, we’re reaffirming our commitment to make app development easier for both mobile and web developers.
Brand new audience builder in Google Analytics for Firebase
Google Analytics for Firebase provides free, unlimited, and robust analytics so you can measure the things that matter in your app and understand your users. A few weeks ago, we announced advanced filtering in Google Analytics for Firebase, which allows you to filter your Analytics event reports by any number of different user properties or audiences at the same time.
Today, we’re thrilled to share that we’ve completely rebuilt our audience system from scratch with a new interface. This new audience builder includes new features like sequences, scoping, time windows, membership duration, and more to enable you to create dynamic, precise, and fresh audiences for personalization (through Remote Config) or re-engagement (through Cloud Messaging and/or the new App campaigns).
For example, if you wanted to create a "Coupon users" audience based on people who redeem a coupon code within your app, and then complete an in-app purchase within 20 minutes, this is now possible with the new audience builder.
In addition to the three big announcements above, we’ve also made the following improvements to other parts of Firebase.
Support for collection group queries in Cloud Firestore
In January, we graduated Cloud Firestore - our fully-managed NoSQL database - out of beta into general availability with lower pricing tiers and new locations. Now, we’ve added support for Collection Group queries. This allows you to search for fields across all collections of the same name, no matter where they are in the database. For example, imagine you had a music app which stored its data like so:
This data structure makes it easy to query the songs by a given artist. But until today, it was impossible to query across artists — such as finding the longest songs regardless of who wrote them. With collection group queries, Cloud Firestore now can perform these searches across all song documents, even though they're in different collections. This means it’s easier to organize your data hierarchically, while still being able to search for the documents you want.
Cloud Functions emulator
We’ve also been steadily improving our tools and emulator suite to increase your productivity for local app development and testing. In particular, we’re releasing a brand new Cloud Functions emulator that can also communicate with the Cloud Firestore emulator. So if you want to build a function that triggers upon a Firestore document update and writes data back to the database you can code and test that entire flow locally on your laptop, for much faster development.
Configurable velocity alerts in Crashlytics
Firebase Crashlytics helps you track, prioritize, and solve stability issues that erode app quality, in real time. One of the most important alerts within Crashlytics is the velocity alert, which notifies you when an issue suddenly increases in severity and impacts a significant percentage of your users. However, we recognize that every app is unique and the one-size-fits-all alerting threshold might not be what’s best for you and your business. That’s why you can now customize velocity alerts and determine how often and when you want to be alerted about changes to your app’s stability. We’re also happy to announce that we’ve expanded Crashlytics to include Unity and NDK support.
Improvements to Test Lab
Firebase Test Lab makes it easy for you to test your app on real, physical devices, straight from your CLI or the Firebase console. Over the past few months, we’ve released a number of improvements to Test Lab. We’ve expanded the types of apps you can run tests on by adding support for Wear OS by Google and Android App Bundles. We’ve also added ML vision to Test Lab’s monkey action feature so we can more intelligently simulate where users will tap in your app or game. Lastly, we’ve made your tests more reliable with test partitioning, flaky test detection, and the robo action timeline, which tells you exactly what the crawler was doing while the test was running.
Greater control over Firebase project permissions
Security and data privacy remain part of our top priorities. We want to make sure you have control over who can access your Firebase projects, which is why we’ve leveraged Google Cloud Platform’s Identity & Access Management controls to give you finer grained permission controls. Right from the Firebase console, you can control who has access to which parts of your Firebase project. For example, you can grant access to a subset of tools so team members who run notification campaigns aren’t able to change your Firebase database’s security rules. You can go even further and use the GCP console to create custom roles permitting access to only the actions your team members are required to take.
More open-sourced SDKs
To make Firebase more usable and extensible, we’re continuing to open source our SDKs and accepting contributions from the community. We are committed to giving you transparency and flexibility with the code you integrate into your mobile and web apps. Most recently, we open sourced our C++ SDK.
In case you missed the news at Cloud Next 2019, here’s a quick recap of the updates we unveiled back in April:
In addition to making Firebase more powerful, we’ve also been hard at work bringing the best of Fabric into Firebase. We know many of you have been waiting for more information on this front, so we have outlined our journey in more detail here.
We’re continuing to invest in Firebase and as always, we welcome your feedback! With every improvement to Firebase, we aim to simplify your app development workflows and infrastructure needs, so you can stay focused on building amazing user experiences. To get a sneak peek at what’s next, join our Alpha program and help us shape the future
The Cloud Functions for Firebase team is pleased to provide a turnkey solution for secure, scheduled functions using Firebase tools and SDKs.
One of the most common questions about Google Cloud Functions is how to schedule a function for regular execution, similar to a cron job. In the past, the Firebase team recommended solutions involving App Engine or third party schedulers as a scheduling mechanism to invoke a function, which works fine. However, solutions that involve the invocation of an HTTP function aren’t fully secure, because the HTTP endpoint URL is effectively public and accessible to anyone who knows that URL. And, solutions that involve creating and sending messages to a Cloud Pub/Sub endpoint can be difficult to manage.
Recently, Google Cloud released Cloud Scheduler, which allows you to schedule HTTP requests or Cloud Pub/Sub messages to functions that you deploy. You can follow a tutorial for getting that set up in your project, if you like. But today, it gets even easier than that, and it doesn’t require working with the Google Cloud console.
Firebase now supports a new type of Pub/Sub function, built on top of Cloud Scheduler, that automatically configures Cloud Scheduler, along with a Pub/Sub topic, that invokes a function that you define using the Cloud Functions for Firebase SDK.
If you’re using version 2.3.0 of the firebase-functions NodeJS module, along with 6.7.0 of the Firebase CLI, you can now define functions that use Google Scheduler’s standard cron syntax like this:
export scheduledFunctionCrontab = functions.pubsub.schedule('5 11 * * *').onRun((context) => { console.log('This will be run every day at 11:05 AM UTC!'); });
You can schedule invocations using an English description as well:
export scheduledFunctionPlainEnglish = functions.pubsub.schedule('every 5 minutes').onRun((context) => { console.log('This will be run every 5 minutes!'); });
To get started with scheduled functions using Firebase tools, navigate to the documentation, and check out this sample available in GitHub. Your project must be on the Blaze payment plan, as Cloud Scheduler and Pub/Sub require billing information. Each Cloud Scheduler job costs $0.10 (USD) per month, and there is an allowance of three free jobs per Google account. Besides the normal costs for Cloud Functions, the costs for this feature are the same as if you set up the underlying infrastructure manually - for more information, check out the full pricing information for Cloud Scheduler and Cloud PubSub.