I want to add images in array in sequence after downloading. I am appending images in array after downloading one by one but they are not in sequence. Can any one please tell me what is best way to do this.
var queue: NSOperationQueue = {
let _queue = NSOperationQueue()
_queue.maxConcurrentOperationCount = 4
return _queue
}()
var imageArrayNsData : [NSData] = []
let session = NSURLSession.sharedSession()
@IBAction func didClickOnStart(sender: AnyObject) {
queue.cancelAllOperations()
let completionOperation = NSBlockOperation() {
print("all done")
}
for (index, imageURL) in imageURLs.enumerate() {
let operation = ImageNetworkOperation(session: session, urlString: imageURL) { image, response, error in
let dtA : NSData = NSData(data: UIImageJPEGRepresentation(image!, 0.75)!)
self.imageArrayNsData.append(dtA)
print("JPEG download\(index)")
}
completionOperation.addDependency(operation)
queue.addOperation(operation)
}
NSOperationQueue.mainQueue().addOperation(completionOperation)
}
The resulting output:
JPEG download0
JPEG download2
JPEG download1
JPEG download3
all done
You should change your model such that it doesn't matter what order the images are downloaded. For example, you have your array of image URL strings:
var imageURLs: [String]
So, your NSData
should be stored in a dictionary (or NSCache
) keyed by that URL string:
var imageData = [String: NSData]()
Then when you download the data, you can update this dictionary:
self.imageData[imageURL] = dtA
Then, when you need to retrieve this data later, you can use the imageURL, e.g.:
let data = imageData[imageURLs[index]]
Or you could define it as a [Int: NSData]
and use the number index as the key. But the idea is that you can use a dictionary, and then the order you receive the responses doesn't matter, but you still enjoy the performance benefit of doing concurrent requests.
What I'd suggest would be something like:
var imageData = [String: NSData]()
@IBAction func didClickOnStart(sender: AnyObject) {
queue.cancelAllOperations()
let completionOperation = NSBlockOperation() {
print("all done")
}
for (index, imageURL) in imageURLs.enumerate() {
let operation = DataOperation(session: session, urlString: imageURL) { data, response, error in
guard let data = data where error == nil else { return }
guard let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 else { return }
NSOperationQueue.mainQueue().addOperationWithBlock {
self.imageData[imageURL] = data
}
print("JPEG download\(index)")
}
completionOperation.addDependency(operation)
queue.addOperation(operation)
}
NSOperationQueue.mainQueue().addOperation(completionOperation)
}
And then access it like so:
if let data = imageData[imageURLs[index]], let image = UIImage(data: data) {
// use `image` here
}
Or
var imageData = [Int: NSData]()
@IBAction func didClickOnStart(sender: AnyObject) {
queue.cancelAllOperations()
let completionOperation = NSBlockOperation() {
print("all done")
}
for (index, imageURL) in imageURLs.enumerate() {
let operation = DataOperation(session: session, urlString: imageURL) { data, response, error in
guard let data = data where error == nil else { return }
guard let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 else { return }
NSOperationQueue.mainQueue().addOperationWithBlock {
self.imageData[index] = data
}
print("JPEG download\(index)")
}
completionOperation.addDependency(operation)
queue.addOperation(operation)
}
NSOperationQueue.mainQueue().addOperation(completionOperation)
}
And access it like so:
if let data = imageData[index], let image = UIImage(data: data) {
// use `image` here
}
Note, ImageNetworkOperation
is just calling DataOperation
to get the NSData
, and then converting it to a UIImage
. If you really want the original NSData
, I'd suggest bypassing ImageNetworkOperation
and just calling DataOperation
directly, like shown above.
I'm not sure but i think, You can not control the Download order.. means all the request are pipelined to server (no matter what the order you create the URL objects in). What you need to do is, you must maintain the mutable array or dictionary that contains the url to actual data mapping, then wait until all the urls have been completely downloaded. Then iterate in a known order.
try:
var previousOperation : NSOperation! = nil
for (index, imageURL) in imageURLs.enumerate()
{
let operation = ImageNetworkOperation(session: session, urlString: imageURL)
{ image, response, error in
let dtA : NSData = NSData(data: UIImageJPEGRepresentation(image!, 0.75)!)
self.imageArrayNsData.append(dtA)
print("JPEG download\(index)")
}
completionOperation.addDependency(operation)
if (previousOperation != nil)
{
operation.addDependency(previousOperation)
}
previousOperation = operation
queue.addOperation(operation)
}
NSOperationQueue.mainQueue().addOperation(completionOperation)
this is a very quick and rough fix, there well may be a better solution of course. The problem appears because operations in an operation queue are performed concurrently and are not guaranteed to finish in order they started. By adding dependency to a previous operation in the loop you make sure they are performed sequentially