WorryFree Computers   »   [go: up one dir, main page]

Todd Kerpleman
Todd Kerpelman
Developer Advocate
Debugging Firebase Cloud Messaging is one of the most common Firebase-on-iOS questions I see on StackOverflow. And so in an effort to garner as many StackOverflow points as I can (oh, yeah, and to educate the developer community), I thought it might be helpful to write up a full debugging guide on what to do when you can't seem to get Firebase Cloud Messaging (FCM) working on your iOS device. First off, I'd recommend taking a moment to watch our Understanding FCM on iOS video. It'll give you a better idea of what's going on underneath the hood, which is always useful when you're trying to debug things. Go ahead! I'll wait.



Okay, back? So you probably noticed in the video that we have several systems all talking to each other:
  1. Your app server (or Firebase Notifications) talks to Firebase Cloud Messaging
  2. Firebase Cloud Messaging then talks to APNs
  3. APNs talks to your user's target device
  4. On your user's target device, iOS communicates with your app.


These four paths of communication means there are four opportunities for things to go wrong. And when they do, they very often manifest as a frustrating "It says my notification was sent, but nothing showed up on my device" kind of bug, which requires some real detective work. So here are the steps I recommend you go through to start tracking down these errors.

1. Temporarily disable any connectToFCM() calls

If you'll recall from the video, your app can explicitly connect to Firebase Cloud Messaging by calling connectToFCM() when it's in the foreground, which allows it to receive data-only messages that don't have a content-available flag directly through FCM.
And while this can be useful in certain circumstances, I recommend disabling this while you're debugging. Simply because it's one extra factor that you want to eliminate. I've seen a few, "My app receives notifications in the foreground, but not in the background" problems out there that probably occurred because the developer was only receiving messages through the FCM channel, and their APNs setup was never actually working properly .
If things break at this point: If you suddenly go from "Hey, I was receiving foreground notifications" to "I'm not receiving any notifications at all", then this is a sign your app was never properly set up to receive notifications from APNs in the first place. So your app might be a little more broken than before, but at least now it's consistently broken. (Hooray!) Keep reading to debug your APNs implementation!
For the next several steps, we're going to go backwards through that "Notifications to FCM to APNs to iOS to your app" chain. Let's start by making sure that iOS can actually speak to your app...

2. Add some good ol' fashioned print() debugging

Thanks to some clever method swizzling, Firebase Cloud Messaging makes it totally unnecessary for you to implement either application(_:didRegisterForRemoteNotificationsWithDeviceToken:) or application(_:didFailToRegisterForRemoteNotificationsWithError:) in your app delegate.
However, for debugging purposes, I like to add in these methods and print out some debug information to see if there are any errors happening that I should be aware of. Start by adding some debug output to your failure method. Something like this:
func application(_ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error) {
  print("Oh no! Failed to register for remote notifications with error \(error)")
}
In theory, any error message printed out here will also probably be printed out by the FCM client library, but I like having my own messages because I can search for specific strings (like "Oh no!" in the above example) among all the Xcode output. This also gives me a handy line of code where I can add a breakpoint.
While you're at it, in your didRegister... method, go ahead and print out a human-readable version of your device token:
func application(_ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  var readableToken: String = ""
  for i in 0..<deviceToken.count {
    readableToken += String(format: "%02.2hhx", deviceToken[i] as CVarArg)
  }
  print("Received an APNs device token: \(readableToken)")
}
You don't need to disable method swizzling or anything to add these debug methods. Firebase will go ahead and call them just as soon as it's done calling its own set of methods.
If you're seeing an error message at this point: If you either receive an error message or don't get back a device token, check the error message for a good hint as to what went wrong. Most mistakes at this point fall under the, "I'm embarrassed to tell anybody why it wasn't working" category. Things like:
  • Testing on the iOS simulator and not the device.
  • Forgetting to enable Push Notifications in your Xcode project settings.
  • Not calling application.registerForRemoteNotifications() when your app starts up.
Sure, these are simple mistakes, but without the benefit of printing out messages to the Xcode console, it's easy for them to go unnoticed.

