Как управлять установками и идентификаторами Firebase

Сервис установки Firebase (FIS) предоставляет идентификатор установки Firebase (FID) для каждого установленного экземпляра приложения Firebase. Идентификатор установки Firebase используется внутри следующих сервисов Firebase:

Сервис Firebase Функция "Firebase установок"
Firebase Cloud Messaging

Firebase Cloud Messaging использует Firebase идентификаторы установки, чтобы показывать сообщения на определенных устройствах.

Firebase Crashlytics

Firebase Crashlytics меняет UUID Crashlytics при каждом изменении идентификатора установки Firebase экземпляра приложения. В будущем идентификатор установки может использоваться для включения функций, которые улучшают отчеты об ошибках и сервисы управления ими.

Firebase In-App Messaging

Firebase In-App Messaging использует Firebase идентификаторы установки, чтобы выбирать устройства для доставки сообщений.

Firebase Performance Monitoring

Performance Monitoring использует идентификаторы установки Firebase, чтобы рассчитать количество уникальных установок Firebase, которые обращаются к сетевым ресурсам. Это позволяет обеспечить достаточную анонимность шаблонов доступа. Кроме того, для управления частотой отправки отчетов о событиях эффективности используются идентификаторы установки Firebase с Firebase Remote Config.

Firebase Remote Config

Remote Config использует Firebase идентификаторов установки, чтобы выбирать значения конфигурации, которые будут возвращаться на устройства конечных пользователей. Функция персонализации Remote Config использует идентификаторы установки Firebase, чтобы связывать данные о событиях в приложении с установками Firebase. Это позволяет прогнозировать и возвращать оптимальные значения конфигурации на устройства конечных пользователей.

Firebase ML

Учетные данные, называемые токенами аутентификации при установке, используются Firebase ML для аутентификации устройства при взаимодействии с экземплярами приложений, например для распространения моделей разработчиков в экземплярах приложений.

Хранилище сегментов пользователей Firebase

В хранилище сегментации пользователей Firebase хранятся Firebase идентификаторов установки и связанные с ними атрибуты и сегменты, чтобы предоставлять информацию о таргетинге другим сервисам Firebase, которые ее используют.

Обычно сервисы Firebase используют сервис установок Firebase без необходимости прямого взаимодействия разработчиков с FIS API. Однако в некоторых случаях разработчикам приложений может понадобиться напрямую вызывать API FIS, например:

  • Чтобы удалить установку Firebase и связанные с ней данные.
  • Чтобы получать идентификаторы (идентификаторы установки Firebase) и настраивать таргетинг на определенные установки приложений.
  • Получение токенов аутентификации для аутентификации установок Firebase.

Чтобы начать работу с FIS API, добавьте SDK в приложение.

Как добавить в приложение SDK для отслеживания установок Firebase

iOS+

  1. Добавьте в Podfile зависимость для установки Firebase:
    pod 'FirebaseInstallations'
  2. Запустите pod install и откройте созданный файл .xcworkspace.
  3. Импортируйте модуль FirebaseCore в UIApplicationDelegate, а также любые другие модули Firebase, которые использует делегат вашего приложения. Например, чтобы использовать свойства Cloud Firestore и Authentication, выполните следующие действия:

    SwiftUI

    import SwiftUI
    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Swift

    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Objective-C

    @import FirebaseCore;
    @import FirebaseFirestore;
    @import FirebaseAuth;
    // ...
          
  4. Настройте общий экземпляр FirebaseApp в методе application(_:didFinishLaunchingWithOptions:) делегата приложения:

    SwiftUI

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Swift

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Objective-C

    // Use Firebase library to configure APIs
    [FIRApp configure];
  5. Если вы используете SwiftUI, вам нужно создать делегат приложения и прикрепить его к структуре App с помощью UIApplicationDelegateAdaptor или NSApplicationDelegateAdaptor. Также необходимо отключить подмену делегата приложения. Подробнее о SwiftUI…

    SwiftUI

    @main
    struct YourApp: App {
      // register app delegate for Firebase setup
      @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
      var body: some Scene {
        WindowGroup {
          NavigationView {
            ContentView()
          }
        }
      }
    }
          

