Unity Firebase RTDB doesn't work after building - firebase

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.

Related

Why do I get "Client is not yet ready to issue requests" - Firestore get() fails when called by Schedule function

In my Firebase project I have a functions.pubsub.schedule().onRun() that runs every 5 minutes to perform some calendar related tasks. It needs to look up in my Firestore collections and does so with a .get() query.
This has been working fine until sometime 2020-03-13 in the morning where the function started to throw
2020-03-13 09:36:02.326 CET scheduledHooks 1042277797598294
Error: INTERNAL ERROR: Client is not yet ready to issue requests.
at Firestore.get projectId [as projectId] (/srv/functions/node_modules/#google-cloud/firestore/build/src/index.js:401:19)
at Query.toProto (/srv/functions/node_modules/#google-cloud/firestore/build/src/reference.js:1556:42) at Query._get (/srv/functions/node_modules/#google-cloud/firestore/build/src/reference.js:1466:30)
at Query.get (/srv/functions/node_modules/#google-cloud/firestore/build/src/reference.js:1457:21)
at FirebaseActivitiesCollection.<anonymous> (/srv/functions/lib/collections/activities/FirebaseActivitiesCollection.js:32:40)
at Generator.next (<anonymous>) at /srv/functions/lib/collections/activities/FirebaseActivitiesCollection.js:8:71
at new Promise (<anonymous>) at __awaiter (/srv/functions/lib/collections/activities/FirebaseActivitiesCollection.js:4:12)
at FirebaseActivitiesCollection.getActivitiesByInterval (/srv/functions/lib/collections/activities/FirebaseActivitiesCollection.js:27:16)
I can't track that I have changed anything, could it be that Firebase made some changes that I should be aware of or am I missing an obvious clue in this error message?
My other Cloud Functions and the Firebase JavaScript SDK still work fine.
Extra info: The same code works on my other environment where I have not deployed. This of course led me to search for changes in the deployed code, but I can't find any!?
It seems by the error, that the project was not connected and initialized correctly. As per the official API here, this error occurs when:
Returns the Project ID for this Firestore instance. Validates that
initializeIfNeeded() was called before.
I would recommend you to check that. In case you are still facing, I would say for you to contact the Firebase Free Support. Since you mentioned that it was working until yesterday morning, it might be some change on their side.
Hope this helps!
This turned out to be a very simple matter of initialization of Firebase:
Before I had:
// Initialize firebase
admin.initializeApp(functions.config().firebase)
..And it worked fine until it didn't anymore.
Now I have:
// Initialize firebase
admin.initializeApp()
... And it works again
Got the same error while running some jest tests involving firebase. Problem was that I didn't have GOOGLE_APPLICATION_CREDENTIALS environment variable set.
Since I was running them locally on my machine, exporting this variable in terminal before running the tests was sufficient for my case:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials/file.json

Flutter Crashlytics log caught exception

Looking for some clarification as to how one can log caught exceptions using flutter's firebase_crashlytics package.
If I understand correctly (and from running some sample code) Crashlytics.instance.log('text'); will only add logs to the next crash report, rather than send off a non-fatal issue itself.
I'm looking for functionality which is equivalent to Crashlytics.logException(e); on Android, e.g.
try {
throwException();
} catch (e) {
Crashlytics.logException(e);
}
which allows you to log caught exceptions so they appear as non-fatal issues in the Crashlytics dashboard.
Is this currently possible with flutter's firebase_crashlytics package?
Is calling Crashlytics.instance.recordError('text', StackTrace.current) the way to achieve this for now?
Many thanks!
Short answer, yes.
Crashlytics.instance.recordError() is the equivalent of Crashlytics.logException()
If you dig into the Flutter firebase_crashlytics source code, you can actually see what Android APIs are involved.
Flutter’s recordError() invokes the method Crashlytics#onError in the Android library.
And tracing Crashlytics#onError, you’ll see that it goes to Crashlytics.logException(exception);
Additional note, you’ll also notice why Crashlytics.instance.log() ”will only add logs to the next crash report”. These logs are added to a ListQueue<String> _logs which is then packaged into the next call of recordError()
A snippet of Flutter’s invocation of Crashlytics.logException():
_recordError(...) {
...
final String result = await channel
.invokeMethod<String>('Crashlytics#onError', <String, dynamic>{
'exception': "${exception.toString()}",
'context': '$context',
'information': _information,
'stackTraceElements': stackTraceElements,
'logs': _logs.toList(),
'keys': _prepareKeys(),
});
}
And some reference notes for Crashlytics.logException():
To reduce your users’ network traffic, Crashlytics batches logged
exceptions together and sends them the next time the app launches.
For any individual app session, only the most recent 8 logged
exceptions are stored.
To add to the accepted answer, Crashlytics.instance.recordError() has now been deprecated for the new method FirebaseCrashlytics.instance.recordError(exception, stack).
BONUS TIP:
I had this problem where all the logged exceptions are grouped under the same issue in Crashlytics dashboard. These might be different crashes of the same or different code components. Due to this, I had to manually go through each instance of the crash to verify.
From my own testing, I found out the grouping is based on the top-most line in the stack trace you passed into the method above. Luckily, Dart has an easy way to get the current stack trace using StackTrace.current.
So to properly group the issues: get the current stack trace at the time of the exception and pass it in FirebaseCrashlytics.instance.recordError(exception, stack).
Hope this helps someone out there, I looked everywhere on the internet for a similar issue but can't find any.

Firebase multi path query takes long to response

I am using firebase realtime database and I have a multi path firebase update query that I was using for a while, however for last couple of days the callback never fires and other firebase database requests are blocked as well until this one fires. Callback takes incredibly longer to fire than it should or it used to. And most interestingly this issue does happen on Windows environment only.
let updateObj = {}
updateObj[`transcripts/${uid}/${itemId}/currentState`] = currentState
updateObj[`lists/${uid}/${itemId}/edited`] = firebase.database.ServerValue.TIMESTAMP
updateObj[`lists/${uid}/${itemId}/filename`] = title
db.ref().update(updateObj, function(error){
//handle error
}
I thought it could possibly be because another reference is initialized at the same path later on but callback sometimes hangs even before that.
updateObj[`transcripts/${uid}/${itemId}/currentState`] = currentState
variable currentstate above has a somewhat of a big array node inside so I also think the array might be the issue considering they don't really work that efficiently with firebase realtime database.
Commenting this out solves all the problem but I am still clueless how this suddenly started to break firebase realtime database for me.

How can I initialize firebase app in a serverless model? What is the concurrent app limit?

I created a serverless function that performs that Firebase Token Validation.
Everything works as intended. Except, I have I get errors on subsequent calls to initialize my app that the default app already exists (same container). This raises some questions.
If my serverless infrastructure was to spin up multiple concurrent containers, each working to initialize the app. Would this also cause this error? That the app is initiailized elsewhere? Or is this error isolated to local instances?
If its the latter, If I provide a named app based on the container it is spun up in, is there a firebase limit to the maximum number of apps that can be initialized at once?
This is how I am initializing the app now:
cred = credentials.Certificate(SERVICE)
firebase_admin.initialize_app(cred)
I could do this but am not sure about firebase app limits or concurrent initializations (cant find any specifics in docs):
cred = credentials.Certificate(SERVICE)
firebase_admin.initialize_app(cred, 'APP-NAME-[CONTAINERID]')
Or, should I just re-write this using my own JWT Decoder and grabbing the public keys from google?
And here is the full error:
Error occurred setting firebase credentials: The default Firebase app already exists. This means you called initialize_app() more than once without providing an app name as the second argument. In most cases you only need to call initialize_app() once. But if you do want to initialize multiple apps, pass a second argument to initialize_app() to give each app a unique name.
UPDATE: AWS Lambda, Python.
I am going to test out with the following, to prevent re-initializing the app within the same container on warm function executions and move forward with the assumption that there are no API limits on performing auth.validate_id_token() and that this won't conflict with concurrent container executions. Ill report back if it tests out differently.
try:
firebase_admin.get_app()
logger.info('firebase already intialized.')
except ValueError as e:
logger.info('firebase not initialized. initialize.')
cred = credentials.Certificate(SERVICE)
firebase_admin.initialize_app(cred)
I will probably still migrate to another JWT validation to reduce function size (since I already have a jwt library for my own app use) and migrate away from relying on Firebase API to decode it.
If you get an error when initializing the admin SDK that says the default app already exists, that just means you're trying to init the admin SDK twice in the same process. Obviously, don't do that. If you init once and only once per process, you will never see this error.
You will have to take some care to only call the init method once per server instance. It's not clear exactly what you're doing from the code you've shown. I don't know about python, but with node, you can init once in a global context without problems. If you need to init during a function execution, you should have some flag to check that ensures the default Firebase app hasn't already been initialized, and init only conditionally based on that flag.

Getting "Resource not found error" while using Azure File Sync

Facing a very strange issue.
Following this guide https://azure.microsoft.com/en-in/documentation/articles/app-service-mobile-xamarin-forms-blob-storage/ to implement File Sync in Xamarin Forms app.
The Get method in my service (GetUser, default get method in App service controller) is being called thrice & on the 3rd iteration it gives me a 404 resource not found error. First 2 iterations work fine.
This is the client call
await userTable.PullAsync(
null,
userTable.Where(x => x.Email == userEmail), false, new System.Threading.CancellationToken(), null);
If I remove the following line,
// Initialize file sync
this.client.InitializeFileSyncContext(new TodoItemFileSyncHandler(this), store);
then the code works just fine, without any errors.
I will need some time doing a sample project, meanwhile if anyone can shed some light, it will be of help.
Thanks
This won't be an answer, because there isn't enough information to go on. When you get a 404, it's because the backend returned a 404. The ideal situation is:
Turn on Diagnostic Logging in the Azure Portal for your backend
Use Fiddler to monitor the requests
When the request causes a 404, look at what is actually happening
If you are using an ASP.NET backend (and I'm assuming you are because all the File tutorials use ASP.NET), then you can set a breakpoint on the appropriate method in the backend and follow it through. You will need to deploy a debug version of your code.
this is sorted now, eventually I had to give it what it was asking for. I had to create a storage controller for User too, although I don't need one as I don't need to save any files in storage against the users.
I am testing the app further now to see if this sorts my problem completely or I need a storage controller for every entity I use in my app.
In which case it will be really odd as I don't intend to use the storage for all my entities.

Resources