Core-Data + AFNetworking + UI Updating (Responsiveness) - ios

Here's the scenario:
I'm writing a DownloadManager, that allows the user to download, pause, cancel, download all, and pause all. The DownloadManager is a singleton, and uses AFNetworking to download files. It has it's own private managed object context, so that user can freely use other parts of the application (by adding, editing, deleting) core-data objects. I have a core-data entity DownloadInfo that stores the download information i.e. fileURL, fileSize, bytesRead, etc. The DownloadManager updates the download progress in DownloadInfo (one for each file).
I have a DownloadManagerViewController which uses NSFetchedResultsController to show the download status to the user. This download view controller is using the main managed object context.
Now let's say that I have 20 files in the download queue. And let's say that only 3 concurrent downloads are allowed. The download manager should download the file, and show the download progress.
Problem:
The DownloadInfo objects are being updated by the DownloadManager at a very high rate. The DownloadManagerViewController (responsible for showing the download progress) is updating the list using NSFetchedResultsControllerDelegate methods. The result is that a lot is happening in the main queue and application has very poor responsiveness.
How can I fix this? How can I make the application responsive, while showing the download progress?
I don't know how else to communicate that the download status between DownloadManager and DownloadManagerViewController. Is there another/ a better way to do this?
I don't want to use main managed object context in my DownloadManager, for reasons mentioned above. Note, that the DownloadManager is using AFNetworking which is handling the requests asynchronously, but eventually the DownloadInfo objects are updated in the main thread (as a result of the callback methods). Maybe there's a way to handle the downloads and status update operations in a background thread? but how? How will I communicate between the main thread and the background thread i.e. how will I tell the background thread to queue another file for download?
Thanks.

Rather than observing every change to the managed object context, consider implementing one or more notifications for the events that you actually want to update the screen. If the notification are being posted from a background thread, be sure to switch back to the main thread before triggering any UI update.
Alternatively, when the FRC delegate methods are called you receive information about what actually changed. You can analyse this and filter out the most frequent and least meaningful changes and just prevent them from resulting in a UI update.

Have you tried to use Instruments to see what's really going on in your ViewController? Time Profiler is going to tell you where the CPU is spending more time and will help you to identify the root of the problem. Without knowing this, we can't know exactly what performance tweaks you should do.
After using Instruments, if the NSFetchedResultsController is forcing a lot of updates of your VC, you should consider not to read the progress from CoreData. I've seen that a lot of my tables or views spend more time reading from CoreData than performing draws. Guessing that your problem is with CoreData reads, I would try to read progress from an NSMutableDictionary and just update the UI when the downloads pass a certan threshold.

Related

How concurrency work with coredata?

I'm reading this link for concurrency in core-data
Coredata - Concurrency
My app flow is:
One thread is contentiously inserting information in the background. And the user is playing in an app and update something.
-> Due to background thread all updates will be saved in database automatically as it will save my main context. So one can not discard what the changes he made as it will save automatically.
Before I go to update my core logic of inserting information, I need your advice.
Is there any way to handle my scenario easily?
Will concurrency work with the background and main thread?
Also, let me know if any demo or tutorial you have/finds. So I may refer it.
Thanks

Core Data Stack: This Apple provided example adds the Persistent Store Coordinator to the stack asynchronously. Why?

I'm very used to creating a Core Data stack synchronously. However, I just noticed that this Apple provided example doesn't do that, instead it adds the persistent store coordinator on a background thread.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/InitializingtheCoreDataStack.html
Why?
What are the ramifications?
Will this approach 'just work' in place of a synchronous Core Data stack setup?
The call to addPersistentStoreWithType... can block if you are doing a migration or interacting with iCloud. Therefore it is safer to throw that onto a background queue so that there is no risk of blocking the UI thread.
In addition, since the applicationDidFinishLaunching is on a ticking clock, you do not want to risk blocking that method for any longer than necessary. Since the example Core Data stack is created through that method, it is best to make sure it returns as quickly as possible.
Update
Any chance of adding how to handle this to your answer - i.e. how should the asynchronous addition be handled, any best practices? I'm guessing that fetched results controllers would update once the store coordinator is added, so showing some sort of 'loading' status in the UI until then may be best. It seems like a little too much for smaller projects though... would everything 'just work' without any code to handle the background setup? e.g. do fetches block until the coordinator is added?
In general your addPersistentStore call is going to finish long before any NSFetchedResultsController hits it. If the addPersistentStore does somehow come in after the NSFetchedResultsController fires then you will need to execute a second performFetch: as Core Data does not fire any internal notifications when that action is complete.
Personally, in the background queue, I will either execute a completionBlock/closure when the addPersistentStore is done or fire off a NSNotification. But I only add that after there is an issue that I have discovered during testing. Otherwise it is just pre-optimization.

Dealing with asynchronous download delays before displaying data

