Google FCM

Google Firebase Cloud Messaging

Google Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably deliver messages at no cost.

Using FCM, you can notify a client app that new email or other data is available to sync. You can send notification messages to drive user re-engagement and retention. For use cases such as instant messaging, a message can transfer a payload of up to 4KB to a client app.

The Alpakka Google Firebase Cloud Messaging connector provides a way to send notifications with Firebase Cloud Messaging.

Project Info: Alpakka Google Firebase Cloud Messaging (FCM)
Artifact
com.lightbend.akka
akka-stream-alpakka-google-fcm
1.1.1
JDK versions
OpenJDK 8
Scala versions2.12.7, 2.11.12
JPMS module nameakka.stream.alpakka.google.firebase.fcm
License
Readiness level
Since 0.18, 2018-03-28
Home pagehttps://doc.akka.io/docs/alpakka/current
API documentation
Forums
Release notesIn the documentation
IssuesGithub issues
Sourceshttps://github.com/akka/alpakka

Artifacts

sbt
libraryDependencies += "com.lightbend.akka" %% "akka-stream-alpakka-google-fcm" % "1.1.1"
Maven
<dependency>
  <groupId>com.lightbend.akka</groupId>
  <artifactId>akka-stream-alpakka-google-fcm_2.12</artifactId>
  <version>1.1.1</version>
</dependency>
Gradle
dependencies {
  compile group: 'com.lightbend.akka', name: 'akka-stream-alpakka-google-fcm_2.12', version: '1.1.1'
}

The table below shows direct dependencies of this module and the second tab shows all libraries it depends on transitively.

Direct dependencies
OrganizationArtifactVersionLicense
com.pauldijoujwt-core_2.122.1.0Apache-2.0
com.typesafe.akkaakka-http-spray-json_2.1210.1.8Apache-2.0
com.typesafe.akkaakka-http_2.1210.1.8Apache-2.0
com.typesafe.akkaakka-stream_2.122.5.23Apache License, Version 2.0
org.scala-langscala-library2.12.7BSD 3-Clause
Dependency tree
com.pauldijou    jwt-core_2.12    2.1.0    Apache-2.0
    org.bouncycastle    bcpkix-jdk15on    1.60    Bouncy Castle Licence
        org.bouncycastle    bcprov-jdk15on    1.60    Bouncy Castle Licence
    org.scala-lang    scala-library    2.12.7    BSD 3-Clause
com.typesafe.akka    akka-http-spray-json_2.12    10.1.8    Apache-2.0
    com.typesafe.akka    akka-http_2.12    10.1.8    Apache-2.0
        com.typesafe.akka    akka-http-core_2.12    10.1.8    Apache-2.0
            com.typesafe.akka    akka-parsing_2.12    10.1.8    Apache-2.0
                org.scala-lang    scala-library    2.12.7    BSD 3-Clause
            org.scala-lang    scala-library    2.12.7    BSD 3-Clause
        org.scala-lang    scala-library    2.12.7    BSD 3-Clause
    io.spray    spray-json_2.12    1.3.5    Apache 2
        org.scala-lang    scala-library    2.12.7    BSD 3-Clause
    org.scala-lang    scala-library    2.12.7    BSD 3-Clause
com.typesafe.akka    akka-http_2.12    10.1.8    Apache-2.0
    com.typesafe.akka    akka-http-core_2.12    10.1.8    Apache-2.0
        com.typesafe.akka    akka-parsing_2.12    10.1.8    Apache-2.0
            org.scala-lang    scala-library    2.12.7    BSD 3-Clause
        org.scala-lang    scala-library    2.12.7    BSD 3-Clause
    org.scala-lang    scala-library    2.12.7    BSD 3-Clause
