Guardar datos

Antes de comenzar

Antes de poder usar Realtime Database, tendrás que hacer lo siguiente:

  • Registra tu proyecto de Unity y configúralo para usar Firebase.

    • Si tu proyecto de Unity ya usa Firebase, significa que ya está registrado y configurado para Firebase.

    • Si aún no tienes un proyecto de Unity, puedes descargar una app de ejemplo.

  • Agrega el SDK de Firebase Unity (específicamente, FirebaseDatabase.unitypackage) a tu proyecto de Unity.

Ten en cuenta que agregar Firebase a tu proyecto de Unity implica realizar tareas en Firebase console y en tu proyecto abierto de Unity (por ejemplo, descargar archivos de configuración de Firebase desde la consola y moverlos a tu proyecto de Unity).

Guarda datos

Existen cinco métodos para escribir datos en Firebase Realtime Database:

Método Usos comunes
SetValueAsync() Escribir o reemplazar datos en una ruta definida, como users/<user-id>/<username>.
SetRawJsonValueAsync() Escribir o reemplazar datos con JSON sin procesar, como users/<user-id>/<username>.
Push() Agregar a una lista de datos. Cada vez que llamas a Push(), Firebase genera una clave única que también se puede usar como identificador único, por ejemplo user-scores/<user-id>/<unique-score-id>.
UpdateChildrenAsync() Actualizar algunas de las claves de una ruta de acceso definida sin reemplazar todos los datos.
RunTransaction() Actualizar datos complejos que pueden dañarse con actualizaciones simultáneas.

Obtén una DatabaseReference

Para escribir en la base de datos, necesitas una instancia de DatabaseReference:

using Firebase;
using Firebase.Database;

public class MyScript: MonoBehaviour {
  void Start() {
    // Get the root reference location of the database.
    DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
  }
}

Escribe, actualiza o borra datos de una referencia

Operaciones básicas de escritura

Si quieres ejecutar operaciones básicas de escritura, puedes usar SetValueAsync() para guardar datos en una referencia específica y reemplazar todos los datos de esa ruta. Puedes usar este método para pasar tipos que corresponden a los JSON disponibles de la siguiente manera:

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

Si usas un objeto C#, puedes utilizar el elemento JsonUtility.ToJson() integrado para convertir el objeto en JSON sin procesar y llamar a SetRawJsonValueAsync(). Por ejemplo, puedes tener una clase User similar a la siguiente:

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

    public User() {
    }

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

Puedes agregar un usuario con SetRawJsonValueAsync(), como se muestra a continuación:

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);
}

Si usas SetValueAsync() o SetRawJsonValueAsync() de esta manera, se reemplazan los datos en la ubicación especificada, incluidos los nodos secundarios. Sin embargo, puedes actualizar un elemento secundario sin volver a escribir el objeto completo. Si quieres permitir que los usuarios actualicen sus perfiles, puedes actualizar el nombre de usuario de la siguiente forma:

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

Agrega datos a una lista de datos

Usa el método Push() para agregar datos a una lista en aplicaciones multiusuario. Cada vez que se agrega un elemento secundario nuevo a la referencia de Firebase especificada, el método Push() genera una clave única. Cuando se usan estas claves generadas de forma automática para cada elemento nuevo de la lista, varios clientes pueden agregar elementos secundarios a la misma ubicación, al mismo tiempo y sin conflictos de escritura. La clave única que genera Push() se basa en una marca de tiempo. Por lo tanto, los elementos de las listas se ordenan cronológicamente de forma automática.

Puedes usar la referencia a los datos nuevos que muestra el método Push() para obtener el valor de la clave generada automáticamente del elemento secundario o configurar los datos de dicho elemento. Si llamas a Key en una referencia Push(), se muestra la clave generada de forma automática.

Actualiza campos específicos

Para escribir de forma simultánea en elementos secundarios específicos de un nodo sin reemplazar otros nodos secundarios, usa el método UpdateChildrenAsync().

