Pull together all the concurrency knowledge you've learned so far in this series to improve the scrolling performance of a table view in a realistic app.
Hi Audrey, I have used this technique in my apps but when you cancel your operation in didEndDisplayingCell, you don’t want the completion handler to be called in willDisplayCell, sometimes cancelling an operation is not immediate
that is why in willDisplayCell completion handler, before calling the cell.updateImage … I always check if my imageProvider is still in the array or set
I know that your image will be nil and your start the activity indicator, but that could be called twice
I notice that when using big tableView with more than 50 cells and while scrolling really fast
Has the errata been posted? I am trying to wrap my head around the issue, but I am not following. Can you break it down and explain what the problem is exactly? Is it that we are setting an image from the imageProvider after cancel has been called on the imageProvider’s queue? If so, what is wrong with that? If the Image Provider has done all the legwork of getting the image, does it really matter if we set the image?
sorry for the delay! I’m just collecting errata in my Notes, for the next update of the course.
the idea is to avoid adding unnecessary work to the main queue, if the cell has already scrolled off the screen — in this case, the cell just sets the image, but another app might do more.
having said that, I can’t see how to check if the provider is in the set, inside its completion handler … need to look at this some more …
I found your tutorial very informative and helpful.
Just a doubt that pops in my mind in course lesson 10 example is you are using operations to download the image from network, decompress, filter etc.
My question is lets say the user network is off and we need to show the error to user on the UI “The network is off”.
In the example shown can we pass the error to the main queue through operations?
hi Amit: thanks! and thanks for the question … URLSession does handle retrying background session tasks automatically when network access is slow, but it’s behind the scenes, and it doesn’t notify you.There aren’t any delegate methods related to network quality.
The only direct communication from an operation is via its completion handler, which doesn’t fire if the operation is still running. So you might have to fall back on reachability APIs and notifications, outside of the operations.
Thanks Audrey.
Do you think dispatch queue’s would be good option with completion blocks propagating the error from bottom to top of every function.?
hi Amit: how are you planning to get the error? if you have to check reachability yourself, wouldn’t it be easier to subscribe to some app-wide notification?
I guess you could implement a URLSession delegate to monitor throughput, and set a threshold to trigger a warning?
if your network task is wrapped in an operation, you’d create the operation with a completion handler that receives an error object along with what you’re hoping to get from the network.
the network task’s handler/delegate method would transfer the response’s unsuccessful status code into your error object, and call the operation’s handler.
you could try diving into Alamofire’s code, to see how they handle various errors?
The kind of examples used in this video course don’t lend themselves to error handling, but my upcoming video course on URLSession has an AsyncOperation subclass that has a completion handler input. Coming soon!
hi Zac: you need an init method to set any properties that don’t have a default value. Here, we’re assuming TiltShiftOperation will get its inputImage from another operation.
If you want to use it as a standalone operation, you could explicitly set its inputImage property after you create it, before adding it to the operation queue. Or create an init method with an inputImage parameter.
There is something I am trying to understand. Is subclassing and building the functionality in of what I want to get done in an operation a must or is just an option or a preference or simply another way to do it that is more proper?
I know you can simply add a bunch of code to be executed as an operation and add it to an OperationQueue. For example:
let queue = OperationQueue()
let operation = {
for i in 0...1000 {
if queue.operations[0].isCancelled == false {
print("A \(i)")
sleep(2)
}
}
}
queue.addOperation(operation)
queue.addOperation {
if queue.operationCount > 0 {
queue.cancelAllOperations()
}
for i in 0...1000 {
print("B \(i)")
sleep(2)
}
}
I am thinking specifically about the ability to check for whether an operation is canceled and not perform the rest of the task if it is.
Is the above example incorrect in that the entire block of code will get sent form the queue to a thread wholesale and won’t be able to stop?
Put another way, if my setup is fairly simple, can I just create a queue and operations with blocks of code and check of isCancelled state in the block or do I need to subclass operation and built the functionality into it in order to check for isCancelled so I can return to stop it?
hi Jay: subclassing Operation is useful if you want to specify parameters when you create an Operation object. If you don’t need this, then just create your Operations. You can call any Operation’s cancel method; note that this just tells it that it’s been cancelled — it doesn’t necessarily stop right away.