Salvar dados

Antes de começar

Antes de usar o Realtime Database, você precisa:

  • registrar seu projeto do Unity e configurá-lo para usar o Firebase.

    • Se o projeto do Unity já usa o Firebase, ele já está registrado e configurado para o Firebase.

    • Se você não tiver um projeto do Unity, faça o download de um app de amostra.

  • Adicione o SDK do Firebase para Unity (especificamente, FirebaseDatabase.unitypackage) ao seu projeto do Unity.

Adicionar o Firebase ao seu projeto do Unity envolve tarefas no Console do Firebase e no projeto aberto do Unity. Por exemplo, fazer o download dos arquivos de configuração do Firebase no console e movê-los para o projeto do Unity.

Salvar dados

Há cinco métodos para escrever dados no Firebase Realtime Database:

Método Usos comuns
SetValueAsync() Grava ou substitui dados em um caminho definido, como users/<user-id>/<username>.
SetRawJsonValueAsync() Grava ou substitui dados com Json bruto, como users/<user-id>/<username>.
Push() Adiciona a uma lista de dados. Toda vez que você chama Push(), o Firebase gera uma chave exclusiva que também pode ser usada como um identificador exclusivo, como user-scores/<user-id>/<unique-score-id>.
UpdateChildrenAsync() Atualiza algumas das chaves de um caminho definido sem substituir todos os dados.
RunTransaction() Atualiza dados complexos que possam ser corrompidos por atualizações simultâneas.

Receber um DatabaseReference

Para gravar dados no Database, você precisa de uma instância de DatabaseReference:

using Firebase;
using Firebase.Database;
using Firebase.Unity.Editor;

public class MyScript: MonoBehaviour {
  void Start() {
    // Set up the Editor before calling into the realtime database.
    FirebaseApp.DefaultInstance.SetEditorDatabaseUrl("https://YOUR-FIREBASE-APP.firebaseio.com/");

    // Get the root reference location of the database.
    DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
  }
}

Gravar, atualizar ou excluir dados em uma referência

Operações básicas de gravação

Em operações básicas de gravação, use SetValueAsync() para salvar dados em uma referência específica e substitua os dados existentes no caminho. Você pode usar este método para transmitir tipos que correspondem aos tipos de JSON abaixo:

  • string
  • long
  • double
  • bool
  • Dictionary<string, Object>
  • List<Object>

Se você usar um objeto do tipo C#, poderá usar o JsonUtility.ToJson() integrado para converter o objeto em Json bruto e chamar SetRawJsonValueAsync(). Por exemplo, é possível que você tenha uma classe de usuário como a descrita a seguir:

public class User {
    public string username;
    public string email;

    public User() {
    }

    public User(string username, string email) {
        this.username = username;
        this.email = email;
    }
}

É possível adicionar um usuário com SetRawJsonValueAsync() da seguinte maneira:

private void writeNewUser(string userId, string name, string email) {
    User user = new User(name, email);
    string json = JsonUtility.ToJson(user);

    mDatabaseRef.Child("users").Child(userId).SetRawJsonValueAsync(json);
}

O uso de SetValueAsync() ou SetRawJsonValueAsync() dessa maneira substitui os dados no local especificado, incluindo qualquer nó filho. No entanto, ainda é possível atualizar um node filho sem reescrever todo o objeto. Para permitir que os usuários atualizem os próprios perfis, atualize o nome de usuário deles desta forma:

mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);

Anexar a uma lista de dados

Use o método Push() para anexar dados a uma lista em aplicativos de vários usuários. O método Push() gera uma chave exclusiva sempre que um novo filho é adicionado a uma referência específica do Firebase. Ao usar essas chaves geradas automaticamente para cada novo elemento da lista, é possível que vários clientes adicionem filhos ao mesmo local simultaneamente sem criar conflitos de gravação. A chave exclusiva gerada por Push()é baseada em um carimbo de data/hora. Portanto, os itens da lista são organizados automaticamente em ordem cronológica.

Use a referência aos novos dados retornados pelo método Push() para receber o valor da chave filha que foi gerada automaticamente ou para definir dados para o filho. Chamar Key em uma referência Push() retorna o valor da chave gerada automaticamente.

Atualizar campos específicos

Para gravar simultaneamente filhos específicos de um nó sem substituir outros nós filhos, use o método UpdateChildrenAsync().

Ao chamar UpdateChildrenAsync(), atualize valores de filhos de nível inferior ao especificar um caminho para a chave. Se os dados estiverem armazenados em vários locais para aprimorar a escalabilidade, atualize todas as instâncias usando a distribuição de dados. Por exemplo, um jogo pode ter uma classe LeaderboardEntry como esta:

public class LeaderboardEntry {
    public string uid;
    public int score = 0;

    public LeaderboardEntry() {
    }

