Before you begin
Before you can use Realtime Database, 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.
Add the Firebase Unity SDK (specifically,
FirebaseDatabase.unitypackage
) to your Unity project.
Note that adding Firebase to your Unity project involves tasks both in the Firebase console 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. |
Get a DatabaseReference
To write data to the Database, you need an instance of 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; } }
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.