Android

Добавьте зависимость для Android SDK установок Firebase в файл Gradle на уровне модуля (обычно app/build.gradle):

implementation 'com.google.firebase:firebase-installations:19.1.2'

JavaScript

В зависимости от того, как размещено веб-приложение, его конфигурация может обрабатываться автоматически или вам может потребоваться обновить объект конфигурации Firebase.

Например, если зависимости добавлены в index.html, добавьте зависимость в элемент <head>:

<script src="/__/firebase/12.19.0/firebase-installations.js"></script>

Flutter

  1. В корневом каталоге проекта Flutter выполните следующую команду, чтобы установить плагин Firebase:

    flutter pub add firebase_app_installations
    
  2. Пересоберите проект:

    flutter run
    
  3. Импортируйте плагин для установки Firebase:

    import 'package:firebase_app_installations/firebase_app_installations.dart';
    

Как удалить установку Firebase

Данные, связанные с установкой Firebase, обычно не позволяют идентифицировать личность. Однако пользователям может быть полезно иметь возможность управлять этими данными и удалять их.

Идентификаторы установки Firebase уникальны для каждого экземпляра каждого приложения. У разных приложений на одном устройстве разные идентификаторы установки Firebase. Идентификаторы установки приложения Firebase позволяют идентифицировать установки приложений и связанные с ними данные.

Когда вы удаляете идентификатор установки, связанные с ним данные удаляются из активных и резервных систем всех сервисов Firebase, которые используют идентификаторы установки Firebase для идентификации установок, в течение 180 дней. Общие сведения об этом процессе приведены в заявлении Google об удалении и хранении данных.

Если вы не отключите все сервисы, генерирующие идентификаторы FID, в приложении, FIS создаст новый идентификатор в течение нескольких дней. Firebase считает новый идентификатор новой установкой Firebase и никак не связывает его с предыдущим идентификатором или данными.

Как удалить идентификатор экземпляра установки с помощью вызова клиентского API

Чтобы удалить FID, созданные сервисами Firebase, вызовите подходящий метод из Firebase SDK установок:

Swift

do {
  try await Installations.installations().delete()
  print("Installation deleted");
} catch {
  print("Error deleting installation: \(error)")
}

Objective-C

[[FIRInstallations installations] deleteWithCompletion:^(NSError *error) {
   if (error != nil) {
     NSLog(@"Error deleting Installation %@", error);
     return;
   }
   NSLog(@"Installation deleted");
}];

Java

FirebaseInstallations.getInstance().delete()
        .addOnCompleteListener(new OnCompleteListener<Void>() {
    @Override
    public void onComplete(@NonNull Task<Void> task) {
        if (task.isSuccessful()) {
            Log.d("Installations", "Installation deleted");
        } else {
            Log.e("Installations", "Unable to delete Installation");
        }
    }
});

Kotlin

FirebaseInstallations.getInstance().delete().addOnCompleteListener { task ->
    if (task.isComplete) {
        Log.d("Installations", "Installation deleted")
    } else {
        Log.e("Installations", "Unable to delete Installation")
    }
}

JavaScript

await firebase.installations().delete();

Dart

await FirebaseInstallations.instance.delete();

Как удалить идентификатор экземпляра с помощью вызова API сервера

Чтобы удалить FID с помощью вызова API сервера, добавьте Firebase Admin SDK на сервер, если вы ещё этого не сделали.

После добавления SDK удалите идентификаторы установки, вызвав функцию удаления на нужном языке (кроме Node.js, в этих методах используются названия, относящиеся к идентификаторам экземпляра). Однако при вызове с любой текущей версией Firebase SDK они удаляют FID.

Node.js

// An FIDsent from a client service SDK
const idToDelete = 'eyJhbGciOiJFUzI1N_iIs5';

admin.installations().deleteInstallation(idToDelete);

Java

// An FID sent from a client service SDK
String idToDelete = "eyJhbGciOiJFUzI1N_iIs5";

FirebaseInstanceId.getInstance().deleteInstanceIdAsync(idToDelete).get();