Cuando llamas a UpdateChildrenAsync(), puedes especificar una ruta de acceso de la clave para actualizar valores secundarios de nivel inferior. Si se almacenan datos en varias ubicaciones para obtener un mejor escalamiento, puedes actualizar todas las instancias de esos datos mediante fan-out de datos. Por ejemplo, un juego puede tener una clase LeaderboardEntry como la siguiente:

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 crear una LeaderboardEntry y actualizarla de forma simultánea en el feed de puntuación reciente y en la lista de puntuaciones propia del usuario, el juego usa el siguiente 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);
}

En este ejemplo, se usa Push() para crear una entrada en el nodo que contiene las entradas de todos los usuarios en /scores/$key y recuperar la clave con Key de manera simultánea. Luego, la clave puede usarse para crear una segunda entrada en las puntuaciones del usuario en /user-scores/$userid/$key.

Con estas rutas de acceso, puedes ejecutar actualizaciones simultáneas en varias ubicaciones del árbol JSON con una única llamada a UpdateChildrenAsync(), de manera similar a este ejemplo en el que se crea la entrada nueva en ambas ubicaciones. Las actualizaciones simultáneas que se ejecutan de esta forma son atómicas, es decir, todas se aplican correctamente o todas fallan.

Borra datos

La forma más sencilla de borrar datos es llamar a RemoveValue() en una referencia a la ubicación de los datos.

También puedes borrar datos si especificas null como el valor de otra operación de escritura, como SetValueAsync() o UpdateChildrenAsync(). Puedes usar esta técnica con UpdateChildrenAsync() para borrar varios objetos secundarios con una sola llamada a la API.

Conoce cuándo se envían los datos

Para saber cuándo se confirman los datos en el servidor de Firebase Realtime Database, puedes agregar una continuación. Tanto SetValueAsync() como UpdateChildrenAsync() muestran la tarea Task, que permite saber cuándo se completa la operación. Si por algún motivo, la llamada no funciona correctamente, la propiedad IsFaulted de la tarea tendrá el valor True, y la propiedad Exception indicará el motivo del error.

Guarda datos como transacciones

Cuando trabajas con datos que se podrían dañar si se hacen cambios simultáneos (por ejemplo, contadores incrementales) puedes usar una operación de transacción. Le debes asignar una Func. Esta Func de actualización toma el estado actual de los datos como argumento y muestra el nuevo estado que deseas escribir. Si otro cliente escribe en la ubicación antes de que se escriba de manera correcta el valor nuevo, se vuelve a llamar la función de actualización con el nuevo valor actual y se intenta nuevamente la operación de escritura.

Por ejemplo, en un juego, podrías permitir que los usuarios actualicen una tabla de clasificación con las cinco puntuaciones más 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);
    });
}

Si usas una transacción, impides que la tabla de clasificación sea incorrecta en caso de que varios usuarios registren puntuaciones al mismo tiempo o el cliente tenga datos inactivos. Si se rechaza la transacción, el servidor le muestra el valor actual al cliente, que vuelve a ejecutar la transacción con el valor actualizado. Esto se repite hasta que se acepte la transacción o hasta que se registren demasiados intentos.

Escribe datos sin conexión

Si un cliente pierde la conexión de red, la app continúa funcionando de manera correcta.

Todos los clientes conectados a una base de datos de Firebase mantienen su propia versión interna de los datos activos. Cuando se escriben datos, se hace primero en esta versión local. Después, el cliente de Firebase sincroniza esos datos con los servidores de bases de datos remotas y con otros clientes según el “mejor esfuerzo”.

Como resultado, todas las operaciones de escritura en la base de datos activan eventos locales al instante, antes de que se escriban datos en el servidor. Esto significa que la app conserva la capacidad de respuesta, sin importar la latencia o el estado de conexión de la red.

Cuando se restablece la conectividad, la app recibe el conjunto de eventos adecuado, de manera que el cliente se sincroniza con el estado actual del servidor sin tener que escribir código personalizado.

Próximos pasos