Recieved firebase answer to Unity project but not to Pico Device - firebase

I have a unity project in which I send subscribe to firebase and receive a callback every time a field changes - which I need to process according to the status of the fields.
FirebaseDatabase.Net is installed in the project
Problem 1, although my subscription invokes the callback func nicely when I change fields in the firebase, then I then build it for apk and install it on VR Pico G24K, I receive no connection from the firebase.
Problem 2: When I attempt to Query the firebase when not inside the callback func, both unity and pico will stuck.
Query func
private async Task<Device> QuerySingleDeviceAsync(string serial)
{
return await _firebaseClient.Child(Fields.DEVICES_ROOT).Child(serial).OnceSingleAsync<Device>();
}
Can anyone here point on something I might be missing (package I need to install for VR usage, or alternative function of questioning the firebase outside the observe's callback so I can better debug the no-comm issue?

Related

Unity Firebase RTDB doesn't work after building

Unity version 2020.3.22f1,
Firebase SDK 9.0.0 dotnet4
I've imported both the analytics and real-time database SDK.
The Analytics works perfectly fine.
Regarding the database, building an Android app bundle and uploading to internal test or building an APK and uploading directly to my phone or building for IOS and uploading to test-flight all 3 results with an error.
This is how I initialize Firebase-
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
//init analytics
FirebaseAnalytics.SetAnalyticsCollectionEnabled(true);
DatabaseReference = FirebaseDatabase.DefaultInstance.RootReference;
Debug.Log(JsonConvert.SerializeObject(DatabaseReference.Database.App.Options));
});
This is how I use the database (increment the win field of a specific level) -
DatabaseReference.Child("Levels").Child($"Level{levelNum}").Child("Wins").RunTransaction((mutableData) =>
{
mutableData.Value = Int32.Parse(mutableData.Value.ToString()) + 1;
return TransactionResult.Success(mutableData);
});
Referring to the log above in the DatabaseRefrence initialization, in the editor, I can see all the configuration properties - databaseUrl,apikey,AppId, etc...
Debugging the APK on my phone the Options property only includes the databaseUrl.
And when trying to perform a transaction to the database an error is being thrown -
W/Unity: Exception in transaction delegate, aborting transaction
System.NullReferenceException: Object reference not set to an instance of an object.
at ....(Firebase.Database.MutableData mutableData)
Things I've tried so far-
I've added the SHA1/SHA256 of both my debug Keystore and googles console App integrity
I've checked and the XML files are being generated with all the details successfully at the streamingAssets folder and at Assets\Plugins\Android\FirebaseApp.androidlib\res\values\google-services.xml
I've tried Initializing the Firebase app manually as mentioned here - https://stackoverflow.com/a/66874818/7210967, doing that indeed results with the debug.log above to include all the Options parameters but the same error occurs as if it doesn't actually use it. (I've tried doing that both with the configuration files in place and removed them completely).
I've tried overriding the default app instance Options.
I've read some posts saying that Proguard obfuscation might cause errors with firebase? couldn't find anything related to Unity.
If anyone has any ideas, please share! ty
Transactions in Firebase Realtime Database work a bit differently than you might expect, as they immediately invoke you handler with the client's guess about the current value of the node, which in general is going to be null.
So when you call mutableData.Value in your code, you get back null and you then call ToString() on it, which leads to the error you get. To solve this, first check whether the mutableData.Value is null before invoking methods on it.
int current = mutableData.Value is null ? 0 : Int32.Parse(mutableData.Value.ToString());
mutableData.Value = current + 1;
return TransactionResult.Success(mutableData);
Syntax errors are possible in the above, as it's been a while since I wrote C#.
A transaction send both the SDKs guess and your new value based on that guess to the server, which then does a compare-and-set operation. If the guess doesn't match the actual value in the database, the server rejects the write with the current value, which the client then uses to call your transaction handler again with an updated current guess.

How to continue running Firebase Cloud Function after request is finished

Really bizarre that Firebase doesn't seem to work quite like typical Express app. Whatever I write in Express and copy-paste to Firebase Functions I typically get error. There is one that I can't figure out on my own though.
This endpoint is designed to start a function and live long enough to finish even longer task. That request is a webhook (send docs, we will transform them and ping you when it's done to specified another webhook). Very simplified example below:
router.post('/', (req, res) => {
try {
generateZipWithDocuments(data) // on purpose it's not async so request can return freely
res.sendStatus(201)
} catch (error) {
res.send({ error })
}
})
On my local machine it works (both pure Express app and locally emulated Firebase Functions), but in the cloud it has problems and even though I put a cavalcade of console.log() I don't get much information. No error from Firebase.
If generateZipWithDocuments() is not asynchronous res.sendStatus() will be immediately executed after it, and the Cloud Function will be terminated (and the job done by generateZipWithDocuments() will not be completed). See the doc here for more details.
You have two possibilities:
You make it asynchronous and you wait its job is completed before sending the response. You would typically use async/await for that. Note that the maximum execution time for a Cloud Function is 9 minutes.
You delegate the long time execution job to another Cloud Function and, then, you send the response. For delegating the job to another Cloud Function, you should use Pub/Sub. See Pub/Sub triggers, the sample quickstart, and this SO thread for more details on how to implement that. In the Pub/Sub triggered Function, when the job is done you can inform the user via an email, a notification, the update of a Firestore document on which you have set a listener, etc... If generateZipWithDocuments() takes a long time, it is clearly the most user friendly option.

Using Offline Persistence in Firestore in a Flutter App

I'm developing a app that uses Firebase's Firestore to send data to the web. One of the functions of the app is being able to save data in the device while being offline and send it to Firestore when internet connection is restored.
I activated offline persistence but it dosen't work.
DEBUG CONSOLE:
W/OkHttpClientTransport(28536): Failed closing connection
W/OkHttpClientTransport(28536): javax.net.ssl.SSLException: Write error: ssl=0x7f7acfc408: I/O error during system call, Broken pipe
W/OkHttpClientTransport(28536): at com.google.android.gms.org.conscrypt.NativeCrypto.SSL_write(Native Method)
W/OkHttpClientTransport(28536): at com.google.android.gms.org.conscrypt.NativeSsl.write(:com.google.android.gms#14798020#14.7.98 (040406-222931072):4)
How can I activate offline persistence and sync with Firestore when internet is restored?
MY CODE:
Future<Null> sendFirebaseData(var selectedModel) async {
Firestore.instance.enablePersistence(true);
var certID = await getIDCertificado();
var dateTime = new DateTime.now();
var nowHour = new DateFormat('kk:mm:ss').format(dateTime);
Map<String, dynamic> dataHeader = {
'ID': certID,
};
Map<String, dynamic> finalDataMap = {}..addAll(dataGeneral)
..addAll(dataInstrumento)..addAll(dataPadrao)
..addAll(dataAdicional)..addAll(dataHeader);
await Firestore.instance.collection('certificados').document((certID.toString()))
.setData(finalDataMap);}
when you use offline persistence in Firestore, don't use Transactions or await for response.
so, change this :
await Firestore.instance.collection('certificados').document((certID.toString()))
.setData(finalDataMap);
to this:
Firestore.instance.collection('certificados').document((certID.toString()))
.setData(finalDataMap);
When you restore your internet connection your data will be sync automatically, even if you are in background.
Doesn't work when your app is closed.
Context Of Promises & Callbacks when Offline
Why the above code change to remove "await" works.
Reference: Firebase Video - How Do I Enable Offline Support 11:13
Your callback won't be called and your promise won't complete until the document write has been successful on the server. This is why if your UI waits until the write completes to do something, it appears to freeze in "offline mode" even if the write was actually made to the local cache.
It is OK to not use async / await, .then() or callbacks. Firestore will always "act" as if the data change was applied immediately, so you don't need to wait to be working with fresh data.
You only need to use callbacks and promises when you need to be sure that a server write has happened, and you want to block other things from happening until you get that confirmation.
I think the currrent answer is outdated. According to the firebase documentation, offline persistentence is enabled by default for Android and iOS. For the web, it is not.
In flutter, the firestore implementation is based on the underlying OS. So you're safe on mobile apps, but not with flutter for web.
It is enabled by default but still only when you are not using await or transactions, further you can use timeout to stop listening to network connection by firestore after a specific time.
ref.setData(newNote.toMap()).timeout(Duration(seconds: 2),onTimeout:() {
//cancel this call here
print("do something now");
});

Using Firestore from a JobIntentService: Failed to gain exclusive lock to the Firestore client's offline persistence

Whenever I exit the app while I have an alarm set and the alarm goes off while the app is "DEAD" I get an Exception while trying to update a field in Firestore.
The code works when the app is running in the foreground so I really have no clue of what is going on. Either way, here is the code for 2 functions which get called from the JobIntentService which is in turn created from a BroadcastReceiver:
private val firestoreInstance: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
fun updateTaskCompletedSessions(taskDocRefPath: String, completedSessions: Int){
val taskDocRef = firestoreInstance.document(taskDocRefPath)
taskDocRef.get().addOnSuccessListener { documentSnapshot ->
documentSnapshot.reference
.update(mapOf("completedSessions" to completedSessions))
}
}
fun updateTaskSessionProgress(taskDocRefPath: String, sessionProgress: String){
val taskDocRef = firestoreInstance.document(taskDocRefPath)
taskDocRef.get().addOnSuccessListener { documentSnapshot ->
documentSnapshot.reference
.update(mapOf("sessionProgress" to sessionProgress))
}
}
The full error goes as follows:
Failed to gain exclusive lock to the Firestore client's offline persistence.
This generally means you are using Firestore from multiple processes in your app. Keep in mind that multi-process Android apps execute the code in your Application class in all processes, so you may need to avoid initializing Firestore in your Application class. If you are intentionally using Firestore from multiple processes, you can only enable offline persistence (i.e. call setPersistenceEnabled(true)) in one of them.
I will appreciate any help. Thank you!
I'm happy to announce that I found a solution! I was using two consequently firing JobIntentServices - one for completedSessions, the other for sessionProgress. (Bad design, I know...)
When I played around with it and made just ONE JobIntentService to call both of these functions, the exception is gone which makes perfect sense.

Can Cloud Functions for Firebase be used across projects?

I was hoping to trigger a Pub/Sub function (using functions.pubsub / onPublish) whenever a new Pub/Sub message is sent to a topic/subscription in a third-party project i.e. cross projects.
After some research and experimentation I found that TopicBuilder throws an error if the topic name contains a / and it defaults to "projects/" + process.env.GCLOUD_PROJECT + "/topics/" + topic (https://github.com/firebase/firebase-functions/blob/master/src/providers/pubsub.ts).
I also found a post in Stack Overflow that says that "Firebase provides a (relatively thin) wrapper around Google Cloud Functions"
(What is the difference between Cloud Function and Firebase Functions?)
This led me to look into Google Cloud Functions. Whilst I was able to create a subscription in a project I own to a topic in a third-party project - after changing permissions in IAM - I could not find a way associate a function with the topic. Nor was I successful in associating a function with a topic and subscription in a third-party project. In the console I only see the topics in my project and I had no success using gcloud.
Has anyone had any success in using a function across projects and, if so, how did you achieve this and is there a documentation URL you could provide? If a function can't be triggered by a message to a topic and subscription in a third-party project can you think of a way that I could ingest third-party Pub/Sub data?
As Pub/Sub fees are billed to the project that contains the subscription I would prefer that the subscription resides in the third-party project with the topic.
Thank you
Google Cloud Functions currently does not not allow a function to listen to a resource in another project. For Cloud Pub/Sub triggers specifically you could get around this by deploying an HTTP-function and add a Pub/Sub push subscription to the topic that you want to fire that cross-project function.
A Google Cloud Function can't be triggered by a subsription to a topic of another project (since you can't subscribe to another project's topic).
But a Google Cloud Function can publish to a topic of another project (and then subscribers of this topic will be triggered).
I solved it by establishing a Google Cloud Function in the original project which listens to the original topic and reacts with publishing to a new topic in the other project. Therefore, the service account (...#appspot.gserviceaccount.com) of this function "in the middle" needs to be authorized by the new topic (console.cloud.google.com/cloudpubsub/topic/detail/...?project=...), i.e. add principal with role: "Pub/Sub Publisher"
import base64
import json
import os
from google.cloud import pubsub_v1
#https://cloud.google.com/functions/docs/calling/pubsub?hl=en#publishing_a_message_from_within_a_function
# Instantiates a Pub/Sub client
publisher = pubsub_v1.PublisherClient()
def notify(event, context):
project_id = os.environ.get('project_id')
topic_name = os.environ.get('topic_name')
# References an existing topic
topic_path = publisher.topic_path(project_id, topic_name)
message_json = json.dumps({
'data': {'message': 'here would be the message'}, # or you can pass the message of event/context
})
message_bytes = message_json.encode('utf-8')
# Publishes a message
try:
publish_future = publisher.publish(topic_path, data=message_bytes)
publish_future.result() # Verify the publish succeeded
return 'Message published.'
except Exception as e:
print(e)
return (e, 500)
google endpoints can be a easier solution to add auth to the function http.

Resources