    public LeaderboardEntry(string uid, int score) {
        this.uid = uid;
        this.score = score;
    }

    public Dictionary<string, Object> ToDictionary() {
        Dictionary<string, Object> result = new Dictionary<string, Object>();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

Para criar uma LeaderboardEntry e atualizá-la simultaneamente no feed de pontuação recente e na lista de pontuação do usuário, o jogo usa o seguinte código:

private void WriteNewScore(string userId, int score) {
    // Create new entry at /user-scores/$userid/$scoreid and at
    // /leaderboard/$scoreid simultaneously
    string key = mDatabase.Child("scores").Push().Key;
    LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
    Dictionary<string, Object> entryValues = entry.ToDictionary();

    Dictionary<string, Object> childUpdates = new Dictionary<string, Object>();
    childUpdates["/scores/" + key] = entryValues;
    childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

    mDatabase.UpdateChildrenAsync(childUpdates);
}

Esse exemplo usa Push() para criar uma entrada no nó que contém entradas para todos os usuários em /scores/$key e simultaneamente recuperar a chave com Key. A chave pode ser usada para criar uma segunda entrada nas pontuações do usuário em /user-scores/$userid/$key.

Com esses caminhos, você faz atualizações simultâneas em vários locais da árvore JSON com uma única chamada ao UpdateChildrenAsync(), da mesma forma que esse exemplo cria a nova entrada nos dois locais. Atualizações simultâneas realizadas dessa maneira são atômicas: ou todas funcionam ou todas falham.

Excluir dados

A maneira mais simples de excluir os dados é chamar RemoveValue() em uma referência ao local desses dados.

Também é possível fazer a exclusão ao especificar null como o valor de outra operação de gravação, como SetValueAsync() ou UpdateChildrenAsync(). Use essa técnica com UpdateChildrenAsync() para excluir vários filhos em uma única chamada de API.

Como saber se os seus dados foram confirmados

Para saber se os seus dados foram confirmados no servidor do Firebase Realtime Database, você pode adicionar uma continuação. Ambos SetValueAsync() e UpdateChildrenAsync() retornam um Task que permite saber quando a operação é concluída. Se, por qualquer motivo, a chamada falhar, as Tarefas IsFaulted serão verdadeiras e a propriedade Exception indicará a causa da falha.

Salvar dados como transações

Para tratar dados corrompidos por modificações simultâneas, como contadores incrementais, use uma operação de transação. Você define Func nessa operação. A função de atualização Func usa o estado atual dos dados como um argumento e retorna o novo estado de acordo com as preferências de gravação. Se outro cliente fizer uma gravação no local antes que seu novo valor seja gravado, sua função de atualização será chamada novamente com o novo valor atual e a gravação será repetida.

Por exemplo, você pode permitir que os usuários atualizem uma tabela de classificação em um jogo com as cinco pontuações mais altas:

private void AddScoreToLeaders(string email,
                               long score,
                               DatabaseReference leaderBoardRef) {

    leaderBoardRef.RunTransaction(mutableData => {
      List<object> leaders = mutableData.Value as List<object>

      if (leaders == null) {
        leaders = new List<object>();
      } else if (mutableData.ChildrenCount >= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary<string, object>)) continue;
          long childScore = (long)
                      ((Dictionary<string, object>)child)["score"];
          if (childScore < minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore > score) {
          // The new score is lower than the existing 5 scores, abort.
          return TransactionResult.Abort();
        }

        // Remove the lowest score.
        leaders.Remove(minVal);
      }

      // Add the new high score.
      Dictionary<string, object> newScoreMap =
                       new Dictionary<string, object>();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

Usar uma transação evita que a tabela de classificação fique incorreta caso vários usuários gravem resultados ao mesmo tempo ou o cliente tenha dados desatualizados. Se a transação for rejeitada, o servidor retornará o valor atual ao cliente, que executará a transação novamente com o valor atualizado. Isso se repetirá até que a transação seja aceita ou até que muitas tentativas sejam realizadas.

Gravar dados off-line

Se um cliente perder a conexão de rede, o app continuará funcionando.

Todos os clientes conectados a um banco de dados do Firebase mantêm a própria versão interna de dados ativos. A gravação deles ocorre primeiro nessa versão local. Depois, o cliente do Firebase sincroniza esses dados com os servidores remotos e com outros clientes de acordo com o modelo “melhor esforço".

Consequentemente, todas as gravações no banco de dados acionam eventos locais, antes de qualquer dado ser gravado no servidor, e o app continua responsivo, independentemente da conectividade ou da latência da rede.

Para que a conectividade seja restabelecida, seu app recebe o conjunto apropriado de eventos, e o cliente faz a sincronização com o estado atual do servidor, sem precisar de um código personalizado.

Próximas etapas