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

Jen Person
Developer Advocate

In this edition: Write test-driven Cloud Functions, HIPAA compliance with Firebase Realtime Database, Swifty Firebase, Building a CRUD Ionic app with Cloud Firestore

Hey, Firebase developers! I recently reached out on Twitter to ask for your suggestions for your favorite Firebase tutorials, and was blown away by the responses! I'm amazed at all the creative ways you are using Firebase products. Here are just a few of the tutorials that I've been checking out lately.

Test-Driven Cloud Functions

Author: Chris Esplin

In the latest tutorial from Google Developer Expert Chris Esplin, see how to write test-driven Cloud Functions using Jest. Test-driven development (TDD) is a software development process that repeats a short development cycle: requirements are made into test cases, then the software is improved to pass the new tests. As someone who tends to test functions in production, I highly recommend you check out this blog to find a much better method!

How to Build a Secure HIPAA-Compliant Chat with Firebase Using Open Source Libraries

Author: David Szabo

The Health Insurance Portability and Accountability Act (HIPAA) is U.S. legislation that provides data privacy and security provisions for safeguarding medical information. Applications that handle certain types of health information have to comply with these regulations, so adhering to these standards is essential for developers who wish to distribute health apps in the United States. Lots of developers have asked me questions about Firebase and HIPAA, so I was super excited to see this tutorial, which shows how to use encryption to make a HIPAA-compliant chat app.

Swifty Firebase APIs @ Ka-ching

Author: Morten Bek Ditlevsen

As a lover of all things Firebase and iOS, I just had to include a Swift tutorial! Morten does some Swifty magic to resolve some of the common stumbling blocks of using Swift with the Realtime Database. In the first article of the series, you see how to implement support for Codable types in Firebase, allowing you to go from this:

ref.observeSingleEvent(of: .value) { snapshot in
    guard snapshot.exists() else {
        /* HANDLE ERROR */
    }
    guard let value = snapshot.value else {
        /* HANDLE ERROR */
    }
    guard let product = Product(usingMyCustomJSONConversion: value) else {
        /* HANDLE ERROR */
    }
}

let json: Any = product.myCustomJSONEncoding()
ref.setValue(json)

to this:

struct Product: Decodable { ... }

ref.observeSingleEvent(of: .value) { (result: DecodeResult<Product>) -> Void in
  // Use result or handle error    
}

try ref.setValue(product)

Triggering Firebase Cloud Functions with Radar events

Author: Nick Patrick, Radar.io

Radar is a toolkit that removes a number of the challenges involved in location context and tracking, and makes it super easy to perform tasks like knowing when a user enters or exits a known public place or custom geofence.

Nick's tutorial shows you how to use Radar's webhooks to trigger a Cloud Function for Firebase. In this case, it's sending a notification when a user visits their favorite coffee shop, but if you're building any kind of location-based app, you'll find lots of possibilities to apply what you learn!

Building a CRUD Ionic application with Firestore

Author: Jorge Vergara, JAVEBRATT

In this tutorial, you'll find step-by-step instructions for implementing an Ionic application with Cloud Firestore. If you use Angular and are new to Firebase, this tutorial will be a great resource for you!

---

Thanks to everyone who took the time to send me your suggestions! And if you don't see your recommended tutorial here, never fear! I couldn't fit all of the amazing recommendations here, so stay tuned for future blog posts. If these tutorials have inspired to build something with Firebase, let me know! You can find me on Twitter at @ThatJenPerson. I can't wait to see what you build!

Jingyu Shi
Partner Developer Advocate, Partner DevRel

In the "Notifying your users with FCM" blog post, we shared guidance on using FCM in modern Android with regard to all the power management features. Following on that, let's look at the common workflow of FCM messages -- notification messages and data messages, and how to handle these messages in your code.

Notification messages

When sending push notifications to your users, notification messages are the preferred method if you want to take advantage of the Firebase Console and let the Android system handle notification posting. Notification messages are high priority by default, and high priority FCM messages will still be delivered to users immediately even when the device is idle.