3. Confirm that you can send user-visible notifications

As with any iOS app, you need to explicitly get the user's permission to show any kind of notification alert or sound. If you're in a situation where your app doesn't appear to be receiving notification messages in the background, your app simply might not have permission from iOS to do so.
You can check this in iOS >= 10 by adding the following code somewhere in your app.
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
  print("Alert setting is \(settings.alertSetting ==
    UNNotificationSetting.enabled ? "enabled" : "disabled")")
  print("Sound setting is \(settings.soundSetting ==
    UNNotificationSetting.enabled ? "enabled" : "disabled")")
}
If you're seeing "disabled" messages at this point: Either you accidentally denied granting your app permission to send you notifications, or you never asked for permission in the first place.
If you accidentally clicked on the "Don't allow" button when the app asked you for permission to send notifications, you can fix this by going to Settings, finding your app, clicking on Notifications, then clicking the Allow Notifications switch.

                                   

On the other hand, if you never asked for permission to show user-visible permissions, then it means you need to add code like this (for iOS >= 10) within your app somewhere:
let authOptions : UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions)
  { (granted, error) in
    if (error != nil) {
      print("I received the following error: \(error)")
    } else if (granted) {
      print ("Authorization was granted!")
    } else {
      print ("Authorization was not granted. :(")
    }
  }
But if everything looks good at this point, you can move on to debugging the APNs connection!

4. Make a call directly through APNs

Remember; just because you're using FCM to handle your notifications doesn't mean you can't also use APNs directly. There are a few ways you can try this; one option is to use an open-source tool like NWPusher to send test notifications. But personally, I prefer sending APNs requests directly through a curl call.
Making an APNs curl request is easier these days now that APNs supports HTTP/2. But it does mean you'll need to make sure your copy of curl is up-to-date. To find out, run curl --version. You'll probably see something like this:
curl 7.47.1 (x86_64-apple-darwin15.6.0) libcurl/7.47.1 OpenSSL/1.0.2f zlib/1.2.5 nghttp2/1.8.0
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets
If you want to talk to APNs, you'll need a version of curl that's greater than 7.43, and you'll need to see HTTP2 among the features. If your version of curl doesn't meet these requirements, you'll need to update it. This blog post by Simone Carletti gives you some pretty nice instructions on how to do that.
Next, you'll need to convert the .p12 file you downloaded from the Apple Developer Portal to a .pem file. You can do that with this command:
openssl pkcs12 -in MyApp_APNS_Certificate.p12 -out myapp-push-cert.pem -nodes -clcerts
You'll also need the APNs device token for your target device. If you added the debug text listed earlier in your application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method, you'll be able to grab this from your Xcode console. It should look something like ab8293ad24537c838539ba23457183bfed334193518edf258385266422013ac0d
Now you can make a curl call that looks something like this:
> curl --http2 --cert ./myapp-push-cert.pem \
-H "apns-topic: com.example.yourapp.bundleID" \
-d '{"aps":{"alert":"Hello from APNs!","sound":"default"}}' \
https://api.development.push.apple.com/3/device/ab8293ad24537c838539ba23457183bfed334193518edf258385266422013ac0d
Three things to notice here:
  1. That --cert argument should link to the .pem file you created in the previous step.
  2. For the apns-topic, include the bundle ID of your app. And yes, the concept of apns-topics are completely different than Firebase Cloud Messaging topics.
  3. Make sure your device ID is included at the end of that URL there. Don't just copy-and-paste the one that I have. It won't work.
