# Cloud Storage: Firebase (optional)

For a comprehensive guide on integrating Firebase with Unity, developers are encouraged to consult the [official Firebase documentation](https://firebase.google.com/docs/unity/setup?authuser=0&_gl=1*cyka2y*_ga*MTYwMDM2MTA0OC4xNjkwOTU4Mzg2*_ga_CW55HF8NVT*MTY5MDk2NTE0Ny4zLjEuMTY5MDk2NTQ4Ny4wLjAuMA..#add-config-file). What follows in this tutorial is a concise overview tailored to our specific use case.

### **Create a Firebase Project**

1. Visit the [Firebase Console](https://console.firebase.google.com/).
2. Click on "Add Project" and follow the prompts to fill in the project information.

   <figure><img src="/files/06YpH7afQu1sjE280bq2" alt=""><figcaption></figcaption></figure>
3. Click "Create Project".

### **Add an App**

1. On the project overview page, click "Add App" and select the appropriate platform (e.g., Android).

   <figure><img src="/files/DFeVAe7302iU8AWsHK3g" alt=""><figcaption></figcaption></figure>
2. Follow the prompts to enter the app's package name and other information.
3. Download and save the configuration file (**`google-services.json`**) and place it in the **`Assets`** folder.

### **Install the SDK**

1. Download [**Firebase Unity SDK**](https://firebase.google.com/download/unity?authuser=0)
2. Import **`FirebaseStorage.unitypackage`** into Unity.

### **Add Storage**&#x20;

1. Go to **`Build > Storage`**

   <figure><img src="/files/7C3SPJFv4S2DZKhGpoZj" alt=""><figcaption></figcaption></figure>
2. Click **Get Started** and set up cloud storage

   <figure><img src="/files/WM98DGCoWLshXkbBKEs3" alt=""><figcaption></figcaption></figure>
3. The highlighted part in the following image is the folder location where we will upload the anchor file.

   <figure><img src="/files/VKbYufGkIQeygbHgoZ5D" alt=""><figcaption></figcaption></figure>

### **Upload/download files**&#x20;

We've developed a dedicated `FirebaseStorageManager` class to facilitate file upload and download functionalities. For detailed implementation, you can refer to the official  [documentation](https://firebase.google.com/docs/storage/unity/upload-files).

{% hint style="info" %}
It's important to note that whether you're uploading or downloading, the **`storagePath`** should include the specific filename, not just the directory. This specification is also highlighted in the documentation.&#x20;

![](/files/hcOgTdXPvj5Lrw2KTcpE)

Therefore, We set the file path obtained from the previous step as **`storageBasePath`** and later concatenate it with the filename to form the complete path for upload/download.
{% endhint %}

```csharp
using Firebase;
using Firebase.Extensions;
using Firebase.Storage;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class FirebaseStorageManager : MonoBehaviour
{
    public static FirebaseStorageManager Instance { get; private set; }
    public string storageBasePath = "gs://YourProjectName-xxxxxx.appspot.com/";

    private FirebaseStorage storage;
    private CancellationTokenSource cancellationTokenSource;
    private bool operationInProgress = false;
    
    public const string MapFolder = "XrealMaps";

    void Start()
    {
        FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
        {
            FirebaseApp.Create();
            storage = FirebaseStorage.DefaultInstance;
            cancellationTokenSource = new CancellationTokenSource();
        });

       if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject); // Optional, if you want this instance to be preserved during scene transitions.
        }
        else
        {
           Destroy(gameObject); // 
        }
        
    }

    public Task UploadFile(string fileName, string localFilePath)
{
    string localFile = localFilePath;
    string storagePath = Path.Combine(storageBasePath, fileName);

    // Check if the file exists.
    if (!File.Exists(localFile))
    {
        Debug.LogError($"File {localFile} does not exist!");
        return Task.CompletedTask; // If the file does not exist, return a completed task.
    }
    else
    {
        Debug.Log($"File {localFile} exist!");
    }

    StorageReference storageRef = storage.GetReferenceFromUrl(storagePath);

    Debug.Log($"Uploading {localFile} to {storagePath}...");

    // Create a stream using FileStream.
    Stream fileStream = new FileStream(localFile, FileMode.Open);

    // Create a TaskCompletionSource to identify when the task is completed.
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

    // Use PutStreamAsync to upload the stream.
    storageRef.PutStreamAsync(fileStream).ContinueWithOnMainThread(task =>
    {
        if (task.IsFaulted || task.IsCanceled)
        {
            Debug.LogError(task.Exception.ToString());
            tcs.SetResult(false); // Set the task as completed, but return failure.
        }
        else
        {
            Debug.Log("File uploaded successfully.");
            tcs.SetResult(true); // Set the task to be completed successfully.
        }
    });

    return tcs.Task;
}



    public Task DownloadFile(string fileName, string localFilePath)
{
    string localFile = localFilePath;
    string storagePath = Path.Combine(storageBasePath, fileName);
    StorageReference storageRef = storage.GetReferenceFromUrl(storagePath);

    Debug.Log($"Downloading {storagePath} to {localFile}...");
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

    storageRef.GetFileAsync(localFile, new StorageProgress<DownloadState>(state =>
    {
        Debug.Log($"Downloading {state.BytesTransferred} of {state.TotalByteCount} bytes.");
    }), cancellationTokenSource.Token).ContinueWithOnMainThread(task =>
    {
        if (task.IsFaulted || task.IsCanceled)
        {
            Debug.LogError(task.Exception.ToString());
            tcs.SetResult(false); 
        }
        else
        {
            Debug.Log("File downloaded successfully.");
            tcs.SetResult(true); 
        }
    });

   
    return tcs.Task;
}

    public void CancelOperation()
    {
        if (operationInProgress && cancellationTokenSource != null)
        {
            Debug.Log("Cancelling operation...");
            cancellationTokenSource.Cancel();
            cancellationTokenSource = new CancellationTokenSource();
        }
    }
}
```

### **Common Issues**

1. After installing Firebase, building software in Unity may fail. This is a known issue with Android and requires adding **`launcherTemplate.gradle`** in Unity, then adding the following content:

```xml
    packagingOptions 
    {
        exclude 'META-INF/com.android.tools/proguard/coroutines.pro'
    }
```

2. Possible error may occur：

```log
18063 19165 E AuthPII : [GoogleAccountDataServiceImpl] getToken() -> BAD_AUTHENTICATION. App: com.google.android.gms, Service: oauth2:https://www.googleapis.com/auth/emeraldsea.mobileapps.doritos.cookie
18063 19165 E AuthPII : vfc: Long live credential not available.
```

In this case, you need to update the Google Play Service of the mobile phone.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xreal.gitbook.io/nrsdk/v2.1.0/development/spatial-anchor/tutorial-sharing-anchors/cloud-storage-firebase-optional.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