When using an FCM notification message, the system handles showing the notification on behalf of your app when it's in the background. When your app is in the foreground, the FCM notification message is delivered to the onMessageReceived()handler and you can handle it by posting a notification if needed or update the app content with the FCM payload data (Max 4KB) or fetch content from app server.

Workflow

An App server sends a notification message (or a notification message with a data payload) to a user:

  1. App server sends a notification message
    • Notification messages are high priority by default, and collapsible--the following message will replace the current message if it's not delivered yet.
  2. The FCM message is delivered to the client app
      When your app is in Foreground
      • Developer needs to handle the notification message in the handler.
      • Post a notification or update app content in the FCM callback.
      When your app is in Background
      • Notification delivered to System Tray.
  3. If there's a notification posted, your user may tap the notification and open your app. You can then use the data in getIntent() to update the app content if needed.

Note: if the user dismisses the notification, data will not be delivered when app opens.

Code sample:

class SimpleFirebaseMessagingService : FirebaseMessagingService() {

   private val TAG = "spFirebaseMsgService"

   override fun onMessageReceived(remoteMessage: RemoteMessage) {

       // when App is in foreground, notification message:
       if (remoteMessage.notification != null) {
           // post notification if needed
           updateContent(remoteMessage.notification)
       }

       // process data payload
   }

   private fun updateContent(notification: RemoteMessage.Notification) {}

}

SimpleFirebaseMessagingService.kt

Data message

You should use data messages if you need to handle the notification in the client app, whether to customize the notification or to decrypt the received payload data.

A data message is normal priority by default, which means it will be batched to the next maintenance window when the device is in Doze, by default.

Handling Messages

When sending data messages, you need to handle the messages in the onMessageReceived() callback in the client app. The suggested approach in the handler is as following:

  1. When you need to notify the user, post a notification first.
  2. Update app content with payload data.
  3. If needed, schedule the work to fetch additional data from app server.

Workflow

An App server sends data messages to notify users with end-to-end encrypted FCM message:

  1. App server sends a data message to FCM server
    • The data message should include all the data needed for a notification in the payload
  2. When the message is received in the FCM app client, you have a short window* to handle the message and post a notification
    1. Handle payload decryption and customized notification in the onMessageReceived() callback, then immediately post a notification to the user
      • Note that FCM does NOT guarantee the order of delivery. Developers should handle it when messages are delivered out of order; when you send non-collapsible messages, you can keep track of the order with a message sequence ID, and hold off notifying the user until all messages are received or you give up waiting in the event the prior messages TTL expired.
    2. Use the payload data to update the client content and store data locally
    3. If you need additional data that exceeds the 4KB FCM payload limit, schedule a job or use WorkManager to fetch the data but don't block displaying an initial notification on this.
      • WorkManager is recommended when it's stable
      • Use JobScheduler
    4. If you need to update the notification with data fetched, update the notification if it's still active
  3. User opens the app
    • If app content is not up-to-date, check if work scheduled in 2.3 is completed, and update your app content accordingly.

Note: Keep in mind that the FCM handler only has approximately 20 seconds once the message is received, as it's designed to complete short tasks like posting a notification. If you need to process longer than this window, we recommend scheduling a job or use the WorkManager API.

Code sample:

class SimpleFirebaseMessagingService : FirebaseMessagingService() {

   private val TAG = "spFirebaseMsgService"

   override fun onMessageReceived(remoteMessage: RemoteMessage) {

       // Use data payload to create a notification
       if (remoteMessage.data.isNotEmpty()) {

           // step 2.1: decrypt payload
           val notificationMessage = decryptPayload(remoteMessage.data)
           // step 2.1: display notification immediately
           sendNotification(notificationMessage)
           // step 2.2: update app content with payload data
           updateContent(remoteMessage.data)

           // Optional step 2.3: if needed, fetch data from app server
           /* if additional data is needed or payload is bigger than 4KB, App server can send a flag to notify client*/
           if (remoteMessage.data["url"] != null) {
               scheduleJob()
               // use WorkManager when it's stable
           }
       }
       // process notification payload when app in foreground...
   }