If all has gone well, you'll see a push notification on the device, and you can move on to the next step. If not, here's a few things to look for:
  1. Are you getting back any error message from APNs? That's a pretty good sign something has gone wrong. Common messages include:
    1. "Bad device token" -- This is what it sounds like. Your device token is incorrect. Double-check that you've copied it correctly from your app
    2. "Device token not for topic" -- This might mean that your topic isn't properly set to your app's bundle ID. But it also might mean that you're not using the correct certificate here -- I've gotten this message when I've used the wrong .pem file.
  2. Is your app in the background? Remember that iOS will not automatically show notification alerts or sounds if your app is in the foreground.
    1. However, in iOS 10, they've made it significantly easier to have iOS show these alerts even if your app is in the foreground. You just need to call completionHandler([.alert]) at the end of userNotificationCenter(_:willPresent:withCompletionHandler:)
  3. Are you sending valid APNs requests? There are a few types of requests that, while syntactically correct, may still get rejected. At the time of this writing, these include sending silent notifications that don't include the content-available flag, or sending silent notifications high priority.
    1. In addition, iOS may throttle silent notifications if your app neglects to call its completionHandler in a reasonable amount of time upon receiving them or uses too much power to process these notifications. Refer to Apple's documentation for more information.
  4. Is APNs having issues? You can double-check the status of APNs and the APNs Sandbox over at https://developer.apple.com/system-status/
But if things seem to be working correctly here, it's time to move on to the next step...

5. Make a curl call directly through FCM

Once you've confirmed your APNs call seems to be working properly, the next step is to confirm the FCM part of the process is working. For that, I also like to make another curl call. For this to work, you're going to need two things: The server key and the FCM device token of your target device.
To get the server key, you'll need to go to the Cloud Messaging settings of your project in the Firebase Console. Your server key should be listed there as a giant 175-character string.



Getting your FCM device token is slightly more work. When your app first receives an APNs token, it will send that off to the FCM servers in exchange for an FCM device token. When it gets this FCM token back, the FCM library will trigger an "Instance ID Token Refresh" notification. 1
So listening to this firInstanceIDTokenRefresh NSNotification will let you know what your FCM device token is, but this notification only gets triggered when your device token changes. This happens infrequently -- like when you switch from a debug to production build, or when you run your app for the first time. Otherwise, this notification will not be called.
However, you can retrieve your cached FCM device token simply through the InstanceID library, which will give you any stored device token if it's available. So to get your latest-and-greatest FCM token, you'll want to write some code like this:
  func application(_ application: UIApplication, didFinishLaunchingWithOptions
    // ...
    printFCMToken() // This will be nil the first time, but it will give you a value on most subsequent runs
    NotificationCenter.default.addObserver(self, 
      selector: #selector(tokenRefreshNotification), 
      name: NSNotification.Name.firInstanceIDTokenRefresh, 
      object: nil)
    application.registerForRemoteNotifications()
    //...
  }

  func printFCMToken() {
    if let token = FIRInstanceID.instanceID().token() {
      print("Your FCM token is \(token)")
    } else {
      print("You don't yet have an FCM token.")
    }
  }

  func tokenRefreshNotification(_ notification: NSNotification?) {
    if let updatedToken = FIRInstanceID.instanceID().token() {
      printFCMToken()
      // Do other work here like sending the FCM token to your server
    } else {
      print("We don't have an FCM token yet")
    }
  }