Python

  from firebase_admin import instance_id

  # An FID sent from a client service SDK
  id_to_delete = 'eyJhbGciOiJFUzI1N_iIs5'

  instance_id.delete_instance_id(id_to_delete)

Проложить маршрут

client, err := app.InstanceId(ctx)
if err != nil {
  log.Fatalln("error initializing client", err)
}

iidToDelete := "eyJhbGciOiJFUzI1N_iIs5"
if err := client.DeleteInstanceId(ctx, iidToDelete); err != nil {
  log.Fatalln("error deleting FID", err)
}

Когда вы удаляете идентификатор установки Firebase с помощью вызова API сервера, сервисы Firebase запускают процесс удаления данных, связанных с этим идентификатором. В течение 1–2 дней они перестают принимать новые данные для этого идентификатора, а затем уведомляют клиентское приложение о том, что идентификатор был удален. Пока Firebase не уведомит клиентское приложение, некоторые сервисы приложения могут по-прежнему использовать идентификатор. Например, экземпляр Firebase может получать уведомления FCM в течение нескольких часов.

Если вы хотите удалить текущий идентификатор установки Firebase и сразу же начать использовать сервисы Firebase с новым, не связанным с ним идентификатором, воспользуйтесь клиентским API для удаления.

Как получить идентификаторы клиентов

Если вам нужно идентифицировать определенные установки приложения, вы можете получить идентификатор установки Firebase. Например, чтобы создать сегменты установок приложений для импорта в BigQuery или провести тестирование во время разработки Firebase In-App Messaging, вы можете идентифицировать и настроить таргетинг на нужные устройства с помощью соответствующих идентификаторов установки Firebase.

Чтобы получить идентификатор установки Firebase:

Swift

do {
  let id = try await Installations.installations().installationID()
  print("Installation ID: \(id)")
} catch {
  print("Error fetching id: \(error)")
}

Objective-C

[[FIRInstallations installations] installationIDWithCompletion:^(NSString *identifier, NSError *error) {
  if (error != nil) {
    NSLog(@"Error fetching Installation ID %@", error);
    return;
  }
  NSLog(@"Installation ID: %@", identifier);
}];

Java

FirebaseInstallations.getInstance().getId()
        .addOnCompleteListener(new OnCompleteListener<String>() {
    @Override
    public void onComplete(@NonNull Task<String> task) {
        if (task.isSuccessful()) {
            Log.d("Installations", "Installation ID: " + task.getResult());
        } else {
            Log.e("Installations", "Unable to get Installation ID");
        }
    }
});

Kotlin

FirebaseInstallations.getInstance().id.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        Log.d("Installations", "Installation ID: " + task.result)
    } else {
        Log.e("Installations", "Unable to get Installation ID")
    }
}

JavaScript

const installationId = await firebase.installations().getId();
console.log(installationId);

Dart

String id = await FirebaseInstallations.instance.getId();

Как получить токены аутентификации для установки

Сервисы Firebase могут аутентифицировать установки Firebase с помощью токенов аутентификации, полученных от FIS. Например, при разработке A/B-тестов для Remote Config можно аутентифицировать целевое тестовое устройство с помощью токена аутентификации установки.

Токен авторизации установки – это кратковременный токен носителя в формате веб-токена JSON (JWT), содержащий следующую информацию об установке:

  • Идентификатор установки Firebase
  • Связанный проект (projectNumber)
  • Идентификатор связанного приложения Firebase (appId).
  • Дата окончания срока действия токена.

Токен авторизации установки нельзя отозвать. Он будет действителен до даты окончания срока действия. Срок действия токена по умолчанию – одна неделя.

Чтобы получить токен авторизации для установки:

Swift

do {
  let result = try await Installations.installations()
    .authTokenForcingRefresh(true)
  print("Installation auth token: \(result.authToken)")
} catch {
  print("Error fetching token: \(error)")
}

Objective-C

[[FIRInstallations installations] authTokenForcingRefresh:true
                                               completion:^(FIRInstallationsAuthTokenResult *result, NSError *error) {
  if (error != nil) {
    NSLog(@"Error fetching Installation token %@", error);
    return;
  }
  NSLog(@"Installation auth token: %@", [result authToken]);
}];