com.typesafe.akka    akka-stream_2.12    2.5.23    Apache License, Version 2.0
    com.typesafe.akka    akka-actor_2.12    2.5.23    Apache License, Version 2.0
        com.typesafe    config    1.3.3    Apache License, Version 2.0
        org.scala-lang.modules    scala-java8-compat_2.12    0.8.0    BSD 3-clause
            org.scala-lang    scala-library    2.12.7    BSD 3-Clause
        org.scala-lang    scala-library    2.12.7    BSD 3-Clause
    com.typesafe.akka    akka-protobuf_2.12    2.5.23    Apache License, Version 2.0
        org.scala-lang    scala-library    2.12.7    BSD 3-Clause
    com.typesafe    ssl-config-core_2.12    0.3.7    Apache-2.0
        com.typesafe    config    1.3.3    Apache License, Version 2.0
        org.scala-lang.modules    scala-parser-combinators_2.12    1.1.1    BSD 3-clause
            org.scala-lang    scala-library    2.12.7    BSD 3-Clause
        org.scala-lang    scala-library    2.12.7    BSD 3-Clause
    org.reactivestreams    reactive-streams    1.0.2    CC0
    org.scala-lang    scala-library    2.12.7    BSD 3-Clause
org.scala-lang    scala-library    2.12.7    BSD 3-Clause

Settings

Prepare your credentials for access to FCM.

Scala
val privateKey =
  """-----BEGIN RSA PRIVATE KEY-----
    |MIIBOgIBAAJBAJHPYfmEpShPxAGP12oyPg0CiL1zmd2V84K5dgzhR9TFpkAp2kl2
    |9BTc8jbAY0dQW4Zux+hyKxd6uANBKHOWacUCAwEAAQJAQVyXbMS7TGDFWnXieKZh
    |Dm/uYA6sEJqheB4u/wMVshjcQdHbi6Rr0kv7dCLbJz2v9bVmFu5i8aFnJy1MJOpA
    |2QIhAPyEAaVfDqJGjVfryZDCaxrsREmdKDlmIppFy78/d8DHAiEAk9JyTHcapckD
    |uSyaE6EaqKKfyRwSfUGO1VJXmPjPDRMCIF9N900SDnTiye/4FxBiwIfdynw6K3dW
    |fBLb6uVYr/r7AiBUu/p26IMm6y4uNGnxvJSqe+X6AxR6Jl043OWHs4AEbwIhANuz
    |Ay3MKOeoVbx0L+ruVRY5fkW+oLHbMGtQ9dZq7Dp9
    |-----END RSA PRIVATE KEY-----""".stripMargin
val clientEmail = "[email protected]"
val projectId = "test-XXXXX"
val fcmConfig = FcmSettings(clientEmail, privateKey, projectId)
Java
String privateKey =
    "-----BEGIN RSA PRIVATE KEY-----\n"
        + "MIIBOgIBAAJBAJHPYfmEpShPxAGP12oyPg0CiL1zmd2V84K5dgzhR9TFpkAp2kl2\n"
        + "9BTc8jbAY0dQW4Zux+hyKxd6uANBKHOWacUCAwEAAQJAQVyXbMS7TGDFWnXieKZh\n"
        + "Dm/uYA6sEJqheB4u/wMVshjcQdHbi6Rr0kv7dCLbJz2v9bVmFu5i8aFnJy1MJOpA\n"
        + "2QIhAPyEAaVfDqJGjVfryZDCaxrsREmdKDlmIppFy78/d8DHAiEAk9JyTHcapckD\n"
        + "uSyaE6EaqKKfyRwSfUGO1VJXmPjPDRMCIF9N900SDnTiye/4FxBiwIfdynw6K3dW\n"
        + "fBLb6uVYr/r7AiBUu/p26IMm6y4uNGnxvJSqe+X6AxR6Jl043OWHs4AEbwIhANuz\n"
        + "Ay3MKOeoVbx0L+ruVRY5fkW+oLHbMGtQ9dZq7Dp9\n"
        + "-----END RSA PRIVATE KEY-----";
String clientEmail = "[email protected]";
String projectId = "test-XXXXX";
FcmSettings fcmConfig = FcmSettings.create(clientEmail, privateKey, projectId);

The last two parameters in the above example are the predefined values. You can send test notifications (so called validate only). And you can set the number of maximum concurrent connections. There is a limitation in the docs; from one IP you can have maximum 1k pending connections, and you may need to configure akka.http.host-connection-pool.max-open-requests in your application.conf.

Sending notifications

To send a notification message create your notification object, and send it!

Scala
import akka.stream.alpakka.google.firebase.fcm.FcmNotificationModels._
import akka.stream.alpakka.google.firebase.fcm.scaladsl.GoogleFcm
import akka.stream.alpakka.google.firebase.fcm._