The very first time your app runs, you'll see a message that you don't have an FCM token, followed by a message a short time later with your actual token. In subsequent runs, you should see your cached token right away. It's a 153-character random string that looks a lot like your server key, so don't get 'em confused.
So, now that you have both pieces of information, you can make a curl call. Try calling something like this:
> curl --header "Content-Type: application/json" \
--header "Authorization: key=AU...the rest of your server key...s38txvmxME-W1N4" \
https://fcm.googleapis.com/fcm/send \
-d '{"notification": {"body": "Hello from curl via FCM!", "sound": "default"},
"priority": "high",
"to": "gJHcrfzW2Y:APA91...the rest of your FCM token...-JgS70Jm"}'
If all has gone well, you'll see a notification on your device, as well as receive a "Success" response from FCM.
{"multicast_id":86655058283942579,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1486683492595106961%9e7ad9838bdea651f9"}]}
Don't get too excited by this success response. That just means FCM has successfully received your message; it doesn't mean that it has successfully delivered it to APNs. You really want to look for the notification on your device.
If your notification doesn't seem to be getting received, here's a few things to look out for:
  • Are you seeing an error message in your response? Don't ignore those. They're usually pretty big hints as to what's going on.
    • InvalidRegistration means you don't have the correct FCM device token. (Remember, it's actually called a "registration token")
    • A 401 error with the message "The request's Authentification (Server-) Key contained an invalid or malformed FCM-Token" means the server key is probably incorrect. Make sure you've copied the whole thing correctly from the Firebase console.
  • Is your priority set to high? Android and iOS devices have different interpretations of medium and high priority values.
  • On Android, medium priority basically means, "Send the message, but be respectful of your user's device if it's in doze mode". This is generally why FCM uses "medium" as its default priority if you don't specify one.
    • On iOS, medium (or 5) priority can best be summarized as, "Maybe we'll send it at some point. But in this crazy world of ours, who can say for sure?!? ¯\_(ツ)_/¯".
    • This is why APNs defaults to a priority value of 10 (or "high") when no priority value is specified and they really only ask you to send messages medium priority when you're sending a data-only content-available message.
    • Ideally, you should send most user-visible messages with medium priority to Android devices and high priority to iOS devices. If you're using the Firebase Notifications panel, you can do this pretty easily.
  • Are you using APNs syntax instead of FCM syntax? While FCM will properly translate FCM-speak to APNs, it will get confused if you send it APNs syntax in the first place. So double-check that you're sending messages properly formatted for FCM. In particular, confirm that you're setting "priority" to "high" and not "10".
    • If you're sending a content available message, make sure you're specifying "content_available": true with an underscore and not "content-available": 2
    • I also recommend trying to send a Notification through the Firebase Notifications panel at this point. If you can make a call through Notifications but not through a curl call, it might be a sign that your message isn't properly formatted.
  • Have you uploaded your APNs certificate to the Firebase console? And has it expired? Remember, FCM needs that certificate in order to communicate with APNs.

6. Make a call through the Notifications panel and/or your server

So if you've gotten to this point, we've basically established that the path from FCM to APNs to iOS to your app is working correctly. So I would be very surprised if the Notifications panel wasn't working at this point. If it is, the best thing to do is check status.firebase.google.com and see if there are any issues with the Cloud Messaging service. (This also includes the Notifications panel)
If the problem is with your server code, well… that's up to you and your server. But now that you've figured out exactly what data you need to generate to make a proper FCM call, I'm hoping you can tackle this part on your own with confidence. Or, at least, the appearance of confidence, which is enough to fool most people.

Whew! Well, I know that was a lot to go through, and hopefully you were able to stop at, like, step 2 because it turns out you forgot to flip some switch in your Xcode project. If that's the case, you're probably not even reading this conclusion. Come to think of it, if you made it this far, it's probably because you're still having problems with your implementation, in which case… uh… go check out our support channels. Or ask @lmoroney, because I'm basically out of suggestions at this point.

Thanks for reading!

[1] That's an NSNotification, not an APNs notification. Hooray for overloaded terms!
[2] One interesting error I ran into was a developer who couldn't understand why his content-available messages were only being received when his app was in the foreground. It turns out he had explicitly connected to FCM (like in step 1) and was using the (invalid) "content-available" key in his message. Since FCM didn't translate this into a valid APNs content-available message, it interpreted it as a data-only message that should only be sent over FCM, which is why it only worked when his app was in the foreground.

Doug Stevenson
Doug Stevenson
Developer Advocate

The Firebase Test Lab team is kicking off 2017 with some new features that make it easier for you to customize the testing of your Android apps. The following two enhancements are designed to save you time and money, so you can get the most out of Test Lab for your daily testing routine.
Sparse Device Matrix Selection

Previously, when selecting the combination of device and API levels, you were obliged to run against every possible valid combination of selected device and API level. For example, to run a test on a Nexus 5 and Nexus 7 on both API levels 19 and 21, the device selection matrix used to look like this: 

The above selection would have scheduled the test on a matrix with a total of four combinations of device and API level. But what if I want to test against only two configurations: a Nexus 5 with API level 21, and a Nexus 7 with API level 19? With the old UI, that was not possible. However, with the new UI, you can easily get this behavior:

The above selection will only schedule the two selected combinations of device and API level. You are no longer obliged to test on every possible combination of selected device and API. With this new UI, you can now be much more specific about the configurations you want. This makes it faster and less expensive to retry tests on only those configurations where your app previously encountered errors.
Saved Matrix Templates

If you use Test Lab frequently and are tired of entering the same device matrix information repeatedly, you can now save a matrix configuration as a template, and refer to that template when running a test. There is a new templates tab available on the Test Lab feature home screen:


Here, you can create and manage your device matrix templates. Then, when you go to run a test, you can select from a list of your templates to apply to the test:

Custom Text Input for Robo Test

If you're already using Firebase Test Lab for Android to run its automated Robo test, you've probably already gotten a lot of mileage out of its ability to intelligently crawl your app running on our devices. But for some of you, Robo wasn't a very useful option, because it was unable to bypass a custom login screen, or enter some specific text at the right place.


Now, you can configure a Robo test to use specific strings at key locations in your app. Here's an example of the Firebase console UI that specifies both a credential for a custom login form, and a search term to be used in a search box:
This UI is available as an "advanced option" when running a test.


This configuration is telling the Robo test to type the string "test_lab_user" into every EditText widget with the Android resource ID named "tv_username". It will do similarly for the tv_password and tv_search EditText widgets. This type of customization will make it easier for Robo log into your app and perform a search where those named widgets are observed. You can have text prepared for any EditText in your app that has a resource ID, as long as you're able to provide its name. However, this does not work for WebView elements.

Keep thinking about app quality!

The Firebase team wants your app to be successful, and a great way to make that happen is by using features provided by Firebase. So make 2017 a year of high quality for your app!


Todd Kerpleman
Todd Kerpelman
Developer Advocate
If you're a fan of our FirebaseDevelopers channel, you might have noticed that we just published a video all about how to best load up Remote Config values in your app:


But we know there are some people out there who would rather read than watch a video, so we thought we'd provide this for you in convenient blog form, too!

So if you've done any Remote Config work in the past, you probably noticed there are three main steps to implementing Remote Config in your app:
  1. You supply remote config with a bunch of default values. This is done locally on the device, either in code or by pulling in values from a local plist or xml file.
  2. You then call fetch() to download any new values from the cloud
  3. Finally, you call activateFetched() to apply these downloaded values on top of the default values you originally supplied.
At this point, when you query remote config for a particular value, you'll either get the updated value from the cloud, or whatever default you've supplied locally. And this is nice, because you can wire up your app to grab all of its values through Remote Config. You're still keeping your network calls nice and small, because you're only downloading the values that are different than your defaults, but you now have the flexibility to change any aspect of your app later through the cloud.

Seems pretty straightforward, right? And, for the most part, it is. The problem is that step 2 up there requires making a network call, and out in the real world, where people are using your app in tunnels, elevators, deserts, or places with poor network connections, you never really know how long it's going to take for that network call to complete. You can't guarantee that this process will be finished before your user starts interacting with your app.

So with that said, here are three strategies you can employ:

Strategy #1: Activate and refresh

This strategy is the most straightforward, which is generally why you'll see it in our sample apps and tutorials. The idea is that, once you've downloaded these new values from the cloud, you can call activateFetched() in your completion handler to immediately apply them, and then you tell your app to just go ahead update itself.


This strategy is nice in that it allows your user to get right into your app. But it's a little weird in that you potentially have your app changing things like button text, UI placement, or other critical values while your user is in the middle of using it, which can be a poor user experience. This is why many developers will opt to choose a solution like...

Strategy #2: Add a loading screen

A fairly common strategy is to display a loading screen when your user first starts up your app. Just like before, you'd call activateFetched in your completion handler to immediately apply these values, but instead of telling your app to refresh its interface, you would tell your app to dismiss the loading screen and transition to the main content. This is nice in that by the time your users make it past your loading screen and into your app, you're almost guaranteed to have fresh values from the cloud downloaded and ready to go.