Java

FirebaseInstallations.getInstance().getToken(/* forceRefresh */true)
        .addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
    @Override
    public void onComplete(@NonNull Task<InstallationTokenResult> task) {
        if (task.isSuccessful() && task.getResult() != null) {
            Log.d("Installations", "Installation auth token: " + task.getResult().getToken());
        } else {
            Log.e("Installations", "Unable to get Installation auth token");
        }
    }
});

Kotlin

val forceRefresh = true
FirebaseInstallations.getInstance().getToken(forceRefresh)
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            Log.d("Installations", "Installation auth token: " + task.result?.token)
        } else {
            Log.e("Installations", "Unable to get Installation auth token")
        }
    }

JavaScript

const installationToken = await firebase.installations()
    .getToken(/* forceRefresh */ true);
console.log(installationToken);

Dart

String token = await FirebaseInstallations.instance.getToken();

Как отслеживать жизненный цикл идентификатора установки Firebase

При нормальной работе приложения Firebaseидентификаторы установки Firebase (FID) не требуют специального мониторинга. Однако приложения, которые явно получают и используют идентификаторы экземпляра приложения, должны добавить логику для отслеживания возможного удаления или ротации идентификатора. Вот несколько случаев, когда идентификаторы FID могут быть удалены или заменены:

  • Удаление или повторная установка приложения, например когда конечный пользователь устанавливает его на новое устройство.
  • Конечный пользователь очищает кеш приложения или устройства.
  • Удаление FID запускается на сервере из-за бездействия приложения (в настоящее время пороговое значение составляет 270 дней бездействия).

Когда в приложении происходит смена или удаление FID в таких случаях, ему назначается новый FID. Кроме того, удаляется токен авторизации установки, связанный с удаленным идентификатором установки, независимо от срока его действия, и заменяется новым токеном авторизации установки.

Приложения могут отслеживать эти изменения и реагировать на них.

Как отслеживать ротацию FID

Swift

installationIDObserver = NotificationCenter.default.addObserver(
        forName: .InstallationIDDidChange,
        object: nil,
        queue: nil
) { (notification) in
  // Fetch new Installation ID
  Task {
    await self.fetchInstallationToken()
  }
}

Objective-C

__weak __auto_type weakSelf = self;
self.installationIDObserver = [[NSNotificationCenter defaultCenter]
        addObserverForName: FIRInstallationIDDidChangeNotification
                    object:nil
                     queue:nil
                usingBlock:^(NSNotification * _Nonnull notification) {
    // Fetch new Installation ID
    [weakSelf fetchInstallationsID];
}];

Уведомление NSNotification с названием NSNotificationName.InstallationIDDidChange отправляется в NSNotificationCenter по умолчанию при каждом назначении нового идентификатора FID.

Android

Клиенты Kotlin и Java должны добавить логику повторных попыток, чтобы отвечать на неудачные вызовы для получения нового FID.

JavaScript

Веб-приложения могут подписаться на хук onIdChange.

При создании нового FID вызывается зарегистрированная функция обратного вызова:

await firebase.installations().onIdChange((newId) => {
  console.log(newId);
  // TODO: Handle new installation ID.
});

Dart

FirebaseInstallations.instance.onIdChange.listen((token) {
  print('FID token: $token');
});

Переход с Instance ID на установку Firebase

До появления установок Firebase Firebase использовал для идентификации установок приложений SDK идентификатора экземпляра. Firebase установки надежнее, производительнее и безопаснее, чем идентификаторы экземпляра. Приложения Firebase, в которых используется Instance ID SDK, следует перевести на Firebase.

Процесс переноса зависит от типа приложения:

  • Приложения, которые не вызывают API идентификатора экземпляра напрямую, можно перенести, обновив версии SDK. Большинство приложений Firebase относятся к этой категории.

  • Приложения, которые явно вызывают API для идентификатора экземпляра, должны обновить версии SDK и изменить код, чтобы заменить методы идентификатора экземпляра на эквивалентные методы Firebase или FCM. Если ваше приложение использует идентификатор экземпляра для получения FCM регистрационных токенов или явным образом использует идентификатор экземпляра для таргетинга на экземпляры приложения или для любых других целей, вам нужно будет обновить код приложения.

