Best Life Cycle Method to Place Main Thread-Blocking Operation in Objective-C - ios

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

Related

How to know when iOS app becomes responsive / Hang time

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.

Parse query on Main Thread

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.

When to refresh data in iOS

I'm not sure this question is strictly limited to iOS, as it's more of a general application-design question on data integrity. But at the moment, I'm getting into iOS and have hit a "best practice" wall.
If we use the Twitter app as an example. We have one tab for our timeline and one tab for our profile. So (to keep things very simple) each one is a View Controller, backed by an object (model). I tweet on the timeline VC, then I head on over to my profile tab. Both viewDidLoad methods have already ran, so the data that was loaded to draw the UI is currently stale. The "count" of my tweets is now out of date. In the iOS world, what is the best methods/approaches to keeping the VC model data synced with the backend?
Is it on a time interval? Or network requests in the viewWillAppear method? Is it event driven, ie. when I tweet in one VC, and it's been saved in the backend web service, I notify any VCs that care that there's a new tweet
I'm not 100% sure this question will have an "answer" in the SO sense, but I'm just trying to understand what's the done thing in the iOS world (as someone who comes from the web development world).
Here's a loose sketch of some things our team has done on a networking application that might give you insight for your own architecture.
At the level of the VC (in viewWillAppear) we typically have multiple NSNotificationCenter observers that will call whatever update methods you need to run after a network update call.
Firing off these notifications is some network listener that lives on a background thread. It's job is to wait for responses from the backend server (typically JSON blobs) that contain updates to the data model. I believe ours has a time interval that will periodically phone home and check to see if we have new things to update.
Note you will necessarily have to devise an asynchronous solution for network calls as the passing/receiving of data packs can be unreliable, and take time, and thus should be computed in the background. You'll also need a way to handle data loss and other errors between server and device. And of course in order to see UI updates, you'll need to switch back to the main thread when updating the VC.
Assuming a some change of state (i.e. new message), then it makes the call to get the new data, which then fires off an NSNotification (with a new payload of info), that goes to the observer on the VC.
As you mentioned with viewWillAppear, UIViewControllers are a core building block of iOS apps. They have a lifecycle to follow to help with things like this scenario.
You have many options, but the best practices would to make calls on the viewWillAppear method and then reload your table views, collection views, etc.
Another thing you can add is push notifications on data change. You can now send a push notification with the "content-available" option that is silent and sent to your app when data changes on the backend. Then your app can refresh data only when needed.
I would stay away from timers. They will keep making network request and keep the radio from going into a battery saving idle mode.

Stop child threads started by `performSelector` on `viewDidDisappear`