Obviously, the big drawback here is that now you have a loading screen. This requires time and effort to build, and is also a barrier to your users getting into your app. But if your app already has a loading screen because you have to perform other work and/or network calls at startup, then this could be a good strategy. Just fetch your remote config values alongside whatever other startup work you're doing.

However, if your app doesn't already have a loading screen, I'd recommend trying an alternate strategy instead. Something like...

Strategy #3: Load values for next time

This one seems a little counter-intuitive, but hear me out: When your user starts up your app, you immediately call activateFetched(). This will apply any old values you've previously fetched from the cloud. And then your user can immediately start interacting with your app.
In the meantime, you kick off an asynchronous fetch() call to fetch new values from the cloud. And in the completion handler for this call… you do nothing. Heck, you don't even need add a completion handler in the first place. Those values you fetched from the cloud will remain stored locally on the device until your user calls activateFetched the next time they start your app.



This strategy is really nice in that your users can immediately start using their app, and there's no weird behavior where your app's interface suddenly changes out from under them. The obvious drawback is that it takes two sessions for your users to see your updated remote config values. You'll need to decide for yourself whether or not this is the right behavior for your app.
If you're using Remote Config to, say, fine-tune some values in your tower defense game, this is probably fine. If you're using it to deliver a message-of-the-day, or content specific to certain dates (like a holiday re-skin of your app) it might not be.

Strategy #3.5: Some hybrid of strategies 2 and 3. Or 1 and 3.

One nice thing about Remote Config is that it can tell you when your last successful fetch() call was performed. And so you can build some hybrid strategy where you first check how old your most recently fetched Remote Config data was. If it's fairly recent (say, in the last 48 hours or so), you can go ahead and run the "apply the latest batch of values and perform another fetch for next time" strategy. Otherwise, you can fall back to one of the earlier two strategies.



Let's talk about caching

As long as we're talking about loading strategies, let me hit on a related topic; caching. It sometimes confuses developers that values from remote config are cached for 12 hours instead of being grabbed immediately every time you call fetch(). And while you can reduce this cache time somewhat, if you start making network calls too frequently, your app might start getting throttled, either by the client, or the Remote Config service.

So, why do we have all of this caching and throttling behavior in the first place?

Well, partly it's a way to ensure we can keep this service free, even if your app scales to millions upon millions of users. By adding in some well-behaved caching, we can make sure that our service can handle your app for free, no matter how popular it gets.

It's also a nice way to protect the service from bad code. There are some developers out there (not you, of course) who might accidentally call fetch() too frequently. And we don't want the entire service getting slammed because one developer out there is accidentally DDoSing it. Having the client-side library serve up cached values and/or throttle frequent network calls helps keep the service safe.

But it's also good for your users. A good caching strategy prevents your app from using up your user's battery and/or data plan by making too many unnecessary network calls, which is always a good thing.

Obviously, while you're developing and testing your Remote Config implementation, this can be inconvenient, which is why you can override the local throttling behavior by turning on developer mode. But out in the real world, even if your users are using your app every single day, this kind of caching behavior is usually just fine.

But what if you want to push out values more frequently than that? What if you're trying to deliver a "Message of the hour" kind of feature through Remote Config? Well, frankly, this might be a sign that the Remote Config service isn't the ideal solution for you, and you should consider using the Realtime Database instead for something a little more timely.

On the other hand, there may be times when you want to infrequently apply an urgent Remote Config update. Perhaps you've accidentally broken your in-app economy with your latest batch of changes and you want everybody to get new values right away. How can you get around the cache and force your clients to perform a fetch?

One solution is to use Firebase Cloud Messaging. With FCM, you can send a data-only notification to all of your devices, letting them know that there's an urgent update pending. Your apps can respond to this incoming notification by storing some kind of flag locally. The next time your user starts up your app, it can look for this flag. If it's set to true, your app can perform a fetch with a cache time of 0 to ensure an immediate fetch. Just make sure you set this flag back when you're done, so your app can resume its usual caching behavior.

