3

I have an array of PFFiles downloaded from Parse, and I am trying to convert them in to an array of NSData (imageDataArray) in order to save them to Core Data. The only problem I seem to be having now is that the elements of imageDataArray are being added in the wrong order, which means the wrong image is found when I search through Core Data.

I found this question (Stack Overflow Question) which seems to explain the reason for the problem as being that the block is completing the tasks at different times, therefore the order of the array is based on whichever is finished first. They then suggest using Grand Central Dispatch, but it is all in Obj C and, as a newbie to coding, I am struggling to convert it to Swift for my project.

Could you please explain to me how I would use GCD (or any other method) to create the imageDataArray in the same order as the original PFFile array?

    override func viewDidLoad() {
    super.viewDidLoad()


    let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

    let context: NSManagedObjectContext = appDel.managedObjectContext


   let dealsQuery = PFQuery(className: ("Deals"))
    dealsQuery.orderByAscending("TrailId")

    dealsQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
        if let objects = objects {

            self.trailID.removeAll(keepCapacity: true)
            self.trailStep.removeAll(keepCapacity: true)
            self.dealNumber.removeAll(keepCapacity: true)
            self.imageFile.removeAll(keepCapacity: true)

            for object in objects {

                self.trailID.append(object["TrailID"] as! Int)
                self.trailStep.append(object["TrailStep"] as! Int)
                self.dealNumber.append(object["dealNumber"] as! Int)
                self.imageFile.append(object["dealImage"] as! PFFile!)

            }

        }


            var counter = 0

            for file in self.imageFile {

            let dealImage = file

            dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in

                if error == nil {

                    weak var aBlockSelf = self

                    let image = UIImage(data: imageData!)
                    aBlockSelf!.imageDataArray.append(imageData!)
                    self.imagesArray.append(image!)

                    if counter == self.trailStep.count - 1{

                        print(self.trailID.count)
                        print(self.trailStep.count)
                        print(self.dealNumber.count)
                        print(self.imageDataArray.count)

                        print(self.trailStep[0])
                        print(self.dealNumber[0])
                        let image = UIImage(data: self.imageDataArray[0])
                        self.imageView.image = image

                    } else {counter++}
                }

            })

Sorry for posting all of my code here. I guess the issue is at the bottom, but as a beginner I thought I might have made a mistake somewhere else so thought I would be best posting it all.

Thanks a lot for your help.


Update 1

I have tried adding a prefilled array (imageArray) and trying to put the data in to that, but it still comes out random when searching for trailStep and dealNumber. What am I missing? Thanks

 var i = 0

            var imageArray = [NSData!](count: self.trailStep.count, repeatedValue: nil)

            for file in self.imageFile {

            let dealImage = file

            dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in

                if error == nil {

                    //weak var aBlockSelf = self

                    let image = UIImage(data: imageData!)
                    imageArray[i] = imageData!

                    if i == self.trailStep.count - 1{

                        print(self.trailID.count)
                        print(self.trailStep.count)
                        print(self.dealNumber.count)
                        print(self.imageDataArray.count)

                        print(self.trailStep[3])
                        print(self.dealNumber[3])
                        let image = UIImage(data: imageArray[3])
                        self.imageView.image = image

                    } else {i++}
                }

            })

Update 2

I have been researching and having a play around with GCD and serial dispatches, and here is where I am at the moment. I think I am taking a far too simplistic view of GCD but can't quite get my head around how it works and how it can get my block to add to imageDataArray in the right order.

var counter = 0

        var imageArray = [NSData!](count: self.trailStep.count, repeatedValue: nil)

        weak var aBlockSelf = self

        var serialQueue = dispatch_queue_create("serial", nil)

        for file in self.imageFile {

            let dealImage = file

            var imageDataArray = [NSData!](count: self.imageFile.count, repeatedValue: nil)

            dispatch_async(serialQueue, { () -> Void in

            dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in



                    if error == nil {

                        let imageIndex = self.imageFile.indexOf(file)
                        let image = UIImage(data: imageData!)
                        imageDataArray.insert(imageData!, atIndex: imageIndex!)

                        if counter == self.trailStep.count - 1{

                            print(self.trailID.count)
                            print(self.trailStep.count)
                            print(self.dealNumber.count)
                            print(imageDataArray.count)

                            print(self.trailStep[0])
                            print(self.dealNumber[0])
                            let image = UIImage(data: imageDataArray[4])
                            self.imageView.image = image

                        } else {counter++}

                        return
                    }

                })

This code is returning an error of "unexpectedly found nil while unwrapping an Optional value" on the line " let image = UIImage(data: imageDataArray[4])". This is the same error that I was getting before I tried to implement the GCD...

2 Answers 2

3

This should be fairly simple. Although GCD dispatch group would be ideal choice, you can solutionize this by keeping a counter of for loop.

In your completion block, deduce the index of the object you are currently inside the for loop using the parent array. In your final image array, add object at the index captured by this index.

EDIT: Post OP question - here is a quick sneak peek of how I would like it to be:

weak var aBlockSelf = self

for file in self.imageFile {

    let dealImage = file

    dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in

        if error == nil {
            let imageIndex = self.imageFile.indexOf(file)
            let image = UIImage(data: imageData!)
            aBlockSelf.imageDataArray.insert(imageData!, atIndex: imageIndex)
            aBlockSelf.imagesArray.insert(image!, atIndex: imageIndex)

As a side not, do not declare self weak reference in the for loop. You should declare it outside.

Sign up to request clarification or add additional context in comments.

11 Comments

Thanks a lot for your help. How exactly would I deduce the index of the parent array? I thought I was doing that with the i variable in the updated code...or does it not work like that?
Hi @Abhinav. I have tried your code but it seems to be giving me an "Array Index Out Of Range" error on the line "aBlockSelf!.imageDataArray.insert(imageData!, atIndex: imageIndex!)". I seemed to be getting this error a lot when I was playing around with trying to sort out this issue. What do you think the problem is? I appreciate your side note by the way. I think I need a quick research of what a self weak reference really is. Thanks for the help.
I made sure the imageDataArray was filled and the same error moved to the "aBlockSelf.imagesArray.insert(image!, atIndex: imageIndex)" line. I thought this array wasn't really necessary so I deleted it, but now I am getting an "unexpectedly found nil while unwrapping an Optional value" error on the line "let image = UIImage(data: imageDataArray[3])" line. Is this because this line is now being actioned before the elements are being added to the array?
Probably, as I had suggested you should use this array only after last iteration of for loop. Try that out. Frankly, I would go with GCD with dependent tasks in this case.
I think I am using that array after the last iteration of the for loop, in the if i == self.trailStep.count - 1 part. It is in the code in Update 1 in my question. Is that what you mean? I will do a bit of research in to GCD with dependent tasks and will give it a try. I'm thinking a might need some advice though :/ Thanks again for your time.
|
0

You don't need to use GCD for this; you just need an array (or map, if you want to associate each image with its filename) to stick each image in when it's retrieved. In other words, if you have files "f1", "f2", "f3", and "f4", in that order, create an array to hold the retrieved images, and insert them at whatever index corresponds to their filename's index in the list of filenames.

1 Comment

Hi, thanks a lot for your comment. I have updated the original question with the change I have made based on your comments, but it is still coming out in a random order. Have I got something wrong?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.