Save Data

Before you begin

Before you can use the Firebase Realtime Database, you will need to create a Firebase project, and add the Firebase Unity SDK packages to your Unity project.

Setup:

Prerequisites

Android

iOS

  • Unity 5.0 or later
  • Xcode 7.0 or later

If you don't have a Unity project already, you can download one of our quickstart samples and experiment with a specific Firebase feature. If you're using a quickstart, remember to get the bundle identifier from the project settings; you need it for the next step.

Set up your app in the Firebase console

To add Firebase to your app, you need a Firebase project and a Firebase configuration file for your app.

Create a Firebase project in the Firebase console if you don't already have one. If you already have an existing Google project associated with your mobile app, click Import Google Project. Otherwise, click Add project.

Android

  1. Click Add Firebase to your Android app and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just download the config file.
  2. When prompted, enter your app's package name. It's important to enter the package name your app is using; this can only be set when you add an app to your Firebase project.
  3. Download a google-services.json file when instructed. You can redownload this file again at any time.
  4. Copy this file to anywhere inside your project's assets folder.

IOs

  1. Click Add Firebase to your iOS app and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just download the config file.
  2. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project.
  3. Download a GoogleService-Info.plist file when instructed. You can redownload this file again at any time.
  4. Add the GoogleService-Info.plist file to the project.

  5. Drag the GoogleService-Info.plist downloaded from the Firebase console into any folder in the Unity project.

Add the Firebase Unity SDK to your app

  1. Download the Firebase Unity SDK.
  2. Select the Assets > Import Package > Custom Package menu item.
  3. Import the FirebaseDatabase.unitypackage package from the Firebase Unity SDK, downloaded previously.
  4. When the Import Unity Package window appears, click the Import button.

Build your app

Android

  1. Select the File > Build Settings menu option.
  2. Select Android in the Platform list.
  3. Click Switch Platform to select Android as the target platform.
  4. Wait for the spinner (compiling) icon in the bottom right corner of the Unity status bar to stop.
  5. Click Build and Run.

iOS

  1. Select the File > Build Settings menu option.
  2. Select iOS in the Platform list.
  3. Click Switch Platform to select iOS as the target platform.
  4. Wait for the spinner (compiling) icon in the bottom right corner of the Unity status bar to stop.
  5. Click Build and Run.

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.

Get a DatabaseReference

To write data to the Database, you need an instance of 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;
  }
}

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:

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:

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:

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. For example, a game might have a LeaderboardEntry class like this:

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:

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. 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:

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.

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

Send feedback about...

Need help? Visit our support page.