В настоящее время FIS обратно совместим с устаревшим идентификатором Firebase Instance ID. Удаление идентификатора экземпляра – это альтернативный способ запросить удаление данных с помощью следующих SDK Firebase:

  • iOS 6.14.0 и более ранних версий
  • Android SDK, выпущенные до 27 февраля 2020 г.

Это означает, что приложениям не нужно переходить на установку Firebase, но мы настоятельно рекомендуем это сделать.

Переход на минимальные версии SDK для установок Firebase

Чтобы перейти с идентификатора экземпляра на Firebase, убедитесь, что в ваших приложениях используются указанные ниже минимальные версии Firebase SDK:

Firebase SDK Минимальная версия Android Минимальная версия iOS
Firebase Cloud Messaging v20.3.0 v6.34.0
Remote Config v19.2.0 v6.24.0
Google Аналитика для Firebase (SDK для сбора данных) v17.4.4 6.18.0
In-App Messaging v19.0.7 v6.24.0
Мониторинг производительности 19.0.8 Версия 6.21.0
Crashlytics v17.2.1 v6.23.0
ML Kit v22.1.2 v6.28.0

Как обновить код, в котором явно вызываются API идентификатора экземпляра

Если ваше приложение для Android или iOS напрямую использует методы Instance ID SDK, вы можете заменить их на идентичные альтернативы в Firebase installations SDK или FCM SDK.

Как получить идентификатор

Методы получения идентификаторов экземпляров заменены методами получения идентификаторов установок. Пример:

До

Swift

Messaging.messaging().token { token, error in
  if let error = error {
    print("Error fetching remote FCM registration token: \(error)")
  } else if let token = token {
    print("Remote instance ID token: \(token)")
  }
}

Objective-C

[[FIRMessaging messaging] tokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
  if (error != nil) {
    NSLog(@"Error fetching the remote FCM registration token: %@", error);
  } else {
    NSLog(@"Remote FCM registration token: %@", token);
    NSString* message =
      [NSString stringWithFormat:@"FCM registration token: %@", token];
    // display message
    NSLog(@"%@", message);
  }
}];

Java

FirebaseInstanceId.getInstance().getInstanceId()
        .addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
            @Override
            public void onComplete(@NonNull Task<InstanceIdResult> task) {
                Log.d("IID_TOKEN", task.getResult().getToken());
            }
        });

Kotlin

FirebaseInstanceId.getInstance().instanceId
        .addOnSuccessListener { result ->
            Log.d("IID_TOKEN", result.token)
        }

После

Swift

do {
  let id = try await Installations.installations().installationID()
  print("Installation ID: \(id)")
} catch {
  print("Error fetching id: \(error)")
}

Objective-C

[[FIRInstallations installations] installationIDWithCompletion:^(NSString *identifier, NSError *error) {
  if (error != nil) {
    NSLog(@"Error fetching Installation ID %@", error);
    return;
  }
  NSLog(@"Installation ID: %@", identifier);
}];

Java

FirebaseInstallations.getInstance().getId()
        .addOnCompleteListener(new OnCompleteListener<String>() {
    @Override
    public void onComplete(@NonNull Task<String> task) {
        if (task.isSuccessful()) {
            Log.d("Installations", "Installation ID: " + task.getResult());
        } else {
            Log.e("Installations", "Unable to get Installation ID");
        }
    }
});

Kotlin

FirebaseInstallations.getInstance().id.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        Log.d("Installations", "Installation ID: " + task.result)
    } else {
        Log.e("Installations", "Unable to get Installation ID")
    }
}

Как удалить идентификатор

Методы удаления идентификаторов экземпляров заменены методами удаления идентификаторов установки Firebase. Пример:

До

Swift

InstanceID.instanceID().deleteID { error in
  if let error = error {
    print("Error deleting instance ID: \(error)")
  }
}

