## Before you begin

Before you can use
[Realtime Database](https://firebase.google.com/docs/reference/unity/namespace/firebase/database.md.txt),
you need to:

- Register your Unity project and configure it to use Firebase.

  - If your Unity project already uses Firebase, then it's already
    registered and configured for Firebase.

  - If you don't have a Unity project, you can download a
    [sample app](https://github.com/google/mechahamster).

- Add the [Firebase Unity SDK](https://firebase.google.com/download/unity) (specifically, `FirebaseDatabase.unitypackage`) to
  your Unity project.

> [!NOTE]
> **Find detailed instructions for these initial
> setup tasks in
> [Add Firebase to your Unity project](https://firebase.google.com/docs/unity/setup.md.txt#prerequisites).**

Note that adding Firebase to your Unity project involves tasks both in the
[Firebase console](https://console.firebase.google.com/) and in your open Unity project
(for example, you download Firebase config files from the console, then move
them into your Unity project).

## Saving Data

There are five methods for writing data to the Firebase Realtime Database:

| Method | Common uses |
|---|---|
| `SetValueAsync()` | Write or replace data to a defined path, such as `users/<user-id>/<username>`. |
| `SetRawJsonValueAsync()` | Write or replace data with raw Json, such as `users/<user-id>/<username>`. |
| `Push()` | Add to a list of data. Every time you call `Push()`, Firebase generates a unique key that can also be used as a unique identifier, such as `user-scores/<user-id>/<unique-score-id>`. |
| `UpdateChildrenAsync()` | Update some of the keys for a defined path without replacing all of the data. |
| `RunTransaction()` | Update complex data that could be corrupted by concurrent updates. |

> [!NOTE]
> **Note:** By default, read and write access to your database is restricted so only authenticated users can read or write data. To get started without setting up [Authentication](https://firebase.google.com/docs/auth.md.txt), you can [configure your rules for public access](https://firebase.google.com/docs/rules/basics.md.txt#default_rules_locked_mode). This does make your database open to anyone, even people not using your app, so be sure to restrict your database again when you set up authentication.

## Get a DatabaseReference

To write data to the Database, you need an instance of `DatabaseReference`:

```c#
using Firebase;
using Firebase.Database;

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

## Write, update, or delete data at a reference

### Basic write operations

For basic write operations, you can use `SetValueAsync()` to save data to a
specified reference, replacing any existing data at that path. You can use this
method to pass types that correspond to the available JSON types as follows:

- `string`
- `long`
- `double`
- `bool`
- `Dictionary<string, Object>`
- `List<Object>`

If you use a typed C# object, you can use the built in `JsonUtility.ToJson()`
to convert the object to raw Json and call `SetRawJsonValueAsync()`.
For example, you may have a User class that looked as follows:

```c#
public class User {
    public string username;
    public string email;

    public User() {
    }

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

You can add a user with `SetRawJsonValueAsync()` as follows:

```c#
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);
}
```

Using `SetValueAsync()` or `SetRawJsonValueAsync()` in this way overwrites data
at the specified location, including any child nodes. However, you can still
update a child without rewriting the entire object. If you want to allow users
to update their profiles you could update the username as follows:

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

### Append to a list of data

Use the `Push()` method to append data to a list in multiuser applications.
The `Push()` method generates a unique key every time a new
child is added to the specified Firebase reference. By using these
auto-generated keys for each new element in the list, several clients can
add children to the same location at the same time without write conflicts. The
unique key generated by `Push()` is based on a timestamp, so list items are
automatically ordered chronologically.

You can use the reference to the new data returned by the `Push()` method to get
the value of the child's auto-generated key or set data for the child. Calling
`Key` on a `Push()` reference returns the value of the
auto-generated key.

### Update specific fields

To simultaneously write to specific children of a node without overwriting other
child nodes, use the `UpdateChildrenAsync()` method.

<>
When calling `UpdateChildrenAsync()`, you can update lower-level child values by
specifying a path for the key. If data is stored in multiple locations to scale
better, you can update all instances of that data using
[data fan-out](https://firebase.google.com/docs/database/android/structure-data.md.txt#fanout). For example, a
game might have a `LeaderboardEntry` class like this:

```c#
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;
    }
}
```

To create a LeaderboardEntry and simultaneously update it to the recent score
feed and the user's own score list, the game uses code like this:

```c#
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);
}
```

This example uses `Push()` to create an entry in the node containing entries for
all users at `/scores/$key` and simultaneously retrieve the key with
`Key`. The key can then be used to create a second entry in the user's
scores at `/user-scores/$userid/$key`.

Using these paths, you can perform simultaneous updates to multiple locations in
the JSON tree with a single call to `UpdateChildrenAsync()`, such as how this
example creates the new entry in both locations. Simultaneous updates made this
way are atomic: either all updates succeed or all updates fail.

### Delete data

The simplest way to delete data is to call `RemoveValue()` on a reference to the
location of that data.

You can also delete by specifying `null` as the value for another write
operation such as `SetValueAsync()` or `UpdateChildrenAsync()`. You can use this
technique with `UpdateChildrenAsync()` to delete multiple children in a single API
call.

### Know when your data is committed.

To know when your data is committed to the Firebase Realtime Database server, you
can add a continuation. Both `SetValueAsync()` and `UpdateChildrenAsync()`
return a `Task` that allows you to know when the operation is complete. If the
call is unsuccessful for any reason, the Tasks `IsFaulted` will be true with the
`Exception` property indicating why the failure occurred.

## Save data as transactions

When working with data that could be corrupted by concurrent
modifications, such as incremental counters, you can use a
[transaction operation](https://firebase.google.com/docs/reference/unity/class/firebase/database/transaction-result.md.txt).
You give this operation a `Func`. This update `Func` takes the current state
of the data as an argument and returns the new desired state you would like to
write. If another client writes to the location before your new value is
successfully written, your update function is called again with the new current
value, and the write is retried.

For instance, in a game you could allow users to update a leaderboard with
the five highest scores:

```c#
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);
    });
}
```

Using a transaction prevents the leaderboard from being incorrect if multiple
users record scores at the same time or the client had stale data. If the
transaction is rejected, the server returns the current value to the client,
which runs the transaction again with the updated value. This repeats until the
transaction is accepted or too many attempts have been made.

> [!NOTE]
> **Note:** Because `RunTransaction()` is called multiple times, it must be able to handle `null` data. Even if there is existing data in your remote database, it may not be locally cached when the transaction function is run, resulting in `null` for the initial value.

## Write data offline

If a client loses its network connection, your app will continue functioning
correctly.

Every client connected to a Firebase database maintains its own internal version
of any active data. When data is written, it's written to this local version
first. The Firebase client then synchronizes that data with the remote database
servers and with other clients on a "best-effort" basis.

As a result, all writes to the database trigger local events immediately, before
any data is written to the server. This means your app remains
responsive regardless of network latency or connectivity.

Once connectivity is reestablished, your app receives the appropriate set of
events so that the client syncs with the current server state, without having to
write any custom code.

## Next Steps

- [Retrieve data from your Realtime Database](https://firebase.google.com/docs/database/unity/retrieve-data.md.txt)
- [Learn how to structure data](https://firebase.google.com/docs/database/unity/structure-data.md.txt)