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.
Related
Is there a way to know when an iOS app becomes responsive to user interaction? For example, a user taps a button and the app performs work, this work may dispatch other work asynchronously to the main thread. In hopes of using it as a performance metric, I want to know the precise moment at which the app is again able to process touch events in a responsive manner. With this I would want data like "On average, the app becomes responsive 55ms after a user interaction".
Currently, immediately after a user interaction, I watch the main queue and have a heuristic for submitting samples to it in order to estimate responsiveness based on the main queue's responsiveness, with the assumption that the main queue's responsiveness directly correlates with the apps' responsiveness. The sampling only occurs until the queue is again consistently responsive for some time again (ex. 100ms). Is there any downside to this method? Is there any other method I could/should be using to do this?
Using MetricKit to watch for Hang Time is not an option as I cannot those results to a specific interaction (i.e. knowing how different interactions affect hang time).
You said:
For example, a user taps a button and the app performs work. I want to know the precise moment at which the app is again able to process touch events in a responsive manner.
The main thread should never be blocked. It should always be responsive. (You can disable the UI if your app requires that, but never block the main thread, regardless.)
So, with that in mind, if you are starting some process that takes a little time, you should:
If you want the app to let the user know that a time consuming process is about to start, add that chrome to the UI (e.g. UIActivityIndicatorView, aka a “spinner”, or whatever);
Start that task, asynchronously, on a background queue (so that it does not block the main thread);
Give that task a “completion handler” closure that it will call when the background work is done;
In that completion handler, the caller can supply the code to remove any chrome added in the first step, above.
In short, rather than worrying about “how does the app know when the main thread is free again”, you should focus on eliminating anything that would ever block the main thread in the first place. See Understand and eliminate hangs from your app.
My app syncs with a server similar to the Apple Mail app. While the sync takes place on a background thread, because it is hitting Core Data rather hard, I've found it necessary to block interaction with user controls during the sync, lest some other operation hit core data and create problems.
I had been putting the sync in View Will Appear to keep the phone and server in constant sync. However, with larger amounts of data, I'm noticing that the sync is unacceptably long...that is it ties up the thread for five or ten seconds. I tried placing it in viewdidload so it gets called less often but it is still annoying to wait when you have just opened app.
I've noticed that Apple does not sync mail immediately but waits a few seconds so as not to tie up the app at first. This gives you the illusion, you don't have to wait (although in reality you usually do).
I'm wondering if there is a place in the life cycle that would be better for syncing such as viewdidappear and also whether there is a way to use a delay to launch the sync five or ten seconds after you are in the view controller when it's less conspicuous.
Thanks in advance for any suggestions.
Firstly blocking the main thread isn't preferred under any circumstances for asynchronous operations , as the user will think that the app is hanging and will quit it
Secondly viewDidAppear is meant for updates when say it's in a vc that navigation returns to with back to refresh content or dismissing a model , other than those 2 things it will act like viewDidLoad with the overhead of delay
Finally if you need to sync the mails with server you have 2 options
grab data every fixed time ( not recommeneded ) say with a timer
use silent push notification to notify the app with server new content and initiating the pull process upon receiving it
I am populating my core data with thousands of records in multiple entities one by one. This process is taking time to complete. This process is working fine when app is in active state. Once app goes in background and comes in foreground, the app is crashing with following error:
"failed to resume in time ios crash"
Please suggest some solution.
Thanks
The creating of the Core Data stack should never be done in the -applicationDidFinishLaunching If a migration is needed just inform the user about it but you need to return from the -applicationDidFinishLaunching... as fast as possible, you should only be creating UI elements in this method. You should not be accessing Core Data at this point.
You should not do very time-consuming tasks on the main thread. The crash is due to the populating process taking more than 10 seconds. It happened to me once in an infinite loop.
Start a new thread for this task, which will resume when your app enters foreground again. Tell the user what's going on, i.e. that the app is populating a database and that it may take some time. It is a good idea to show a progress indicator too.
As long as the user is kept well informed, he will accept to be patient.
my app is based on data, that I get from the web, in XML.
I implemented the NSXMLParser, and it works really good. the only (major) problem, is that
the launch of the app takes about 25 seconds!!! (the parser needs to parse 30 objects, each object has 5-7 elements- all are url's/strings).
so, it takes long time to start/end element, parse it, insert it to the right array, and so on...
Does parsing with Gdata or other api/object will take less time?
Downloading data from the Internet, on the main thread, when launching the app is VERY BAD. If a user has a slow (or no) connection, iOS will actually kill the app before it finishes because it is taking too long to respond.
You must launch your app very quickly and show the user the initial view without any delay.
In your case, show a mostly empty view indicating that it is accessing data. Then start the file download and processing in the background. When the data is processed, then update the main view on the main thread allowing the user to continue.
You should also consider supplying the app with some initial default data so you app is usable and useful even if the user can't connect to the Internet. This default data could also be whatever was downloaded the last time the app was used.
Parsing on the background thread will make your app more responsive.
I have what sounded like a really simple issue at first, and I think still is, but I'm missing the answer.
When my app logs in, I start a background task to download some data from the server. If that data isn't downloaded by the time the user taps one of two buttons, I want to put up a spinner view and wait until that task is finished. Once finished, remove the spinner view and then continue to push the view controller.
What I seem to be missing is how to do this and wait so that the main thread isn't blocked.
Once the data is downloaded, I can set a global flag, or send out a notification, but the view controller has to have a way to wait on that condition to either be set, or to know that there was a download error.
Any thoughts?
Stack overflow is not meant to be used in the way you're using it, so you're going to get downvoted.
But, what you are looking for is dispatch_async. You use it to create queues.
If you need more information, this page has a good explanation, as well as this page.