Propagar atualizações da Configuração remota em tempo real

Ao usar o acionamento de funções em segundo plano da Configuração remota fornecido pelo Cloud Functions para Firebase com o FCM, é possível propagar atualizações da Configuração remota em tempo real. Nesse cenário, você cria uma função que é acionada ao publicar ou reverter o modelo do Configuração remota no painel ou na API. A atualização do modelo aciona a função para enviar uma mensagem do FCM que informa os clientes que a configuração existente está desatualizada e que a próxima busca deve ser realizada no servidor.

Diagrama mostrando uma atualização da Configuração remota acionando alertas do FCM pelo Cloud Functions

Na outra parte deste documento, você vai conferir as etapas para propagar as atualizações da Configuração remota em tempo real.

Inscrever instâncias de apps cliente em um tópico do FCM

Se você quiser segmentar uma mensagem do FCM para um grande grupo de instâncias de aplicativos cliente, como toda sua base de usuários, o envio de mensagens por tópico é o mecanismo mais eficiente. Todas as instâncias de app que devem receber atualizações em tempo real da Configuração remota precisam se inscrever em um nome de tópico, como PUSH_RC:

Swift

extension AppDelegate : MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
        messaging.subscribe(toTopic: "PUSH_RC") { error in
            print("Subscribed to PUSH_RC topic")
        }
    }
}
    

Objective-C

- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
    [[FIRMessaging messaging] subscribeToTopic:@"PUSH_RC" completion:^(NSError * _Nullable error) {
        NSLog(@"Subscribed to PUSH_RC topic");
    }];
}
    

Android

@Override
public void onNewToken(String s) {
    FirebaseMessaging.getInstance().subscribeToTopic("PUSH_RC");
}
    

Criar uma função para enviar um ping do FCM nas atualizações de modelos

Você pode acionar uma função em resposta a eventos da Configuração remota, incluindo a publicação de uma nova versão de configuração ou a reversão para uma versão mais antiga. Para propagar atualizações de modelos em tempo real, crie uma função que detecte eventos de publicação de modelos e, em seguida, use o SDK Admin do FCM da sua função para enviar um ping silencioso a instâncias de apps cliente:

exports.pushConfig = functions.remoteConfig.onUpdate(versionMetadata => {
  // Create FCM payload to send data message to PUSH_RC topic.
  const payload = {
    topic: "PUSH_RC",
    data: {
      "CONFIG_STATE": "STALE"
    }
  };
  // Use the Admin SDK to send the ping via FCM.
  return admin.messaging().send(payload).then(resp => {
    console.log(resp);
    return null;
  });
});

Essa função configura um parâmetro CONFIG_STATE e, em seguida, o envia como o payload de dados de uma mensagem do FCM para todos os clientes inscritos no tópico PUSH_RC.

Definir o estado da Configuração remota no cliente

O payload de dados mostrado na etapa anterior sempre define CONFIG_STATE como STALE nas preferências compartilhadas do aplicativo. Isso indica que o modelo da Configuração remota armazenado no aplicativo agora está desatualizado devido à criação do novo modelo atualizado, e que a publicação desse modelo acionou a função. Atualize seu gerenciador de notificações para testar essa condição:

Swift

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

    if (userInfo.index(forKey: "CONFIG_STATE") != nil) {
        print("Config set to stale")
        UserDefaults.standard.set(true, forKey:"CONFIG_STALE")
    }

    completionHandler(UIBackgroundFetchResult.newData)
}
    

Objective-C

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    if (userInfo[@"CONFIG_STATE"]) {
        NSLog(@"Config set to stale");
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"CONFIG_STALE"];
    }

    completionHandler(UIBackgroundFetchResultNewData);
}
    

Android

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    if (remoteMessage.getData().containsKey("CONFIG_STATE")) {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        sharedPreferences.edit().putBoolean("CONFIG_STALE", true).apply();
    }
}
    

Buscar as atualizações da Configuração remota na inicialização do app

Swift

func fetchConfig() {
  welcomeLabel.text = remoteConfig[loadingPhraseConfigKey].stringValue

  var expirationDuration = 3600
  // If your app is using developer mode, expirationDuration is set to 0, so each fetch will
  // retrieve values from the service.
  if remoteConfig.configSettings.isDeveloperModeEnabled || UserDefaults.standard.bool(forKey: "CONFIG_STALE") {
    expirationDuration = 0
  }

  remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
    if status == .success {
      print("Config fetched!")
      self.remoteConfig.activateFetched()
    } else {
      print("Config not fetched")
      print("Error: \(error?.localizedDescription ?? "No error available.")")
    }
    self.displayWelcome()
  }
}
    

Objective-C

- (void)fetchConfig {
    self.welcomeLabel.text = self.remoteConfig[kLoadingPhraseConfigKey].stringValue;

    long expirationDuration = 3600;
    // If your app is using developer mode, expirationDuration is set to 0, so each fetch will
    // retrieve values from the Remote Config service.
    if (self.remoteConfig.configSettings.isDeveloperModeEnabled || [[NSUserDefaults standardUserDefaults] boolForKey:@"CONFIG_STALE"]) {
        expirationDuration = 0;
    }

    [self.remoteConfig fetchWithExpirationDuration:expirationDuration completionHandler:^(FIRRemoteConfigFetchStatus status, NSError *error) {
        if (status == FIRRemoteConfigFetchStatusSuccess) {
            NSLog(@"Config fetched!");
            [self.remoteConfig activateFetched];
            [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"CONFIG_STALE"];
        } else {
            NSLog(@"Config not fetched");
            NSLog(@"Error %@", error.localizedDescription);
        }
        [self displayWelcome];
    }];
}
    

Android

private void fetchWelcomeMessage() {
    mWelcomeTextView.setText(mFirebaseRemoteConfig.getString("loading_phrase"));

    long cacheExpiration = 43200; // 12 hours in seconds.
    // If your app is using developer mode or cache is stale, cacheExpiration is set to 0,
    // so each fetch will retrieve values from the service.
    if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled() ||
            mSharedPreferences.getBoolean("CONFIG_STALE", false)) {
        cacheExpiration = 0;
    }

    mFirebaseRemoteConfig.fetch(cacheExpiration)
            .addOnCompleteListener(this, new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if (task.isSuccessful()) {
                        Toast.makeText(MainActivity.this, "Fetch Succeeded",
                                Toast.LENGTH_SHORT).show();

                        // After config data is successfully fetched, it must be activated before newly fetched
                        // values are returned.
                        mFirebaseRemoteConfig.activateFetched();
                    } else {
                        Toast.makeText(MainActivity.this, "Fetch Failed",
                                Toast.LENGTH_SHORT).show();
                    }
                    mWelcomeTextView.setText(mFirebaseRemoteConfig.getString("welcome_message"));
                }
            });
}
    

Por fim, adicione lógica ao seu app para aplicar a busca da Configuração remota da rede (ignorando o armazenamento local) porque CONFIG_STATE é STALE. Se o app busca na rede com muita frequência, ele pode ser limitado pelo Firebase. Consulte Limitação.