val result1: Future[immutable.Seq[FcmResponse]] =
  Source
    .single(notification)
    .via(GoogleFcm.send(fcmConfig))
    .map {
      case res @ FcmSuccessResponse(name) =>
        println(s"Successful $name")
        res
      case res @ FcmErrorResponse(errorMessage) =>
        println(s"Send error $errorMessage")
        res
    }
    .runWith(Sink.seq)
Java
import akka.stream.alpakka.google.firebase.fcm.*;
import akka.stream.alpakka.google.firebase.fcm.javadsl.GoogleFcm;

CompletionStage<List<FcmResponse>> result1 =
    Source.single(notification)
        .via(GoogleFcm.send(fcmConfig))
        .map(
            res -> {
              if (res.isSuccess()) {
                FcmSuccessResponse response = (FcmSuccessResponse) res;
                System.out.println("Successful " + response.getName());
              } else {
                FcmErrorResponse response = (FcmErrorResponse) res;
                System.out.println("Send error " + response.getRawError());
              }
              return res;
            })
        .runWith(Sink.seq(), materializer);

With this type of send you can get responses from the server. These responses can be FcmSuccessResponse or FcmErrorResponse. You can choose what you want to do with this information, but keep in mind if you try to resend the failed messages you will need to use exponential backoff! (see Akka docs RestartFlow.onFailuresWithBackoff)

If you don’t care if the notification was sent successfully, you may use fireAndForget.

Scala
import akka.stream.alpakka.google.firebase.fcm.FcmNotificationModels._
import akka.stream.alpakka.google.firebase.fcm.scaladsl.GoogleFcm
import akka.stream.alpakka.google.firebase.fcm._

val notification = FcmNotification("Test", "This is a test notification!", Token("token"))
Source
  .single(notification)
  .runWith(GoogleFcm.fireAndForget(fcmConfig))
Java
import akka.stream.alpakka.google.firebase.fcm.*;
import akka.stream.alpakka.google.firebase.fcm.javadsl.GoogleFcm;

FcmNotification notification =
    FcmNotification.basic(
        "Test", "This is a test notification!", new FcmNotificationModels.Token("token"));
Source.single(notification).runWith(GoogleFcm.fireAndForget(fcmConfig), materializer);

With fire and forget you will just send messages and ignore all the errors.

To help the integration and error handling or logging, there is a variation of the flow where you can send data beside your notification.

Scala
import akka.stream.alpakka.google.firebase.fcm.FcmNotificationModels._
import akka.stream.alpakka.google.firebase.fcm.scaladsl.GoogleFcm
import akka.stream.alpakka.google.firebase.fcm._

val result2: Future[immutable.Seq[(FcmResponse, String)]] =
  Source
    .single((notification, "superData"))
    .via(GoogleFcm.sendWithPassThrough(fcmConfig))
    .runWith(Sink.seq)
Java
import akka.stream.alpakka.google.firebase.fcm.*;
import akka.stream.alpakka.google.firebase.fcm.javadsl.GoogleFcm;

CompletionStage<List<Pair<FcmResponse, String>>> result2 =
    Source.single(Pair.create(notification, "superData"))
        .via(GoogleFcm.sendWithPassThrough(fcmConfig))
        .runWith(Sink.seq(), materializer);

Here I send a simple string, but you could use any type.

Scala only

You can build any notification described in the original documentation. It can be done by hand, or using some builder method. If you build your notification from scratch with options (and not with the provided builders), worth to check isSendable before sending.

Scala
val buildedNotification = FcmNotification.empty
  .withTarget(Topic("testers"))
  .withBasicNotification("title", "body")
  //.withAndroidConfig(AndroidConfig(...))
  //.withApnsConfig(ApnsConfig(...))
  .withWebPushConfig(
    WebPushConfig(
      headers = Map.empty,
      data = Map.empty,
      WebPushNotification("web-title", "web-body", "http://example.com/icon.png")
    )
  )
val sendable = buildedNotification.isSendable

There is a condition builder too.

Scala
import akka.stream.alpakka.google.firebase.fcm.FcmNotificationModels.Condition.{Topic => CTopic}
val condition = Condition(CTopic("TopicA") && (CTopic("TopicB") || (CTopic("TopicC") && !CTopic("TopicD"))))
val conditioneddNotification = FcmNotification("Test", "This is a test notification!", condition)
Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.