How do you speed up frontend response to user actions that require backend actions? - flutter

This question isn't specifically for flutter, but it's what I'm learning at the moment, so I'm tagging it as such.
I'm writing a flutter app where the user can "favourite" a note, which changes the state of that note's icon. I'm using firebase as backend.
The way I've implemented it is to update the state of the icon whenever the note object gets updated, but that of course takes time between the button press and the update.
This got me thinking about how do apps eliminate time between user action and feedback, when there's usually supposed to be time needed for the request to be sent to the backend, an update coming back to the app, etc?
For example: When a user upvotes a post on reddit, they can see the upvote button change state immediately, and the post's upvote counter updates accordingly, without any delay.
Do apps cache these user actions and have some way of mixing using cached information and actual backend(live) data, so that the user gets this nice immediate feedback?

Use Optimistic UI pattern. Optimistic UI immediately switches to the final state while the real operation is still in-progress. If the operation fails, rollback to the previous state and possible show error. Details how to implement Optimistic UI depends on the use case.

Related

What are the best practices when working with data from multiple sources in Flutter/Bloc?

The Bloc manual describes the example of a simple Todos app. It works as an example, but I get stuck when trying to make it into a more realistic app. Clearly, a more realistic Todos app needs to keep working when the user temporarily loses network connection, and also needs to occasionally check the server for updates that the user might have added from another device.
So as a basic data model I have:
dataFromServer, which is refreshed every five minutes, and
localData, that describes what changes have been made locally but haven't been synchronized to the server yet.
My current idea is to have three kinds of events:
on<GetTodosFromServer>() which runs every few minutes to check the server for updates and only changes the dataFromServer,
on<TodoAdded>() (and its friends TodoDeleted, TodoChecked, and so on) which get triggered when the user changes the data, and only change the localData, and
on<SyncTodoToServer>() which runs whenever the user changes the todo list, or when network connectivity is restored, and tries to send the changes to the server, retrieves the new value from the server, and then sets the new dataFromServer and localData.
So obviously there's a lot of interaction between these three methods. When a new todo is added after the synchronization to the server starts, but before synchronization is finished, it needs to stay in the local changes object. When GetTodosFromServer and SyncTodoToServer both return server data, they need to find out who has the latest data and keep that. And so on.
Coming from a Redux background, I'm used to having two reducers (one for local data, one for server data) that would only respond to simple actions. E.g. an action { "type": "TodoSuccessfullySyncedToServer", uploadedData: [...], serverResponse: [...] } would be straightforward to parse for both the localData and the dataFromServer reducer. The reducer doesn't contain any of the business logic, it receives actions one by one and all you need to think about inside the reducer is the state before the action, the action itself, and the state after the action. Anything you rely on to handle the action will be in the action itself, not in the context. So different pieces of code that generate those actions can just fire these actions without thinking, knowing that the reducer will handle them correctly.
Bloc on the other hand seems to mix business logic and updating the state. API calls are made within the event handlers, which will emit a value possibly many seconds later. So every time you return from an asynchronous call in an event handler, you need to think about how the state might have changed while that call was happening and the consequences this has on what you're currently doing. Also, an object in the state can be updated by different events that need to coordinate among themselves how to avoid conflicts while doing so.
Is there a best practice on how to avoid the complexity that brings? Is it best practice to split large events into "StartSyncToServer" and "SuccessfullySyncedToServer" events where the second behaves a lot like a Redux reducer? I don't see any of that in the examples, so is there another way this complexity is typically avoided in Bloc? Or is Bloc entirely unopinionated on these things?
I'm not looking for personal opinions here, only if there's something I missed in the Bloc manual (or other authoritative source) about how this was intended to work.

How To Cancel Firebase Query in Swift Xcode

How would you cancel a firebase query? For instance, I have two functions. In functionOne I retrieve data from Firebase Firestore. In functionTwo, I'd like to be able to cancel the retrieval process if the user hits "Cancel."
In other words, I don't want the retrieval process going on in the background still when the user cancels. I want it terminated. How can I do this?
Thanks!
I couldn't find anything on the internet about this so far.
It sounds like you have a large data set - in those cases, loading it all in at one time can overwhelm the device and cause your app to crash.
The solution is to use pagination to control the amount of data that's loaded.
While that's the main purpose of paginating your data, it also very useful when you want to give control the user and the ability to 'cancel' a query.
Simply put, if your dataset is 100,000 documents, use pagination to load in 1000 at a time and in between incrementing the pagination cursor, check to see if the user has clicked cancel.
That will be seamless to the user and will cancel when they click cancel.
This technique also enables you to add a loading bar or other indicator to the UI showing how much data has loaded.
There is no way to cancel a Firestore query once it's been started. As in: once the initial query has been sent to the server, there's no way to stop the server from executing it.
But you can stop receiving further updates by detaching any listeners from the queries.

riverpod conditional provider state updates