   private fun decryptPayload(dataPayload: Map<String, String>): String {
       return "decrypted message"
   }

   private fun sendNotification(notificationMessage: String) {}
   private fun updateContent(dataPayload: Map<String, String>) {}
   private fun scheduleWork() {
       // it's recommended to use WorkManager when it's stable, use JobScheduler
       // on background work complete, update the notification if still active
   }
}

SimpleFirebaseMessagingService.kt

FCM on modern Android

Android has introduced many power improvement features in recent versions, so make sure you review and test your FCM use cases against these features. You can learn more about Android Power features and how it works with FCM from this blog post.

If you are not using FCM yet, it's time to upgrade. The C2DM library was officially deprecated in 2012 and shut down completely in 2015, making it no longer compatible with modern Android. We also announced in April 2018 that Google Cloud Messaging (GCM) server and client APIs have been deprecated and will be removed as soon as April 11th, 2019. You can find out more about the announcement and migration guides in this blog post.

To take advantage of all the new features and functionality that FCM and Android provide, we recommend using FCM today. To get started, visit the Firebase Cloud Messaging documentation.

Sebastian Schmidt
Software Engineer

These days, we are increasingly using mobile and web apps in situations where there's little to no internet connectivity. We want to play videos during long road trips, navigate through areas with weak cell signals, and edit our cloud photos and documents on planes with no WiFi. That's why it's crucial to think about how your app will work offline as you're building your next big idea!

Firebase has long helped developers build offline-capable apps. We introduced offline persistence for the iOS and Android Realtime Database SDKs back in 2015. We took care of the hard work behind the scenes, and you only had to write a single line of initialization code.

Taking it a step further, the Cloud Firestore SDKs support offline persistence on the Web, in addition to Android and iOS, allowing you to build rich and offline-capable apps on all your favorite platforms.

With offline persistence, the Cloud Firestore SDKs manage all your data locally, and can execute advanced queries purely against the local cache. Even when your app is restarted, all local edits to documents remain buffered until they are successfully sent to the backend.

But until now, Cloud Firestore's offline support for Web had one big caveat: It only supported offline persistence for the first tab. If you've used it, you have probably come across this error:

'There is another tab open with offline persistence enabled. Only one such tab is allowed at a time. The other tab must be closed or persistence must be disabled.'

Fortunately, you never need to see that error again.

Today, we are introducing multi-tab offline persistence for the Cloud Firestore Web SDK. Your users can read and modify their local data even when they open your app in multiple tabs. All tabs access the same persisted data and synchronize local edits together, even when your network is not connected. This feature can even help reduce billing costs, because the SDK can share query results between tabs without reissuing the query to the backend!

The Firebase JS SDK 5.5.0 release includes experimental offline support for the Cloud Firestore Web SDK for Chrome, Safari and Firefox. You can turn on multi-tab synchronization as follows:

 const db = firebase.firestore();
 db.enablePersistence({experimentalTabSynchronization:true}).then(() => {
       console.log("Woohoo! Multi-Tab Persistence!");
     });

Try out our new offline persistence features and let us know what you think! Share your feedback in our Google Group or on our GitHub page.

Jen Person
Developer Advocate

As an app developer, getting users to stay with your app is one of your biggest challenges. While OS-level notifications are a great way to remind users to return to your app, they can get lost in a sea of notifications from other apps.. If you want to tell your customers about, say, some new power-up that's for sale in your in-app store, wouldn't it be better to tell them about it when they've started up your game, or completed a level, rather than when they're driving to work?

With that in mind, we recently released the latest product to help you grow your app: Firebase In-App Messaging. With In-App Messaging, you can now engage users who are actively using your iOS or Android app by sending them targeted, personalized, and contextual messages. These notifications nudge users toward important in-app actions, such as subscribing to your newsletter, watching a video, or buying an item.