To begin, I realise I may be causing this problem by addressing a previous problem incorrectly...
In a ViewController main thread I am starting a background thread to get updated data from a server using:
[self performSelectorInBackground:#selector(sampleTask:) withObject:#"CMD" ];
This process can take 15-30 seconds so in the main thread I display a local cache of the data from a SQLite database (populated from a previous request to the server the last time the view was opened) and reload the table once the sync with the server is finished.
If the user navigates back out of this view before the sync with the server is finished, the background thread keeps running until it is done. This itself isn't a problem, until the user changes their mind and goes back into this view again. If the timing is right, there's two background threads trying to sync data with the server. If the user does this a few times, the thread count can build up. Eventually the app will collapse, if not cause other problems on the device.
Is there a way to stop the created threads on a trigger like viewDidDisappear?
Or should I be writing a lock to a shared resource (e.g. NSUserDefaults) to prevent a new background thread from being started?
Or -- like I mentioned in the first line -- do I have a bad approach to the issue of updating the local cache that is just causing further problems like this one?
I think you can use simple bool valued semaphore which shows that some sync task is performing. So before performing the next similar task you should check that semaphore.
If you recreate your viewcontroller each time you need static class variable common for all instances of your view controller.
performSelector:withObject:afterDelay: does not create a separate thread.
To quote part of the docs on that method:
Invokes a method of the receiver on the current thread using the
default mode after a delay
It uses a timer to trigger your selector after a delay. As such it is subject to the same accuracy limitations as NSTimer. (The method performSelectorInBackground:withObject: does submit your selector on a background thread.)
But back to your question.
You can use the method
cancelPreviousPerformRequestsWithTarget:selector:object:
To cancel a pending call to performSelector:withObject:afterDelay:

How can I change the background operation priority dynamically using Dispatch or Operation queues.

Here is the problem that I got. I have several tasks to complete in background when application is running. When I run these tasks in background by pushing them to concurrent dispatch queue it takes more then 10 seconds to complete all of them. They basically load data from disk and parse it and represent the result to the user. That is they are just cached results and hugely improve the user experience.
This cached results are used in a particular functionality inside the app, and when that functionality is not used immediately after opening the application, it is not a problem that it takes 10 seconds to load the data that supports that functionality, because when user decides to use it, that data will already be loaded.
But when user immediately enters that function in the app after opening it, it takes considerable time (from the point of view of the user) to load the data. Also the whole data is not needed at the same moment, but rather the piece of it at a given moment.
That's why we need concurrently load the data, and if possible bring the results as soon as possible. That's why I decided to break the data into chunks, and when user requests the data, we should load the corresponding chunk by background thread and give that thread the highest priority. I'll explain what I mean.
Imagine there are 100 pieces of data and it takes more than 10 seconds to load them all. Whenever user queries the data first time, the app determines which chunk of the data user needs and starts loading that chunk. After that part is loaded the remaining data will also be loaded in the background, in order to make later queries faster (without the lag of loading the cache). But here a problem occurs, when user decides to change the query immediately after he has already entered one, and that change occurs for instance on the 2nd second of data loading process (remember it takes more than 10 seconds to load the data and we still have more than 8 seconds to complete the loading process), then in the extreme case user will receive his data waiting until all data will be loaded. That's way I need somehow manage the execution of the background tasks. That is, when user changes the input, I should change the priorities of execution, and give the thread that loads the corresponding chunk the highest priority without stopping it, so it will receive more processor time, and will finish sooner, and deliver results to the user faster, than it would if I have left the priorities the same. I know I can assign priorities to queues. But is there a way that I can change them dynamically while they are still executing?
Or do I need to implement custom thread management, in order to implement these behaviour? I really don't want to dive into thread management, and will be glad if it is possible to implement using only dispatch or operation queues.
I hope I've described the problem well. If not please comment bellow what is unclear, I'll explain.
Thank you so much for reading so far :) And special thanks to one who will provide an answer. And very special thanks to one, who will give me solution using dispatch or operation queues :)))
I think you need to move away from thinking about the priority at which the queues are running (which actually doesn't sound very important for the scenario you are describing) and more towards how you can use Dispatch I/O or an even simpler Dispatch source to control how the data is being read in. As you say, it takes 10 seconds the load the data and if the user suddenly changes their query immediately after asking, you need to essentially stop reading the data for the previous request and do whatever needs to be done to fulfill the most recent query. Using Dispatch I/O to chunk the data (asynchronously) and update the UI also asynchronously will allow you to change your mind mid-stream (using some sort of semaphore or cancellation flag) and either continue to trickle the data in (you don't say whether or not that data will remain useful if the user changes their mind or not), suspend the reading process, or cancel it altogether and start a new operation. Eithe way, being able to suspend/resume a source and also have it fire callbacks for reasonably small chunks of data will certainly enable you to make decisions on a much more granular chunk of time than 8 seconds!
I'm afraid the only way to do that is to cancel running operation before starting new one.
You cannot remove it from queue until it's done or canceled.
As an improvement for your problem I would suggest to load things even user doesn't need them in background - so you can load them from cache after it's there.
You can create 2 NSOperationQueue with 2 different priorities and download things in background whenever user is idle on LowPriorityQueue. For important operations you can have high priority queue - which you will cancel each time search term changes.
On top of that you just need to cache results from both of those queues.

Resources