I'm porting my app to river_pod, it's been great so far but I always stumble upon the same problem. There is some situations where I need a provider to update its state only conditionally depending on the new value acquired by the ref.watch.
An example of this is my last road-block:
I have a ChangeNotifier provider that exposes the current user location. This provider is listened to by multiple other providers. One of them is a FutureProvider that fetches the trending posts nearby every time the location changes. The problem here is that this location updates very frequently (every 10s or so) so this fetch is done a very unnecessary amount of time.
What I would like to do in that situation is, in this FutureProvider, be able to get the new position but update only conditionally (here the condition being, if the last fetch was done more than 1km away) to avoid this unnecessary network call and all underlying UI updates it causes.
This implies two things, having access to the last state to make the comparison, and be able to cancel an update (because here even if I don't do the fetch and return the last value, the UI will still read that as an update).
I understand that those mechanisms are not built-in, so I was wondering, was is the river_pod way to approach this problem?
Cheers!
I was having same problem to solve. I had to compare old data and new data and change the state in provider only if there is a change.

Handle timeout of GNotifications in Gnome?

My program needs to react to the user not taking any action on a GNotification.
More specificially, a piece of data is written to the database only if the user does not press the "undo" button on the notification sent after the data's creation. My target deployment scenario does have notifications enabled and a real timeout value.
To be precise: Moving the notification "away" / deleting it should also count as such a timeout.
1) Is there a built-in way to 'listen' to notification timeouts?
2) If not, how could I still implement similar behavior?
I would use the D-Bus org.freedesktop.Notifications interface. Although it is still a draft specification, it does appear stable. My experience accessing the D-Bus interface using Vala has been that it is easier to use and gives the full feature set of the specification. GNotification doesn't seem to be as feature complete.
From the draft specification you will see there is an expire_timeout argument of the org.freedesktop.Notifications.Notify method. That should fit your time out requirement, although I've not used it personally. There is also a org.freedesktop.Notifications.NotificationClosed signal that will allow your program to be notified when the notification is closed, including because of a time out or if it was dismissed by the user.
This post about the screen lock re-design for GNOME Shell 3.10 might give some indication of what notifications are capable of. The post includes some screenshots of notifications appearing in the lock screen.

Replaying events - validating transitions

I'm wondering exactly what logic should be contained when applying an event to a state while replaying events using some event sourcing solution.
Specifically, I'm wondering about validation, say I've got entity which can be in one of the following status:
Logged
Active
Close
Cancelled
and the progress needs to be Logged->Active->Close or Logged->Active->Cancelled, we cannot jump from Logged to Close directly for example.
Now, I understand, the validation should be contained in commands: UpdateState would check if the entity current state allows transition to desired one, and would produce appropriate event StatusUpdated which would be persisted into the event store.
Question is, what should I do when replaying it back? Should I just update the status, or should I perform same validation (so that if status transition requirements change it won't be possible to load some previously updated entities unless we add some additional logic), to ensure we won't end up with entities that do not satisfy our current logic?
PS. I think I've got problems grasping it because in my understanding events are essentialy just about 'announcing' something that happened already (and the sender state is already modified) so that interesting parties can react accordingly. And in case of events loading/replaying, you need to alter said state instead of actually 'announce' anything...
You do not need to perform any validation when replaying the event stream.
Commands model things that will be done in the future: You ask the system do to something for you. It's up to the system to decide whether to do it or not, e.g. based on business rules and validation.
Events in contrast model things that have already happened. As in the reality, you can not change the past.
So, this means, when an event gets persisted, it was in consequence of a command which was taken as valid at the point in time when it was processed. Replaying an event stream simply means to have a look at what happened in the past, and you can not change this.
Hence, you do not need to run any validation again.
Moreover, this means that if one day your business logic changes, all the things (business accidents!) that happened in the past still have happened, so they must not change. Hence you are not allowed to use any validation logic, as it may be another one today than when it was when you stored the events.
And again: You can not (and should not) change the past :-)
Example
Supposed you have a way of validating credit card numbers. A customer comes to your shop, pays, you consider his / her card as valid given your current set of rules, and everything is fine.
Then, one day the credit card institute changes the way credit card numbers are calculated, and hence you have another validation algorithm.
When you now play back your past events, the payment had happened, with or without the new validation rules - and you can not change the fact that it had happened! If you wanted to you had to create a new transaction to send money back to the customer. Again, this would result in a new event, not in a changed one from the past.
So, to cut a long story short: Don't validate events against anything. They are valid by definition, as they had happened before.
Any event stream that's been written to the event store should be valid to be played back without introducing any logic in the event handlers. If you needed to change your transitioning process, you'd need to look at doing some sort of conversion, along the lines of this example.
Regarding your last point. Event sourcing is a technique for persisting and restoring the state of an entity using a historical record of ordered events. It just so happens that when you're saving the entity, you can also publish these events for any interested parties to consume.