You now have the flexibility and control to set-up in-app messages in a variety of formats and trigger them based on user behavior. I'm going to walk you through just one example of the many ways you can utilize Firebase In-App Messaging. I've already set up my app to be able to receive messages by following the instructions in the Getting Started guide.

Let's say that I have a social media app that's all about posting pictures of cats. I've recently launched a new sharing feature that I want to tell my users about.

This is a perfect use case for Firebase In-App Messaging! With Firebase In-App Messaging, I can create this campaign right from the Firebase Console, as shown below.

In the console, I've selected an image and button color that align with the look-and-feel of my app. I have also included a button action, which, in my case, will bring up the sharing dialog in the app, so my users can easily start sharing their favorite photos.

With In-App Messaging, you have multiple options for targeting messages, allowing you to tailor messages to the groups you want to reach. You can select all users of the app, users from specific Google Analytics audiences, users matching given properties, or users who Firebase Predictions believes will participate in a behavior in the next seven days. In my case, I am selecting all users.

With the scheduling feature, I can decide when this dialog will appear in my app. While I could use Firebase In-App Messaging to show a message to my users when they first start up the app, I'm going to make my campaign more targeted and relevant by showing this message only to my users after they like (or "paws-up") their first photo.

I also have the option to set a start and end date for my campaign, in case I'm running a seasonal promotion.

Finally, you can select a conversion event, which is the measurement you would use to determine if the campaign is successful. In my case, I'll be tracking whether or not a user has successfully shared an image.

The chosen conversion event is tracked in the Firebase console, like the chart below.

As the campaign continues, I can track impressions, button clicks, and conversions. I can also edit the campaign, allowing me to test changes that may improve the conversion rate.

This is just one example of how In-App Messaging can promote conversion. It can also be used to highlight a sale or coupon within an ecommerce app, inform users of terms of service updates, or showcase new features. A guiding light within an app, Firebase In-App Messaging encourages app exploration and discovery, increases session time, and spurs conversions. For more examples, check out our blog announcement!

Ready to dive in? Get started today by checking out the documentation:

Getting started with Firebase In-App Messaging

Have a cool use case for Firebase In-App Messaging? Share it with me on Twitter at @ThatJenPerson. I can't wait to see the campaigns you build!

David East
David East
Developer Advocate

Firebase provides realtime streams and async callbacks to do all sorts of awesome things in your app. Getting data from one realtime stream is easy enough, but what if you want to join it from another? Combining multiple async streams of data can get complex. At Firebase, we wanted to help simplify things, so we created a new JavaScript library: RxFire.

RxJS + Firebase

RxJS is a popular JavaScript library that simplifies async streams of data. The core component of RxJS is the observable. The observable is a mechanism for observing data as it changes over time. RxJS allows you to transform the changes as they occur with array like operators such as map and filter. This works magically with Firebase.

RxJS is a powerful toolkit for observing and transforming data streams. Firebase provides multiple types of data streams. Combine the two and you have a simple way of managing async data across multiple Firebase streams. Need to get data from an authenticated user? Want to join two records in Firestore? Want to lazy load the Firebase SDK with Webpack? This is the magic we're talking about. We wanted to capture this magic in a library: RxFire.

Introducing RxFire

RxFire provides a set of observable creation methods. You simply call a function with some parameters to receive an RxJS observable. With that observable in hand you can use any operators provided by RxJS to transform the stream as you like.

The observable has a .pipe() method that is used to attach operators. This is really similar to an array. You can .map() and .filter(), but the magic is that as data comes in over time it is ran through these operators. This is great for situations where you want to listen for when a user logs in.

import firebase from 'firebase/app';
import 'firebase/auth';
import { authState } from 'rxfire/auth';
import { filter } from 'rxjs/operators';

const app = firebase.initializeApp({ /* config */ });
authState(app.auth())
  .pipe(
    filter(u => u !== null)
  ).subscribe(u => { console.log('the logged in user', u); });

The code above uses RxFire to listen to authentication state. The .filter() operator checks for an authenticated user. The .subscribe() callback is triggered only if a user is present.

