0
votes

I am attempting to pull data from my Realtime Database in Firebase and then put that string into a TMP_Text field in unity.

There seems to be an issue with writing to a text field while within the .ContinueWith(task => portion of my code. While inside the task.isCompleted I can get the snapshot print the value after converting it to string, however, as soon as I try to write to the text field the code haults. In the example below the output is as follows.

  1. Prints the snapshot, both key and value
  2. Prints the value of the snapshot as a string
  3. No errors no thing happens
  4. The 4th print of Value does not print at all
public void PopulateScoreBoard()
    {
        FirebaseDatabase.DefaultInstance.GetReference("userlist").Child(LoadingScreen.userID).Child("score").GetValueAsync().ContinueWith(task =>
        {
            if (task.IsFaulted)
            {
                //handle error
                scoreboard.text = "Error!";
            }
            else if (task.IsCompleted)
            {
                //get snapshot fo data
                DataSnapshot snapshot = task.Result;
                print(snapshot);
                print(snapshot.Value.ToString());

                //Get the scoreboard value within the result snapshot
                scoreboard.text = snapshot.Value.ToString();

                //print the same value to ensure it is working
                print(snapshot.Value.ToString());
            }
        });
    }

Looking forward to getting some input, I'm assuming there is some reason you cannot write to anything in this, however, I cannot find it. If this is the case what is my alternative?

*Update: It seems as though after further testing you cannot set anything equal to anything inside the ContinueWith(task => is that normal?

2

2 Answers

0
votes

I was able to find an answer but I would still love some input to see if I am doing it right.

I changed to this code and it seems to be working fine.

public void loadData()
    {
        FirebaseDatabase.DefaultInstance.GetReference("userlist").ValueChanged += Script_ValueChanged;
    }

    private void Script_ValueChanged(object sender, ValueChangedEventArgs e)
    {
        scoreboard.text = e.Snapshot.Child(LoadingScreen.userID).Child("userScore").GetValue(true).ToString();
    }

And then called it at start.

0
votes

First, I think you could fix your initial issue by replacing ContinueWith with ContinueWithOnMainThread:

public void PopulateScoreBoard()
    {
        FirebaseDatabase.DefaultInstance.GetReference("userlist").Child(LoadingScreen.userID).Child("score").GetValueAsync().ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted)
            {
                //handle error
                scoreboard.text = "Error!";
            }
            else if (task.IsCompleted)
            {
                //get snapshot fo data
                DataSnapshot snapshot = task.Result;
                print(snapshot);
                print(snapshot.Value.ToString());

                //Get the scoreboard value within the result snapshot
                scoreboard.text = snapshot.Value.ToString();

                //print the same value to ensure it is working
                print(snapshot.Value.ToString());
            }
        });
    }

The reason is that ContinueWith will return in the background. Then you perform the non-thread-safe operation scoreboard.text = "Error!";, which will cause something in the range of an exception to a crash. There's an article I wrote about this here.

Now, your answer is more correct. GetDataAsync will register a ValueChanged listener, wait for a result, and then unregister this listener. The problem is that this means that if you have stale data in your cache, your GetValueAsync will be the last value retrieved from the Firebase backend rather than the latest data.

Looking at your code, the only thing you should do different is cache the "userlist" reference and unregister later. This has to do with how Unity handles memory and object lifetime - it would be possible in your code for the text to be set on a null object:

private DatabaseReference _userListReference;

void Start() {
    _userListReference = FirebaseDatabase.DefaultInstance.GetReference("userlist");
    _userListReference.ValueChanged += Script_ValueChanged;
}

void OnDestroy() {
    _userListReference.ValueChanged -= Script_ValueChanged;
}

private void Script_ValueChanged(object sender, ValueChangedEventArgs e)
{
    scoreboard.text = e.Snapshot.Child(LoadingScreen.userID).Child("userScore").GetValue(true).ToString();
}

Due to a current bug, you MUST cache the reference to remove the event listener. If you try to retrieve it again with GetReference, you'll make a second C# copy of the underlying object and the event listener will live on.