You might be tempted to have your clients respond to this incoming notification by immediately waking up and fetching new values from the Remote Config service, but if every instance of your app did that at once, there's a very good chance it will hit the server-side throttle, so I don't recommend this. Stick with the above strategy instead; that will spread out the network calls across the time period it takes for all of your users to open up your app.

So there ya go, folks! A few tips and tricks for loading up your values from Remote Config. Got any tips of your own? Feel free to share them in the comments on our YouTube video, or head on over to the Firebase Talk group and let us know how you're coming along with your Remote Config implementation.

Yes, that was kind of a pun. Cache hits; get it?

Francis Ma
Francis Ma
Firebase Product Manager
Almost eight months ago, we launched the expansion of Firebase to help developers build high-quality apps, grow their user base, and earn more money across iOS, Android and the Web. We've already seen great adoption of the platform, which brings together the best of Google's core businesses from Cloud to mobile advertising.

Our ultimate goal with Firebase is to free developers from so much of the complexity associated with modern software development, giving them back more time and energy to focus on innovation.

As we work towards that goal, we've continued to improve Firebase, working closely with our user community. We recently introduced major enhancements to many core features, including Firebase Analytics, Test Lab and Cloud Messaging, as well as added support for game developers with a C++ SDK and Unity plug-in.


We're deeply committed to Firebase and are doubling down on our investment to solve developer challenges.
Fabric and Firebase Joining Forces

Today, we're excited to announce that we've signed an agreement to acquire Fabric to continue the great work that Twitter put into the platform. Fabric will join Google's Developer Product Group, working with the Firebase team. Our missions align closely: help developers build better apps and grow their business.


As a popular, trusted tool over many years, we expect that Crashlytics will become the main crash reporting offering for Firebase and will augment the work that we have already done in this area. While Fabric was built on the foundation of Crashlytics, the Fabric team leveraged its success to launch a broad set of important tools, including Answers and Fastlane. We'll share further details in the coming weeks after we close the deal, as we work closely together with the Fabric team to determine the most efficient ways to further combine our strengths. During the transition period, Digits, the SMS authentication services, will be maintained by Twitter.


The integration of Fabric is part of our larger, long-term effort of delivering a comprehensive suite of features for iOS, Android and mobile Web app development.

This is a great moment for the industry and a unique opportunity to bring the best of Firebase with the best of Fabric. We're committed to making mobile app development seamless, so that developers can focus more of their time on building creative experiences.



Doug Stevenson
Doug Stevenson
Developer Advocate

I've got to come clean with everyone: I'm making no new year's resolutions for 2017. Nor did I make any in 2016. In fact, I don't think I've ever made one! It's not so much that I take a dim view of new year's resolutions. I simply suspect that I would likely break them by the end of the week, and feel bad about it!

One thing I've found helpful in the past is to see the new year as a pivoting point to try new things, and also improve the work I'm already doing. For me (and I hope for you too!), 2017 will be a fresh year for boosting app quality.

The phrase "app quality" can take a bunch of different meanings, based on what you value in the software you create and use. As a developer, traditionally, this means fixing bugs that cause problems for your users. It could also be a reflection of the amount of delight your users take in your app. All of this gets wrapped up into the one primary metric that we have to gauge the quality of a mobile app, which is your app's rating on the store where it's published. I'm sure every one of you who has an app on a storefront has paid much attention to the app's rating at some point!

Firebase provides some tools you can use to boost your app's quality, and if you're not already using them, maybe a fresh look at those tools would be helpful this year?

Firebase Crash Reporting

The easiest tool to get started with is Firebase Crash Reporting. It takes little to no lines of code to integrate it into your iOS and Android app, and once you do, the Firebase console will start showing crashes that are happening to your users. This gives you a "hit list" of problems to fix.
One thing I find ironic about being involved with the Crash Reporting team is how we view the influx of total crashes received as we monitor our system. Like any good developer product, we strive to grow adoption, which means we celebrate graphs that go "up and to the right". So, in a strange sense, we like to see more crashes, because that means more developers are using our stuff! But for all of you developers out there, more crashes is obviously a *bad* thing, and you want to make those numbers go down! So, please, don't be like us - make your crash report graphs go down and to the right in 2017!

