1

I am working with Parse and would like to download images for offline use. I understand that this is not possible with Local Datastore so I have decided to add them to Core Data.

I have successfully downloaded the PFFiles and put them in to an Array. I am then trying to create an Array for the NSData, but the Array count is always 0 when I use the code below

class DealsDownloadViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!

var trailID = [Int]()
var trailStep = [Int]()
var dealNumber = [Int]()
var imageFile = [PFFile]()
var imagesArray = [UIImage]()
var imageDataArray = [NSData]()
var number = 0

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 {

            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!)


            }

            for file in self.imageFile {

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

                    if error == nil {

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

                    } else {print("error here")}

                })

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





        } else {print("problem making arrays")}


        }

}

Log from the original code

If I move the Print statement up, I just get it printing every iteration of the loop.

for file in self.imageFile {

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

                    if error == nil {

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

                    } else {print("error here")}

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

                })


            }


        } else {print("problem making arrays")}


        }

}

Log from second code

In this case I can see that the data is added to both the imagesArray and imageDataArray.

This seems like such a simple issue but I am going crazy over it. What am I doing wrong, and is this the most efficient way of adding this data to Core Data? Am I overlooking something obvious?

I am new to programming so please do point out any mistakes I have made, and I am especially new as a questioner to stackoverflow (you have been indispensable while learning) so please let me know if you need any information that I have missed.

Thanks for your help.

Update 1

I have tried editing the code as explained in the comments and I am still getting the same result. I have moved the Print statement around on this code and it is still giving me the same results as above.

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!)

                    }

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

                })

            }

        } else {print("problem making arrays")}

    }

}

Am I missing something very simple? Thanks again for your help.

Update 2

This is the same code with (I think) the print statements moved outside of the For Loop. This is giving me counts of 9,9,9,0,0 from the print statements, whereas I think I should be expecting 9,9,9,9,9.

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!)

                    }

                })

            }

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

        } else {print("problem making arrays")}

    }

}
Jon Buckley
  • 117
  • 1
  • 12

1 Answers1

0

There is no issue here!

You are being deceived by the way asynchronous block works. Asynchronous block gets queued up to get executed some point later in next run loop.

Your first print statement is just after you pass the code to block which is yet to be executed. Which is why you do see your image array empty.

Abhinav
  • 37,684
  • 43
  • 191
  • 309
  • Thanks a lot for your reply. I have read up a bit to try to better understand Asynchronous Blocks, but I can't work out how I can alter my code. Where should I move the print statement to? I have tried moving it all around the code but it never seems to give the 9,9,9,9,9 result that it should. Thanks again. I really appreciate your help as a newbie. – Jon Buckley Oct 29 '15 at 06:33
  • First of all, inside block, you should pass a weak reference to self object - like `weak var aBlockSelf = self`. And then calling it like `aBlockSelf.imageDataArray.append(imageData!)`. You should move your `print` statement just after `imagesArray` is set inside the block. – Abhinav Oct 29 '15 at 06:36
  • I still seem to be getting the same results from the print statement. I have updated my original question to add the change that you suggested, and I have moved both the print statements and the aBlockSelf variable around that part of the code, but it keeps giving me the same 9,9,9,0,0 or 9,9,9,0,0,9,9,9,1,1...etc result. Am I still missing something? Thanks – Jon Buckley Oct 29 '15 at 07:22
  • Results looks good to me as you are fetching images in for loop and passing block in for loop so your image count will get updated by 1. – Abhinav Oct 29 '15 at 07:31
  • But the problem I get is when I move the image count outside the for loop, the print statements give me 9,9,9,0,0, whereas shouldn't I expect 9,9,9,9,9? – Jon Buckley Oct 29 '15 at 08:03
  • I have just updated the original question again with the print statement outside the for loop. Thanks – Jon Buckley Oct 29 '15 at 08:08
  • No @JonBuckley - see you are updating the variable inside a block which you are not sure when it gets executed. Putting a statement just after the for loop will be executing earlier than block execution. You will have to wait for all blocks to get finished and then read the data. If I were you, I would have printed arrays on last iteration of for loop. I hope you got my point. Let me know if this is still not clear. – Abhinav Oct 29 '15 at 08:34
  • Ok, I think I understand now. That sounds like a great idea. Could you tell me how I can perform an action on the last iteration of the loop? I'm not sure how I would do that. Thank you so much for your help. This has been driving me crazy. I hope this is the end :) – Jon Buckley Oct 29 '15 at 08:49
  • :-). I'm installing Xcode 7.1 and its taking a little time. So, bear with me for not sharing the code. You can intialize an `Int` like `var counter = 0` just before the `for` loop, increment it inside the `for` loop. Then inside the condition `counter == self.imageFile.count` print the arrays. Can you accept this answer post that :)! – Abhinav Oct 29 '15 at 08:54
  • 1
    I think you've cracked it! Thanks a lot for your patience. I knew there must have been an easy answer. I think I need a bit more practice. I really appreciate your help. – Jon Buckley Oct 29 '15 at 09:15
  • Always happy to help @JonBuckley. Cheers :)! – Abhinav Oct 29 '15 at 09:21
  • Hi again, I have run in to another problem a little further on. I was hoping you could take a quick look if you have the time (http://stackoverflow.com/questions/33429044/loop-within-block-appending-to-array-in-the-wrong-order-swift-2-0). The elements of the imageDataArray are being added in the wrong order because of the block...but I am struggling to work out a solution to get them in the same order as the imageFiles array, so I can search within Core Data using trailStep and dealNumber. Any help is much appreciated. Thanks again. – Jon Buckley Oct 30 '15 at 10:02
  • I just suggested u a solution in that thread. Take a look. I would also advise you to prefill your image arrays with dummy data to avoid crash. – Abhinav Oct 30 '15 at 10:23
  • That is great. Thanks a lot. I will give it a try and let you know how I get on. – Jon Buckley Oct 30 '15 at 10:51