Xamarin.Forms - Android Activity with async OnCreate method - asynchronous

I'm running into the questions if it's a problem to mark FormsAppCompatActivity.OnCreate(Bundle bundle) as async? I have to fetch user-specific data from an AWS DynamoDB, and I need to retrieve the user from the Akavache cache before in order to query with the userId. Of course, I could also save the userId to the local settings or serialize the whole user object to be able to retrieve it synchronously.
I also don't expect performance issues during startup because the cache uses SQLite definitely exists. The only problem is that either I await Akavache's GetObject<T>(string key) and therefore, mark everything down to OnCreate as async, or I subscribe to the returned Observable and the following methods will try to query the user data without a valid userId, because the Observable hasn't returned yet.

Since you're using XF for development, the code LoadApplication(new App()); in OnCreate of MainActivity will hook the lifecycle event to App's lifecycle event in PCL.
You didn't post any code, I guess that you place your code for data fetch after LoadApplication(new App());, then as you said it didn't return yet, otherwise the behavior should be different.
I suggest you to call your task in the OnStart() of App in PCL and together use DependencyService to call into your async task for fetching data from PCL.

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.

In instrumented tests, how do you make Cloud Firestore write commands succeed when disabling the network?

So I am attempting to use the Cloud Firestore offline cache ONLY as an API for my instrumentation tests, to avoid having to read and write from the server database during my integration tests.
First, in my test setup method, I call this method
protected fun setFirestoreToOfflineMode() {
Tasks.await(FirebaseFirestore.getInstance().disableNetwork())
}
Then, at the beginning of each relevant test, I use
fun givenHasTrips(vararg trips: Trip) {
GlobalScope.launch(Dispatchers.Default) {
trips.forEach {
firestoreTripApi.put(it)
}
}
}
In that put method, I have the following code:
try {
Tasks.await(tripCollection().document(tripData.id).set(tripData)),
firestoreApiTimeoutSeconds, TimeUnit.SECONDS)
Either.Right(Unit)
} catch (e: Throwable) {
Either.Left(Failure.ServerError)
}
I am calling the set() method and am waiting for a successful result in order to be able to return that the operation was a success, to update my UI afterward.
What happens is the cache DB is written correctly BUT the "set()" function times out because the database is in offline mode. I have read that Firestore only confirms a success if the Server DB was correctly written. If that is the case, I do not know if it is possible to have this call not time-out when operating strictly in offline-cache mode.
Is there a solution to have Firestore act as if the local cache database was the source of truth and return successes if placed in offline mode, just for tests?
The Task returned by the methods that modify the database (set, update, delete) only issues a callback when the data is fully committed to the cloud. There is no way to change this behavior.
What you can do instead is set up a listener to the document(s) that are expected to change, and wait for the listener to trigger. The listener will trigger even while offline.

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");
});

Can I implement beforeCreate trigger on Firebase Functions

On the Firebase docs they mention 4 types of triggers:
onCreate
onDelete
onUpdate
onWrite
Is there a way to listen to added row in the Cloud Functions and modify fields of an added row before the "child_added" listeners are triggered? Is there a way to implement BeforeCreate?
Desired BeforeCreate cycle (in Cloud Functions):
Request to add a new message
Change the message fields
Add a new message with modified fields
Clients receive a "child_added" event
All events for the Realtime Database in Cloud Functions trigger asynchronously after the write has been committed. For this reason, other users may already have seen the data before your function can change it.
To solve this problem you'll want to ensure the data only gets written to the location everyone sees after it's been validated/modified.
To validate/modify the new data before listeners to that data can see it, you have two options:
Use a HTTP triggered function for writing the data. The application code calls the HTTP function, which does the data manipulation you want, and then writes the result to the database.
Have the applications write to a "moderation queue", which is just a separate location in the database. The Cloud Function triggers fro this queue, validates/modifies the data, writes it to the actual location, and then deletes it from the queue.
With both of these approaches you lose parts of the transparent offline behavior of the Firebase Realtime Database though, so you'll have to choose.
You need to use onWrite for this to work, as you are saving to the database more than once when you are using child_added.
onWrite(handler) returns functions.CloudFunction containing non-null functions.database.DeltaSnapshot
Event handler that fires every time a Firebase Realtime Database write of any kind (creation, update, or delete) occurs.
more info here: https://firebase.google.com/docs/reference/functions/functions.database.RefBuilder#onWrite
Op wants to do the following:
Request to add a new message
If he wants to request a new message from the end-user then it is better done on the client side.
Change the message fields
Here he wants to change what was written inside the field, which is also usually done on the client side not in cloud functions.
Add a new message with modified fields
Here he wants to add the new message to the database (according to my analysis). Then this can be done in the cloud functions and the message can be added using set()
Clients receive a "child_added" event
then here after adding the new message to the database, he wants the client to receive the database trigger, that will be triggered with the new message. Here he can use cloud functions like onWrite() or onCreate()

Comparing Cloud Functions for Firebase database triggers onCreate(), onWrite(), onUpdate(), when to use?

It's a simple question, I have seen all these methods addressed in the title in the documentation but all the examples are using onWrite() for triggering database events which then one would have to check to make sure it's not for removing or updating with
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite(event => {
...
// Only edit data when it is first created.
if (event.data.previous.exists()) {
return;
}
// Exit when the data is deleted.
if (!event.data.exists()) {
return;
}
...
});
The only examples with onCreate() for instance, are related to auth events. Is there a reason or am I just being paranoid? Why not just use onCreate() and not bother the check?
onCreate(), onUpdate() and onDelete() were added in the Firebase SDK for Cloud Functions (v0.5.9) release on 7 July 2017. This is detailed in the release notes:
An updated beta release of the Firebase SDK for Cloud Functions
(v0.5.9) is now available. It includes the ability to listen to
granular create, update, and delete database events by using the
onCreate(), onUpdate(), and onDelete() methods.
Prior to that release, the only database event handler was onWrite(). The documentation has not yet been updated to include examples of the new handlers.
There is no reason not to take advantage of the convenience of the new handlers.

Resources