NSOperationQueue blocking tableview refresh - uitableview

I have been using NSOperationQueue to download some data from server on background.
i have multiple data to be downloaded .So multiple operation is added to queue for each data download
there UITableview which i need to refresh once single data is received.
I have used KVO approache to track "isFinished" key for each operation finish.
But when i refresh tableview all delgates for tableview is called but table isnt refreshed.
i also see from my console logs that even when tableview isnt refreshed completely,i see logs from worker thread (is used for background download of data).
So thats why my table isnt refresh completely?
What could be work around for it?

Would be nice to see some code, but one of the possible reasons could be that you are running reloadData from a background queue.
Try this:
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});

Related

UITableView does not load the new state if user scrolling

SOLVED: It is not about UITableViewDiffableDataSource. The problem was accessing realm from 2 different thread sequentially and not getting consistent result
One of the thread was main thread and scrolling somehow kept main thread busy and triggered race condition
Everything works if user not scrolling tableview when update is happening.
If user is scrolling or just have finger on the tableview, no animations happening on update and differences does not show up.
I am not getting any error in the console
Data update code is like below:
var snapshot = tableViewDataSource.snapshot()
snapshot.deleteAllItems()
snapshot.appendSections([.conversation])
snapshot.appendItems(conversationList, toSection: .conversation)
tableViewDataSource.apply(snapshot)
Is this somehow an expected behavior?
In my experience, you have to be cognizant of when UI updates occur. UI updates always occur on the main thread.
When the user is actively scrolling, I believe this blocks the main thread, so your diffable data source is likely updating, but your app cannot update the UI until the user releases his/her finger from the display.

How to check dispatch_async get called or not earlier for same operation

I faced a problem. My application downloading images from server and showing in a UITableView For downloading the images I am using dispatch_async.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// downloading image from server
dispatch_async(dispatch_get_main_queue(), ^{
// update UI for showing downloaded image
});
});
Now the problem is if user scroll up and down very fast then multiple dispatch_async get fired for the same image. I have image id. Is there any way to check whether a dispatch_async get fired or not for that image or not by image id?
You'd have to set up your own mechanism for keeping track of whether there were any pending requests for that image (e.g. a dictionary keyed by the image URL absolute string or something like that). The thing is, it's not as simple as that, as the cell that now needs to be updated may be different than (or in addition to) the cell for which it was originally requested. This gets ugly quickly.
But I'd step back and ask whether this is the root problem, or a symptom of a broader problem. For example, if the user scrolls down relatively quickly to the 100th row. Is that image request backlogged behind the previous 99 rows? When you're in that sort of situation, the problem you describe starts to become serious, because if you now scroll quickly back up to the top before the other 99 images have finished downloading, you'll be issuing requests to get them again.
But, if you change your asynchronous image retrieval to cancel requests for cells that are not visible, suddenly this serious problem becomes somewhat academic. Plus, you have the added benefit that the image for the 100th row that the user rapidly scrolled to is not backlogged behind all of the other image requests, so it appears quite quickly.
This means that you want to employ cancelable asynchronous requests. This would therefore suggest using NSURLSession (or delegate-based NSURLConnection if you have to support OS versions that predate NSURLSession). Also, this would generally make one lean towards operation queues and subclassed asynchronous NSOperation subclass rather than GCD queue suggested by your question (to make them cancelable, asynchronous, and also to constrain the degree of concurrency). And then you have to then implement the code that makes use of all of this cancellation logic that you've so industriously created (e.g. UIImageView category or some image request manager that your UITableViewDataSource and/or UITableViewCell subclass uses).
Also, you want to make sure that you employ a cache mechanism (possibly both to memory and persistent storage). This way, if you've already retrieved a particular image, when you scroll back to that row, the image is ready for you to retrieve it, not requiring another network call.
I know that you have said that you want to do this yourself, but this is a non-trivial amount of work to do properly. This is why I (and others) suggest you consider checking out one of the UIImageView categories that allow you to asynchronously retrieve an image. See the UIImageView categories provided by SDWebImage or AFNetworking. If you're properly dequeuing cells, these achieve very responsive UI while retrieving the images asynchronously. They effectively cancel requests for cells that are reused, thereby prioritizing visible cells. They also cache results resulting in good performance as you scroll back.
For that you should store in some place the downloaded images or mark if there is a current operation for them. So you will be doing basically a image cache mechanism. I will advise that instead to do that you can use a well tested library for that like SDWebImage that precisely deal with download images and cached them to memory or disk.
Keep it simple: use NSURLSession rather than rolling your own attempt at asynchronous URL fetches. Rely on its caching to avoid repeat downloads where the previous download completed. Then all you have to worry about is repeat requests for downloads that are currently ongoing.
The easiest thing is probably just to keep two dictionaries: URL to NSURLSessionDataTask and URL to array of completion handlers.
When a cell wants a URL it calls into the handler for those two dictionaries, supplying a completion handler.
If there's no data task for that URL, the handler creates one and starts it, also creating a dictionary containing just that completion handler. Both are put into their respective dictionaries.
If there is a data task for that URL, the completion handler is added to the array in the second dictionary.
Tasks are created with a completion block that calls the handler, which upon receipt will remove the task from the first dictionary and call everything in the array in the second dictionary, then removing the array from there.
Given that you're view related, the handler permits external calls from the main queue only. It will ensure that the work subsequent to a completed data task is also performed on the main queue. That means you'll have no concurrency issues.

