In the comments of this answer I was having a discussion about backgroundTasks which eventually led to:
Use backgroundTasks for anything that isn't related to download/upload. For upload/download use NSURLSessions's backgroundSessions. Then I made another comment asking why not use background Sessions for all types of requests and was told:
For regular REST calls, background sessions are much less convenient,
and generally not what you'd want. They're not a general purpose tool
for every request; they're for performing uploads and downloads.
What makes background sessions less convenient for REST calls? Sometimes you may have slow internet with a huge chunk of data. Wouldn't it be a convenience to make sure all your data submissions go through?
I'm not sure but if you're doing something like a bank transaction, you wouldn't want to use a backgroundSession. Because you want the user to know of the decision before they leave. User should never the assumption that they could leave app and app would continue to work as should. Nor they should be under the impression that they can resume (by a downloadTaskWithUrl).
What happens if for some reason the user makes a $2000 transaction and hits the button expecting the transaction to go through but it doesn't. The next time the user comes back to the screen they could either be logged out due to security reasons and never know about it or stay logged in but they see an alert that the transaction failed. And now they're like "Oh no my daughter needed $2000 urgently. She must be still waiting for the money!".
You don't want to allow users to have bad expectations*. Rather you want users to take FULL responsibility themselves and not hit home screen and wait for the success/failure. So once the user clicks on submit transfer he'd wait for it to either get success and move on or see failure and wait and investigate the reason his transaction didn't go through.
You usually convey the possibility of failure through an spinner/animation and the actual outcome (failure or success) through an alert.
*Bad expectation is: Every time I hit the the submit transfer button it will go through and there's 0 chance of failure and no need for you to wait and see it go through.
Related
Why should I remove firebase references observers? I'm ok if all references get observed as long as the app is running. I'm not sure if references will still be observed after the user leaves the app, but if memory is only consumed while the user is using the app, why bother remove the observers? Especially if the data is mostly light weight texts.
I've looked into firebase documentation and there is no clear info about it.
There are several snippets saying that is a good practice to remove them on the opposite method you created them.
If you create them on onCreate, remove them on onDestroy. If you created them on viewWillAppear, remove them on viewWillDisappear.
In my experience I found out that is less cpu demanding to just leave them around if they are not firing very often. I have several observers on my app it's less cpu intensive to just quit the app than to remove them.
The case is not what can happen if the user leaves the app, after it's closed/killed it'll not be observed anymore. Neither is a problem because of CPU or internet usage, since you'll get light weight texts. The case is what can happen if the user is using the app and an observable is fired.
Let's say that in your app's home page you'll have an observable sending the user to a specific page if a value on the user's node changes, and if in the middle of the usage this value changes the user may be redirected to another page without knowing what happened, so bad UX.
Something that happened to me was leaving observables and them i signed out of my account, my app broke because there was no user connected, therefore i wasn't able to observe a node where i needed to be authenticated to query a value.
So that's why you should remove every observable as you go off a page or when quiting the app or signing out, because it can give the user some errors or uncommom behaviours that'll make them think that the app is broken or is not a good app.
Hope this helps :D
I run a marketplace iOS app and from time to time we have "competitions", where we have an especially sought after item for sale for a good price, that drops as a specific time. Sometimes thousands of people will try to buy this item within 1-2 seconds and I therefore need to make sure that only 1 person will get the item. The solution I have for it now feels kind of clumsy, so I was wondering how a good solution would look like when I use Firebase as my database.
The process is as such:
User finds the item on his iOS app and clicks "Purchase".
A request is sent to our API (build on RoR) that processes the purchase (usually takes 10-20 seconds for the purchase to go through).
Right now, I set the buyers ID temporarily as an attribute on the item, I wait a second and check whether the buyer ID is still the same on the item. It works, but it doesn't feel optimal.
Any suggestions on how I can make sure 2 people can't purchase the same item?
To avoid something like this in your rails app, the keywords mutex and race condition should probably help you to find a bunch of appropriate gems.
I personally like to use redis for this kind of task, because in redis, transactions are atomic by default (https://en.wikipedia.org/wiki/Atomicity_(database_systems)).
So maybe this gem could suit your needs (untested): https://github.com/kenn/redis-mutex.
For the theory, refer to this articles:
https://en.wikipedia.org/wiki/Mutual_exclusion
https://en.wikipedia.org/wiki/Race_condition
Store in /items/foo
a record with the structure:
{id:<blah>, available: <timestamp>, (purchaser: null)}
let buyers write their user name to to buy:
/item/foo/purchaser
You want 3 things to happen.
Block someone writing before the servers timestamp of available
only allow 1 person to do the operation. Once the /item/foo/purchaser is set, you don't want it modifiable (i.e. write once)
only allow the authenticated user id to be used in the purchaser field
To enforce this logic you use security rules, on the subpath of "/items/$itemid/purchaser"
".write": "now > data.parent().child('available').val()" +// 1.
"&& data.val() == null" + // 2.
"&& newData.val() == auth.id" // 3.
My guess is tht you should use locks.
On a request coming in, check if you can acquire a lock. If yes, the the user is the first one. Then, the next requests won't be able to acquire the lock. This means the product as already been purchased.
Take a look at this redis doc part : http://redis.io/topics/distlock
At the application(RoR) level, you can set a flag(eg: lock_foo=true) that is shared across the cluster(can be in your cache store).
If this value is true, don't allow any other users to access the product/make the purchase.
You can definitely implement this with Firebase. As dvxam and Anshul Mengi mentioned, a lock system is the good way to go:
You could have on the document a property called lock:
{
"lock": {
"userId": "myUserId",
"expiresAt": "myTimestamp"
}
}
When a user clicks on the purchase button, you can use a Firebase transaction to make sure only one user can get the lock and that the first one gets it.
When another user clicks the purchase button, if a non-expired lock is present with a different userId, you can deny the purchase.
When the user completes the purchase you can then use another transaction to check if it is the same userId and if the lock is not expired.
Transactions are absolutely necessary here, and they are not available on the Firebase REST api (hence no more in the ruby wrapper), so you would need to run this code client-side using the iOS SDK, or to spin a nodeJS server for this task.
Hope it helps.
How about this for different:
When user clicks purchase, immediately create a purchase request record that contains product, user and timestamp, and then poll every few seconds to see if the purchase was successful
Run a background job that searches for un-purchased products that have at least one purchase request against them, and marks the product as purchased (selecting one purchase request / user as the "winner")
I'm not sure if there's a specific pattern I can apply in this case or how this is "normally" solved?
I can't speak to Firebase, but I can definitely speak to how this is "normally" solved in Rails and relational databases.
Before jumping in to code, note that it seems like you need linearizability, one of the hardest things to ask of a database, and some databases can't guarantee it even when they say they do. You might be able to hack around needing linearizability if all you need to know is whether it's been purchased or not, but I wouldn't take that hack lightly. Consistency in distributed systems is a really complex and edge-case-ridden topic, especially while under load (which it sounds like you'll be).
In Rails+RDB (postgres, mysql, sqlite) an atomic, linearized quantity update looks roughly like this (with some rails validation niceities thrown in):
class Product
validates :quantity, numericality: {greater_than: 0}, on: :purchase
def purchase
with_lock do # simultaneously aquires a lock and reloads the model
return false if !valid?(:purchase) # immediately release the lock if not valid
update_attribute(:quantity, quantity - 1) # saves without validation; YYMV
end
end
end
This general pattern of "lock+reload -> check -> update" is the gold standard for reliability, but it's "heavy." The first object to acquire the lock will win, but while it's doing its thing, all the other processes asking for a lock will be in queue. Somewhere there's a timeout and max connection pool defined, so if say 4000 locks are asked for within 1 second but it takes 10 seconds to determine success, you'll need 4000 connections and, even worse, the last lock asked for will be waiting for over 11 hours! That will make managing the connection pools and setting reasonable timeouts challenging.
The benefits, though, are that it will "just work" - if the first purchase fails, the next purchase will acquire a lock, and so on, until someone wins. Then, it will return helpful ActiveModel errors to everyone else in the queue. Additionally, it's simple enough code-wise that you know as long as your database provides linearizability, you're in the clear.
To mitigate the 11-hours issue hopefully you can very quickly deny everyone with outstanding locks to flush the queue.
I don't know exactly what you're doing while you try to make a purchase, but if it was just a credit card validation and a data update, I'd highly recommend the approach I've outlined with a database known to be linearizably consistent. Otherwise, you're going to need to consult a true distributed systems expert or run your users under the bus figuring this out.
In this document describing the lifecycle of a Windows 10 UWP app, it states:
Users now expect your app to remember its state as they multitask on their device. For example, they expect the page to be scrolled to the same position and all of the controls to be in the same state as before. By understanding the application lifecycle of launching, suspending, and resuming, you can provide this kind of seamless behavior.
However, there doesn't appear to be much documentation on how this is actually achieved. I gather that everything is to be manually saved by the app developer, and then recreated from scratch on resume using whatever data you stashed away when the app was suspending, all in order to create the illusion that the exact memory state of the app never changed.
I'm trying to puzzle through this using just a minimal example, a XAML page containing nothing other than a TextBox. Even this situation, though, I'm struggling a bit to understand how to achieve the goal. I'll provide more general thoughts, but my concrete question simply is how do you save and then restore a simple TextBox for resume from termination? I'm working in C++/CX but will take any help I can get.
Here are my thoughts on this so far:
At minimum, obviously the text of the TextBox has to be saved.
This could be saved into the ApplicationData::Current->LocalSettings.
One issue I see immediately is that the document I cited above on lifecycles states that apps must take care of their saving within 5 seconds of the suspend signal or face termination. A Textbox could potentially hold a lot of data, causing a save to potentially be cutoff in the face of busy IO, particularly if we start scaling beyond the trivial single TextBox situation.
Fortunately, the document states, "We recommended that you use the application data APIs for this purpose because they are guaranteed to complete before the app enters the Suspended state. For more info, see Accessing app data with the UWP app." Unfortunately, when you follow that link, there is nothing relevant there providing any more detail, and I can't find anything documenting this behavior in the API's. By saving into ApplicationData::Current->LocalSettings are we safe from being cut off with corrupted or lost data?
Once the minimum has been taken care of, next we'll probably need extras like cursor and window position.
We can get the cursor position with TextBox->SelectionStart, which as far as I can tell, is undocumented in the API of this usage of returning the current cursor position. This seems an easy fit to also store as an int into ApplicationData::Current->LocalSettings.
How can we get, save, and restore the scroll position of the TextBox window?
Now that we've got the extras, how about the hard stuff, like undo history? I'm assuming this is impossible as my question on Stackoverflow on how to access the TextBox's undo facility has gotten no answers. Nonetheless, it does seem like a poor user-experience if they swap to another app, come back thinking that the app never closed due to the beautiful and seamless restore from termination we implemented, and their undo history has been wiped out.
Is there anything else that would need to be saved for the TextBox to create the ideal user-experience? Am I missing something or is there an easier way to do all this? How would something like Microsoft's Edge Browser handle the complex case where there are dozens of tabs, form inputs, scroll positions, etc. that all need to be saved in 5 seconds?
The App lifecyle document you reference has been updated for Windows 10, but seems to have lost some of the important bits that you are wondering about.
I found an old blog post, Managing app lifecycle so your apps feel "always alive", that seems to be the inspiration for your link.
In the blog post, there is a paragraph towards the end that reads:
Save the right data at the right time
Always save important data incrementally throughout the life of your app. Because your app has only up to five seconds to run suspending event handler code, you need to ensure that your important app data has been saved to persistent storage by the time it is suspended.
There are two types of data for you to manage as you write your app: session data and user data. Session data is temporary data that is relevant to the user’s current experience in your app. For example, the current stock the user is viewing, the current page the user is reading in an eBook or the scroll position in a long list of items. User data is persistent and must always be accessible to the user, no matter what. For example, an in-progress document that the user is typing, a photo that was taken in the app or the user’s progress in your game.
Given the above, I'll attempt to answer your questions:
how do you save and then restore a simple TextBox for resume from termination?
As the end user is typing in the TextBox, the app saves the contents in the background to the data store. To borrow from how word processing software works, you auto-save the textbox "document". I would consider the textbox content to be what the blog post above describes as "user data". Since the save is done outside of suspension, there is no time window to worry about.
When your app resumes from termination, it checks the data store and loads any data into the textbox.
Once the minimum has been taken care of, next we'll probably need extras like cursor and window position.
I would consider these items "session data" and would save them during suspension. After all there is no need to keep track of this info while the app is active. The user doesn't care where the cursor was 10 minutes ago when he started typing - he only cares about the cursor position at the time of suspension.
how about the hard stuff, like undo history?
I would consider undo history to be "user data" and would save it while it is happening (outside of suspension). In other words, as the user types in content, your app should be saving the information necessary to undo.
I have a JSF 2/Primefaces/JBoss application that has to run some long term processes from time to time. Specification says that once the process is started, its progress has to be monitored, showing the occurrence of intermediary phases of it.
The total count of steps is retrieved in the beginning and a progress bar has to show the process evolution and and estimate of the remaining time, based on the statistics calculated during the process execution. It's not like only showing that something is happening, but actually showing how much the process still remains to be done.
Even if the user closes the page, the process has to continue and if the user gets back to the page, it has to show the current progress status of the process. If the process is already finished, then some informations should be presented, like the number of objects processed and, the total time taken and some other statistics, like the object that took longer to be processed.
How to accomplish that in a JSF 2/Primefaces/JBoss application?
You question can be broken down into two main problems.
1. How to log and track the status of a job.
2. How to present this information in the UI.
Depending on your requirements and your JBoss version, you may want to consider using a managed thread. You must decide how you will track the process steps. You could log each completed step in a database or keep it in memory. How will errors be handled? What if the process does not complete? Once you have these back end design decisions completed and implemented you just need to figure out what you want the UI to be like.
As mentioned in the comments section, primefaces offers a couple different options such as polling or server sent events. If you are using polling leaving the page and revisiting should already be handled by presenting the result for any point in time. You would then just need to refresh the view on an interval.
My iOS app relies heavy on server side data, and just for the launch of it, I need a little bit of information from Parse to get the job done on the app delegate... the issue is I'm making this query on the main thread because otherwise I would use a block or a queue, and immediately after the start of the app, the launch image shows up, then the query starts and the screen goes blank, then the query arrives and the app screen refreshes and is ready to go, but this looks very odd for the user experience and I don't want it to happen..
With the query on the main thread the launch image stays until the data arrives, and it looks much better and the loading time is about 2-3 seconds...
It feels like a bad practice, but...
Any advices?
Regards,
Miguel Rojas Cortés
Don't block the main thread when the app launches. If the network request isn't fast enough, the watchdog will terminate your app and your users will give you 1 star reviews.
Just display your UI with as much info as you have, and show some visual indication that more data is loading. Then update the views when the data arrives.
Also remember to handle the case that the user launches your app with no connectivity. The user should get an appropriate error and an option to retry.
You just need to do a bit more work here
Create a separate nib / view controller for the launch screen instead of the using the default iOS one
When the launch view controller loaded, starts the request, and don't do any transition just yet. Maybe show some kind of loading indicator there.
When all data has arrived, do a transition to the first screen (either fading smoothly or abruptly, IDK).
Doing query on main thread in this case may work 90% of the time, but the other 10%, eg when network is flaky, it's not a nice experience. The app will just hang there, and you got no chance to handle returned errors, since the main thread is blocked.