Simplify common Firebase tasks

RxFire also includes helpful functions for common Firebase tasks. Do you need a synchronized array of child events in the Realtime Database? You're just one function call away!

import firebase from 'firebase/app';
import 'firebase/database';
import { list } from 'rxfire/database';

const app = firebase.initializeApp({ /* config */ });
const todosRef = app.database().ref('todos');
list(todosRef).subscribe(list => { console.log('a synchronized array!', list); });

Events occurring on the todoRef trigger the subscribe callback with a new array.

For all frameworks

The idea of mixing RxJS and Firebase together is not revolutionary or unique. The officially supported AngularFire library heavily uses RxJS with Firebase. We took the lessons we learned from over two years of development and ported it out so all frameworks could benefit. It doesn't matter if you're using React, Preact, Vue, Stencil, Polymer, or just plain old JavaScript. RxFire is setup to be a simple library to simplify the Firebase streams. Just look at this simple Preact component.

import { Component, h } from 'preact';
import firebase from 'firebase/app';
import 'firebase/firestore';
import { collectionData } from 'rxfire/firestore';

export class AppComponent extends Component {

  componentWillMount() {
    this.app = firebase.initializeApp({ /* config */ });
    this.todosRef = app.firestore().collection('todos');
    collectionData(todosRef, 'id').subscribe(todos => { 
      // re-render on each change
      this.setState({ todos });
    });       
  }

  render() {
    const lis = this.state.todos.map(t => <li key={t.id}>{t.title}</li>);
    return (
      <div>
        <ul>
          {lis}
      </ul>
    </div>
   );
  }  
}

The component renders new list items each time the collection data changes.

Build you own library with RxFire

Notice that RxFire isn't a complete wrapper for Firebase and RxJS, rather it works in a complementary fashion. This makes is easy to sprinkle in to existing applications or even build new integrations.

Let's say you want to create an idiomatic library for your app or framework. You can use RxFire as a base layer and let it do the heavy lifting for you. All you have to do is place your own API on top of it instead of worrying about complex tasks like synchronizing multiple events into a single array.

Combine multiple data sources

A common task is joining data in either the Realtime Database or Firestore. This is difficult because you're trying to wrangle data from multiple callbacks. Each callback can trigger at different times and it becomes a strain to figure out what's going on.

RxFire simplifies combining multiple data sources. You don't have to worry about nested callbacks or wrangling variables that are out-of-scope. All you need to do is use RxJS's observable combination methods like merge or combineLatest. In fact, combineLatest works just like a join.

import firebase from 'firebase/app';
import 'firebase/firestore';
import { collectionData, docData } from 'rxfire/firestore';
import { combineLatest } from 'rxjs';

const app = firebase.initializeApp({ /* config */ });
// Create observables of document and collection data
const profile$ = docData(app.firestore().doc('users/david'));
const cart$ = collectionData(app.firestore().collection('carts/david'));
const subscription = combineLatest(profile$, cart$, (profile, cart) => {
  // transform the profile to add the cart as a child property
  profile.cart = cart;
  return profile;
})
  .subscribe(profile => { console.log('joined data', profile); });

// Unsubscribe to both collections in one call!
subscription.unsubscribe();

The combineLatest method waits for each observable to fire once. It then triggers the subscription callback each time a change occurs to either the profileRef or cartRef. The last argument allows you to transform the data into a single object, making joins much easier.

Retrieving data from an authenticated user is another common task that traditionally required nested callbacks. RxJS allows you to get one piece of data asynchronously and then use that to conditionally get another. In this case you can check for an authenticated user and then retrieve a photo from Cloud Storage based on the user's id.

import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/storage';
import { authState } from 'rxfire/auth';
import { getDownloadURL } from 'rxfire/storage';
import { switchMap, filter } from 'rxjs/operators';