How To Update UIProgressView after UITextField DidEndOnExit

I have a UITextField, which checks a password and then my app loads data from a remote server. While this is happening I would like a progress view to display the progress of the download. My issue is that the UITextField seems to lock up the display until it returns and therefore I cannot update the progress view on the main thread. I also would like an answer without GCD or any other kind of threading as I am using core data and that would probably overcomplicate the app. Is there a way to have the UITextField not lock up the view so that I can update my progressView on the main thread?
If your app is loading data from a remote server, then you will have to use multi-threading(GCD, etc). Otherwise it just locks up the main thread till the download is finished which makes your app unresponsive.
To keep it simple, use GCD to fetch data(raw NSData) and give it to the main thread. Do all your processing on the main thread(core data, etc) as usual.
EDIT: One more thing, it is not the textfield locking up your UI, it is the download. So I don't think you can do anything other than multi-threading to help you here.

Asynchronous (non-blocking) display of alert in iPhone App

In my iPad App, am connecting to a web service. Whilst connecting to it, am displaying the progress activity indicator and a corresponding message to the user in a label (the label is in a subview and am adding the subview to the current view).
After this line of code (which calls a method to add the subview to the view), am invoking the method to call the web service. However, the web service call is getting executed first, and then only the user-information subview is displayed.
Is there any way to say that I want to 'continue displaying' the alert view even while the execution continues to the next line of code?
// Calling method to add info/alert subview to current view [self displayUserMessage];
// Connect to Web Service [self connectToWebService];
I'm not sure if I totally understand your question. Also it's far more easy to understand if you provide some code after your explanation... Anyway what I understand is that you are connecting to a web service and showing some info while the connection is on going?
Remember that if you don't want to hang your user interface you need to send the webService Connection in another thread, so you can keep the main thread free. You can do so using GCD.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self connectWithWebService];
});
Then depending on the architecture of the web service, you can use a delegate o maybe a completion block to show some messages (info/alert) to the user. In that case remember that anything related to UI should run on the main thread. So as I said before depending on your architecture you should do something like this
dispatch_async(dispatch_get_main_queue(), ^{
// Show UI Changes
});
The UI should update properly while the webService method is running on background.
If you want asynchronous connections its easier to go with NSURLConnection's sendAsynchronousRequest:queue:completionHandler:..
you can display your alert before calling it and dismiss it in the completion handler.

Terminating asynchronous NSURLConnection loading prematurely

I'm making an app that loads data asynchronously for a data picker, then reloads the data picker, and everything's fine.
The problem is that if the user taps on the text field, the app makes the network request for the info, but if it hasn't loaded and then the user taps the back button, then the view disappears, but the data is still loading in the background.
I've tried running this loading method on a custom thread and then calling [myThread cancel]; on viewWillDisappear, but the data still loads. I want to avoid the user using data when he cleary doesn't want to load it anymore. Any help?
I was thinking of killing com.apple.nsurlconnectionloader but I have no idea how to do that.
Few things to note here, canceling an NSThread will not terminate it immediately, it will only mark it for termination.
What I would do instead is implement your NSURLConnection delegates within an NSOperation, and when you want to cancel your download you can call cancel on the operation, which will set its isCanceled property to YES. You can then use this as a flag to terminate the operation's runloop, and the download will not continue.
Apple has an excellent technote on using NSOperation here https://developer.apple.com/library/ios/#technotes/tn2109/_index.html

Resources