Objective-C

[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError *error) {
  if error != nil {
    NSLog(@"Error deleting instance ID: %@", error);
  }
}];

Android

FirebaseInstanceId.deleteInstanceId();

После

Swift

func delete(completion: @escaping (Error?) -> Void)

Objective-C

- (void)deleteWithCompletion:(nonnull void (^)(NSError *_Nullable))completion;

Java

FirebaseInstallations.getInstance().delete()
        .addOnCompleteListener(new OnCompleteListener<Void>() {
    @Override
    public void onComplete(@NonNull Task<Void> task) {
        if (task.isSuccessful()) {
            Log.d("Installations", "Installation deleted");
        } else {
            Log.e("Installations", "Unable to delete Installation");
        }
    }
});

Kotlin

FirebaseInstallations.getInstance().delete().addOnCompleteListener { task ->
    if (task.isComplete) {
        Log.d("Installations", "Installation deleted")
    } else {
        Log.e("Installations", "Unable to delete Installation")
    }
}

Как получить токен регистрации FCM

До появления установок Firebase клиенты FCM получали токены регистрации из идентификатора экземпляра. Теперь в SDK FCM есть методы для получения токена регистрации.

До

Java

FirebaseInstanceId.getInstance().getInstanceId()
        .addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
            @Override
            public void onComplete(@NonNull Task<InstanceIdResult> task) {
                if (!task.isSuccessful()) {
                    Log.w(TAG, "getInstanceId failed", task.getException());
                    return;
                }

                // Get new Instance ID token
                String token = task.getResult().getToken();

                // Log and toast
                String msg = getString(R.string.msg_token_fmt, token);
                Log.d(TAG, msg);
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });

Kotlin

FirebaseInstanceId.getInstance().instanceId
        .addOnCompleteListener(OnCompleteListener { task ->
            if (!task.isSuccessful) {
                Log.w(TAG, "getInstanceId failed", task.exception)
                return@OnCompleteListener
            }

            // Get new Instance ID token
            val token = task.result?.token

            // Log and toast
            val msg = getString(R.string.msg_token_fmt, token)
            Log.d(TAG, msg)
            Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
        })

Swift

InstanceID.instanceID().instanceID { result, error in
  if let error = error {
    print("Error fetching instance ID: \(error)")
  } else if let result = result {
    print("Instance ID token: \(result.token)")
  }
}

Objective-C

[[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result,
                                                    NSError * _Nullable error) {
  if (error != nil) {
    NSLog(@"Error fetching instance ID: %@", error);
  } else {
    NSLog(@"Instance ID token: %@", result.token);
  }
}];

После

Java

FirebaseMessaging.getInstance().getToken()
    .addOnCompleteListener(new OnCompleteListener<String>() {
        @Override
        public void onComplete(@NonNull Task<String> task) {
          if (!task.isSuccessful()) {
            Log.w(TAG, "Fetching FCM registration token failed", task.getException());
            return;
          }

          // Get new FCM registration token
          String token = task.getResult();

          // Log and toast
          String msg = getString(R.string.msg_token_fmt, token);
          Log.d(TAG, msg);
          Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
        }
    });

Kotlin

FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
    if (!task.isSuccessful) {
        Log.w(TAG, "Fetching FCM registration token failed", task.exception)
        return@OnCompleteListener
    }

    // Get new FCM registration token
    val token = task.result

    // Log and toast
    val msg = getString(R.string.msg_token_fmt, token)
    Log.d(TAG, msg)
    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
})

Swift

Messaging.messaging().token { token, error in
  if let error = error {
    print("Error fetching remote FCM registration token: \(error)")
  } else if let token = token {
    print("Remote instance ID token: \(token)")
  }
}

Objective-C

[[FIRMessaging messaging] tokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
  if (error != nil) {
    NSLog(@"Error fetching the remote FCM registration token: %@", error);
  } else {
    NSLog(@"Remote FCM registration token: %@", token);
    NSString* message =
      [NSString stringWithFormat:@"FCM registration token: %@", token];
    // display message
    NSLog(@"%@", message);
  }
}];