const app = firebase.initializeApp({ /* config */ });
authState(app.auth())
  .pipe(
    filter(u => u !== null),
    switchMap(u => {
      const ref = app.storage().ref(`profile/${u.uid}`);
      return getDownloadURL(ref):
    });
  ).subscribe(photoURL => {
    console.log('the logged in user's photo', photoURL);
  });

The code above listens for the authenticated users and filters out any logged out or null events. When there is an authenticated user, we get their photo URL based on the logged in user's id.

Simplify code-splitting of Firebase

Modern web tools like Webpack provide advanced tooling like code-splitting. Code-splitting is an easy way to lighten the load of your application's core code. The idea is to keep only what's needed in the core of the application and load the rest only when needed. This is achieved by using the dynamic import() statement.

document.querySelector('#btnLoadFirebase')
  .addEventListener('click', event => {
    // load Firebase only when this button is clicked!
    import('firebase/app')
      .then(firebase => { console.log('lazy loaded!' });
});

Webpack is super smart and takes the lazily loaded module ('firebase/app' in this case) and splits it out into a separate file. This is a huge benefit for performance because it lightens to amount of JavaScript the browser has to load upfront.

While this technique is great for performance, it's really tricky to get right. You may have to load multiple libraries and deal with multiple async callbacks. RxFire makes this much easier. Using RxFire you can simplify the loading The dynamic import() uses promises, but RxJS makes it easy to convert promises to observables.

import { from, combineLatest } from 'rxjs';
import { map, mergeMap } from 'rxjs/operators';

function lazyLoadCollection(config, collectionName) {
  const app$ = from(import('firebase/app'));
  const firestore$ = from(import('firebase/firestore'));
  const rxfire$ = from(import('rxfire/firestore'));
  return combineLatest(app$, firestore$, rxfire$)
    .pipe(
      map(([firebase, firestore, rxfire]) => {
        const app = firebase.apps[0] || firebase.initializeApp(config);
        return { app, rxfire };
      }),
      mergeMap(([app, rxfire]) => {
        const ref = app.firestore().collection(collectionName);
        return rxfire.collectionData(ref, 'id');
      })
    );
}

document.querySelector('#btnLoadTodos')
  .addEventListener('click', event => {
    lazyLoadCollection({ /* config */ }, 'todos').subscribe(todos => {
      console.log('lazy todos!', todos);
    });
});

The sample above creates a stream of todos from Firestore while lazily loading Firebase, RxFire, and RxFire on a button click. This type of code is still more complex that we'd like, but RxFire is in beta and we're working on making this much easier.

Give it a try!

We'd love for you to kick the tires on RxFire. Give it an install on npm or yarn. Make sure to include Firebase and RxJS as they are peer dependencies.

npm i rxfire firebase rxjs
# or
yarn add rxfire firebase rxjs

RxFire is in beta. We really value your feedback in any form. File issues or feature requests on Github and even contribute your own ideas and code! We're really excited to see what you'll do with RxFire.

Shyam Jayaraman
Software Engineer

At Firebase, we're committed to transparency and a thriving developer community, which is why we started open sourcing our SDKs last year. Today, we're continuing on that mission by open sourcing our first Firebase Android SDKs.

For this initial release, we are open sourcing our Cloud Firestore, Cloud Functions, Realtime Database, Storage, and FirebaseCommon SDKs. We intend to release more SDKs going forward, so don't forget to star or watch the repo.

Where's the repo?

The Firebase Android SDK source code can be found at https://github.com/firebase/firebase-android-sdk.

For the SDKs included in the repository, GitHub is the source of truth, though you can also find our project in the Google Open Source directory and on firebaseopensource.com. On GitHub, you'll be able to observe the progress of new features and bug fixes and build a local copy of the SDK on your development machine to preview upcoming releases. Our GitHub README provides more details on how you build, test, and contribute to our Android SDK.

As with our already available open-source iOS, Javascript, Node.js, Java, Python, Go and .NET SDKs, should you find issues in our code you can report them through the standard GitHub issue tracker. Your feedback is vital in shaping the future of Firebase and we look forward to hearing from you on GitHub!