Firebase Test Lab for Android

Even better than fixing problems for your users is fixing those problems before they even reach your users. For your Android apps, you can use Firebase Test Lab to help ensure that your apps work great for your users among a growing variety of actual devices that we manage. Traditionally, it's been kind of a pain to acquire and manage a good selection of devices for testing. However, with Test Lab, you simply upload your APK and tests, and it will install and run them to our devices. After the tests complete, we'll provide all the screenshots, videos, and logs of everything that happened for you to examine in the Firebase console.

With Firebase Test Lab for Android now available with generous daily quotas at no charge for projects on the free Spark tier, 2017 is a great time to get started with that. And, if you haven't set up your Android app builds in a continuous integration environment, you could set that up, then configure it to run your tests automatically on Test Lab.

If you're the kind of person who likes writing tests for your code (which is, admittedly, not very many of us!), it's natural to get those tests running on Test Lab. But, for those of us who aren't maintaining a test suite with our codebase, we can still use Test Lab's automated Robo test to get automated test coverage right away, with no additional lines of code required. That's not quite that same as having a comprehensive suite of tests, so maybe 2017 would be a good time to learn more about architecting "testable" apps, and how those practices can raise the bar of quality for your app. I'm planning on writing more about this later this year, so stay tuned to the Firebase Blog for more!

Firebase Remote Config

At its core, Firebase Remote Config is a tool that lets you configure your app using parameters that you set up in the Firebase console. It can be used to help manage the quality of your app, and there's a couple neat tricks you can do with it. Maybe this new year brings new opportunities to give them a try!

First of all, you can use Remote Config to carefully roll out a new feature to your users. It works like this:
  1. Code your new feature and restrict its access to the user by a Remote Config boolean parameter. If the value is 'false', your users don't see the feature. Make 'false' the default value in the app.
  2. Configure that parameter in the Firebase console to also be initially 'false' for everyone.
  3. Publish your app to the store.
  4. When it's time to start rolling out the new feature to a small segment of users, configure the parameter to be 'true' for, say, five percent of your user base.
  5. Stay alert for new crashes in Firebase Crash Reporting, as well as feedback from your users.
  6. If there is a problem with the new feature, immediately roll back the new feature by setting the parameter to 'false' in the console for everyone.
  7. Or, if things are looking good, increase the percentage over time until you reach 100% of your users.




This is much safer than publishing your new feature to everyone with a single app update, because now you have the option to immediately disable a serious problem, and without having to build and publish a whole new version of your app. And, if you can act quickly, most of your users will never encounter the problem to begin with. This works well with the email alerts you get from Firebase Crash Reporting when a new crash is observed.

Another feature of Remote Config is the ability to experiment with some aspect of your app in order to find out what works better for the users of your app, then measure the results in Firebase Analytics. I don't know about you, but I'm typically pretty bad at guessing what people actually prefer, and sometimes I'm surprised at how people might actually *use* an app! Don't guess - instead, do an experiment and know /for certain/ what delights your users more! It stands to reason that apps finely tuned like this can get better ratings and make more money.

Firebase Realtime Database

It makes sense that if you make it easier for you user to perform tasks in your app, they will enjoy using it more, and they will come back more frequently. One thing I have always disliked is having to check for new information by refreshing, or navigating back and forward again. Apps that are always fresh and up to date, without requiring me to take action, are more pleasant to use.

You can achieve this for your app by making effective use of Firebase Realtime Database to deliver relevant data directly to your users at the moment it changes in the database. Realtime Database is reactive by nature, because the client API is designed for you set up listeners at data locations that get triggered in the event of a change. This is far more convenient than having to poll an API endpoint repeatedly to check for changes, and also much more respectful of the user's mobile data and battery life. Users associate this feeling of delight with apps of high quality.

What does 2017 have in store for your app?

I hope you'll join me this year in putting more effort into making our users even more delighted. If you're with me, feel free to tweet me at @CodingDoug and tell me what you're up to in 2017!