Retrieve complex data from Firestore using Unity + Firestore SDK for your mobile app (2022)
Tags: unity3d, firebase, arrays, android
Published: 2022-03-30 18:56:16

Hi! In this article I'd like to show you a simple way to get started with retrieving any type of data from Firestore in Unity using the Firestore Package for your mobile app. At the time of writing this article there's no JSON support yet.
The shown method is quite straightforward and does **<u>not</u>** involve writing our own JSON converters.

⚠️ = Be aware
⏬ = Download

### Table of contents:
* [Requirements](#requirements)
* [Preparation](#preparation)
* [Adding Data](#adding-data)
* [Retrieving Data](#retrieving-data)


⚠️ In this tutorial there will be code snippets from my Painting Recognition app. There's no available code repository **<u>yet</u>** since this app is a schoolproject on which my grades are dependent.

---

### Requirements <a name="requirements"></a>

In this demo we'll be using:

- ⏬ [Unity Hub (Personal License) -> Unity 2020.3.26f1]
  (https://unity3d.com/get-unity/download)
   * Modules (2): Android & iOS Build Support

- ⏬ [Firebase Unity SDK 8.8.0]
  (https://firebase.google.com/download/unity)

- [Firebase Console]
  (https://console.firebase.google.com)

### Preparation

(If you have already **done these steps**, go to [Adding Data](#adding-data) or [Retrieving Data](#retrieving-data))

Here's what you need to do to set it all up!
#### 1. Create/Use an Unity project
Tutorial link [here]
(https://www.youtube.com/watch?v=mcHBN_mDIxI).

#### 2. Add Firebase to your Unity project
Tutorial link [here]
(https://firebase.google.com/docs/unity/setup).

⚠️ Be sure to enter a `Package Name` for your app since you'll have to use this later!
![Firebase App Management](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlg9814195v3ds17rzgz.png)

#### 3. Link Firebase with Unity

Once you've filled in your `Package Name` you can now do the same in your **Unity project**:

   * Go to 'File' -> 'Build Settings' 
   * Select your `Platform`
   * Once selected, click on `Player Settings`
![Player Settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/587lncv8iok3pluuvzmu.png)
 
 
   * Scroll down until you find `Bundle Identifier`
![Bundle Identifier](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gye4oygolx6fzo7k589g.png)
 
   * Enter the **same** `Package Name` you've chosen
   * Next add the Firebase SDK:
     * Extract the `.zip` SDK
     * Go into the folder `dotnet4` since our Unity version is > 2017
     * Go back to Unity and create and open the folder 'Firebase' in 'Assets'
     * Drag and drop the `FirebaseFirestore.unitypackage` in the 'Firebase' folder (to keep it organised)


   * Finally, download the `Project Identifier` file from the [Firebase Console](https://console.firebase.google.com):
     * Android: ⏬ `google-services.json`
     * iOS: ⏬ `GoogleService-Info.plist`
![google-services file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sez25fxfgm32lc6koxw2.png)
     * Drop this file into the 'Assets' folder in Unity

### Adding Data <a name="adding-data"></a>

Before we can retrieve any data we need to add it first. Be advised to structure your to be retrieved data beforehand. It might save you a lot of time rewriting code!

Firestore is a non-relational (a.k.a. **NoSQL**) database like MongoDB. There are no columns, no specific order and no foreign keys to create any type of relationship with other data. In Firestore or other NoSQL databases, data is stored in a `Collection` of `Documents`.

In this example the data will be structured the following way:
![Data Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fbo2pvizc63z1n03nbzu.png)

> Above is a list of paintings (known as a `Collection`) wherein each individual painting (known as a `Document`) resides.

Each document also has its own `fields`. This is the actual data that belongs to that document.

In this example, the document fields look schematically like this:
![Document Scheme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v49n2k4rr4nh4yib7vn6.png)
> Each painting has a couple of points of interest and each point has multiple descriptions for the different languages that they can be read in.

In Firestore it will look hierarchical:
![Hierarchical structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pvabjzo1mvdrabjq4a22.png)

Now it's up to you! **Add data** to your Firestore. You can do this manually or through code. If you'd like have more information on structures and how to add data, take a look on the [Firestore Documentation: Add Data](https://firebase.google.com/docs/firestore/manage-data/add-data).

### Retrieving Data <a name="retrieving-data"></a>

Now onto the fun part: `<coding>` :)! In Unity, add an empty `GameObject` in the hierarchy and create a new script in `Assets > Scripts`. Attach this script as a component on the cube and open this script in your favorite editor (I use JetBrains Rider).
It should look like this:
![Inspector Add Component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ukj4773xadyuc246wyyw.png)

#### Reference to Firebase

First we need to get a reference to our collection (fill in your collection name): 

```
private Task<QuerySnapshot> _storageSnapshot;
void Awake() => _storageSnapshot = FirebaseFirestore.DefaultInstance.Collection("Paintings").GetSnapshotAsync();
```

We don't have to worry about opening or closing connections. The code will do its magic by checking our `Project Identifier` and `Package Name`.

#### Coroutines

Next we'll write a coroutine. This method is identified by returning an IEnumerator. Coroutines work like void methods in terms of returning nothing really. To make it work, we add a method as a parameter (also known as a callback) to which we give our result to. In this case it's a list of our retrieved paintings.

A coroutine is Unity's way of handling asynchronous operations and multithreading. We can start iterating on the retrieved collection by creating an anonymous method to continue the Task:

```
public IEnumerator GetPaintingsAndPois(Action<IList<Painting>> callback)
{
     IList<Painting> paintingList = new List<Painting>();
     int paintingCount = 0;
     _storageSnapshot.ContinueWithOnMainThread(task =>
     {
          var collection = task.Result;
          if (collection.Count == 0)
              Debug.LogError("There are no paintings available in the collection 'Paintings'.");
                
          paintingCount = collection.Count;
          Debug.Log("Found " + paintingCount + " paintings!");
     }
     yield return new WaitUntil(() => paintingList.Count == 
     paintingCount && paintingList.Count != 0);
     Debug.Log("Done getting " + paintingCount + " paintings!");
     callback(paintingList);
}
```

With coroutines you can stop execution until a certain predicate is met. In this example I wait until it has retrieved all the paintings. Due to the synchronous way of how the paintings are retrieved (no async/await keywords), the `WaitUntil()` is very important. I have tried using async/await but I ran into order of execution and threading issues. As said previously, coroutines handle this for you. 

At this stage, we've gotten all our paintings but did nothing with them. Let's take a look on how to iterate over them.

(If you'd like to see the boilerplate code for this, you can visit the [Firestore Documentation: Get Data](https://firebase.google.com/docs/firestore/query-data/get-data))

#### Dictionaries

Since there's no JSON support, we have to make it work the cumbersome way with dictionaries. Dictionaries in particular are very useful for combining data into `Key-Value Pairs`. In our case it's going to be matching a string (name of the key) to an object (fields from a Document). `Value` is an object so we can cast it to any type we need.

Let's start with unstructured, easy to access data like the painting's title. For this we need to loop over each `Document` in our retrieved collection:

```
//...
//paintingCount = collection.Count;
//Debug.Log("Found " + paintingCount + " paintings!");

foreach (var paintingData in collection.Documents)
{
      string titlePainting = ???
}
```

To retrieve a `Value` from a dictionary, you can use the `[]` operator. In code it will look like this:

```
paintingData.ToDictionary()["title"]
```
This will give you the `Value` for the key "title". Using this method we can now loop over each painting/document and get our `field` by safe casting it ([using 'as'](https://stackoverflow.com/a/7566234)) to the right type at the end:

```
foreach (var paintingData in collection.Documents)
{
      string titlePainting = paintingData.ToDictionary()["title"] as string
      //string painter = ...
}
```

#### Retrieving an array

Now we can apply this method to every other `field` _except_ to an array. Let's say we want to retrieve the `PointsOfInterest[]` array from our painting.
![POI Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c2z079mbqsq3b0jqw3vv.png)

You might think we can apply the same technique using `[]` on the dictionary and then safe cast it to object[] but it will result in a `NullPointerException`. It goes against intuition since it is an array in Firestore.
![Array Firestore](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ewowsbhas1tsu49dr6re.png)

What does work however is safe casting it to `List<object>`:
```
List<object> pointsOfInterest = painting.ToDictionary()["pois"] as List<object>;
```

#### Retrieving nested arrays

Now we've covered how to get an array out of a `Document` but... how do we get an array out of an array you might ask. Luckily the answer is somewhat the same.

Since we have a `List<PointsOfInterest>` we can iterate it and safe cast our object to a `Dictionary<string, object>` to be able to retrieve data from it:
```
foreach (object poi in pointsOfInterest)
{
      Dictionary<string, object> poiDictionary = poi as Dictionary<string, object>;
}
```
And now you can simply use `[]` to get the descriptions and safe cast them to a `List<object>`:
![Descriptions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/04tn98yhjwqjplicurg2.png)
 
```
foreach (object poi in pointsOfInterest)
{
      Dictionary<string, object> poiDictionary = poi as Dictionary<string, object>;

      //Another array...
      List<Description> = poiDictionary["descriptions"] as List<object>;
      //string language ...
}
```

#### Result

Everything combined, the coroutine's structure should look similar to this:

```
public IEnumerator GetPaintingsAndPois(Action<IList<Painting>> callback)
{
  //...
  _storageSnapshot.ContinueWithOnMainThread(task =>
  {
    var collection = task.Result;
    foreach (var paintingData in collection.Documents)
    {
      string titlePainting = paintingData.ToDictionary()["title"] as string
      //string painter = ...

      List<object> pointsOfInterest = painting.ToDictionary()["pois"] as List<object>;
      foreach (object poi in pointsOfInterest)
      {
        Dictionary<string, object> poiDictionary = poi as Dictionary<string, object>;
        List<Description> = poiDictionary["descriptions"] as List<object>;
        //string language...
      }
    }
  }
  //...
}
```

---

### Conclusion

And that's it! You've successfully gotten all the fields you needed. Now you can work on the more _fun_ things in your app.

If you have any issues, feel free to let me know down below in the comments :)!