My app connects to a Parse server asynchronously and downloads the necessary data into the app's Core Data store . I would then like to display this data in a tableview. But in most cases -- as the connection is asynchronous -- the table view can access the data store much faster than the downloaded does . In this situation, I get an empty table view cell, and just after that the data is ready in the data store.
What is the best way to deal with the delays caused by asynchronous downloads? Is there a concept that I'm missing? Is it NSFetchedResultsController?
What do you think is the best way to deal with the delays caused by
asynchronous downloads?
It depends on requirements you have. In particular, if the user can interact with UI during async downloads, you can do nothing on it, otherwise you could use just a spinner to alert him something is downloading and stop the interaction until the sync as finished.
Anyway, in both cases, you should say something about the download. In particular, are you saving data in a different thread (different from the main one)? If so you should merge the changes from the context you use in background to the context associated with the NSFetchedResultsController (always the main one since NSFetchedResultsController manages UI elements).
Is there a "concept" that I miss and is it NSFetchedResultsController?
Did you setup correctly the delegate NSFetchedResultsControllerDelegate? If so, the NSFetchedResultsController tracks changes on the entity you registered on your fetch request. Not changes will happen for other entities.
Asynchronous is a design problem that you will need to work with. Look to some of the other popular applications in your field and see how they solve it. Do they show a spinner (I personally hate that) or do they show some unobtrusive indicator that data is being downloaded (better)?
If you use a NSFetchedResultsController (which I am guessing by your question you are not currently) you will get the data displayed once it is saved in Core Data with no additional effort on your part. So you can at least show the data as soon as possible.
In the meantime, I recommend let the cells/table be empty and let the user know that your app is working. Display the data as soon as possible. Perhaps consider downloading the data in pieces so that they can start to see it asap.

Using GCD or not?

I have an iPhone app which pretty much is a mobile app for a website. Pretty much everything it does is call API methods from our server. The app retrieves the user's information, and keeps updating the server using the API.
My colleague and I had a discussion whether to introduce GCD to the downloading aspect on the app. My colleague argues that since the UI needs to wait for the download to complete before it can display the pictures, text or whatever, there is absolutely no need for GCD. My argument is that we should keep the main thread busy with UI rendering (even if there is no data), and introduce GCD to the app to create other threads for download.
Which argument is right here? In my case, if the UI renders with no data, will there be some sort of lag? Which will yield a cleaner, sleeker and faster app?
One argument would be : what will happen when the download fails and times out because there is a problem at the server end ?
Without GCD the app will remain blocked and will crash after a time
out since the UI can not be blocked for longer than 20 seconds.
With GCD the application remains functional but there will be no data
being downloaded and the application will not crash.
Other aspects to take into account are :
the thread safety of the objects that you are using
how you handle downloads that are no longer necessary because the user navigates away from the page
I don't think doing time consuming operations in the main thread is a good idea.
Even if user have to wait for the data te be downloaded before he can do anything meaningful, still he will not hope UI is blocked.
Let's assume you have a navigator view, and after user tap some button, you push a new view to it and start download something. If user suddenly decides he don't want to wait anymore, he tap the "back" button. If your downloading operation blocks UI, user will have to wait it to end, it's really bad.
A more appropriate question would perhaps be if you should download asynchronously or on the main thread for your app, since there are several different methods to download asynchronously on iOS (e.g. using NSThread, NSOperation or indeed GCD). An easy approach to achieve your goals could be to use the AFNetworking library. It makes multithreaded networking / internet code very easy to implement and understand.
Personally I'm very fond of GCD and recommend you learn it someday soon, though in itself it is not as suitable for asynchronous downloading compared to a library like AFNetworking (that uses GCD under the hood, I believe).
Here is a good read on using NSOperationQueues (that uses GCD behind the scenes) to download images. There is also some Github code you can check out. They have an elegant solution to pause downloads and enqueue new downloads when the user moves to different parts of your app.
http://eng.alphonsolabs.com/concurrent-downloads-using-nsoperationqueues/?utm_medium=referral&utm_source=pulsenews
Use GCD / NSOperationQueues as opposed to using NSThreads. You will have a good learning on core fundamentals and at the same time create a well architectured app. :)

Correct approach for safely saving a Core Data managed object context on a background thread?

The Apple "Concurrency with Core Data" documentation states the following when discussing using core data with background threads.
Saving in a Background Thread is Error-prone
Asynchronous queues and threads do not prevent an application from
quitting. (Specifically, all NSThread-based threads are “detached”—see
the documentation for pthread for complete details—and a process runs
only until all not-detached threads have exited.)
and in particular:
If you need to save on a background thread, you must write additional code such that the main thread prevents the application from quitting until all the save operation is complete.
What is the recommended approach for achieving this inside an IOS application?
In the app delegates applicationWillTerminate and related methods, you need to check if any background threads have unsaved changes and save them before allowing the app to terminate or go into the background.
I recommend taking a look at using Magical Record (https://github.com/magicalpanda/MagicalRecord/). It greatly simplifies dealing with core data on background threads. I recently found this and used it for a project. We've now undertaken a maintenance effort to update a variety of existing apps to use the new Magical Record Core Data wrapper. It has saved us tons of time and frustration in the few weeks we have been using it.

Resources