1

I'm trying to set an image variable after an image has been downloaded. I'm using 'inout' to pass the image variable to my download function, but it doesn't get set.

Here's my code:

var img: UIImage?

func downloadImage(url: String?, inout image: UIImage?) {

    if url != nil {

        var imgURL: NSURL = NSURL(string: url!)!
        var request: NSURLRequest = NSURLRequest(URL: imgURL)

        NSURLConnection.sendAsynchronousRequest(request, queue: 
            NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,
            data: NSData!,error: NSError!) -> Void in

            if error == nil {
                dispatch_async(dispatch_get_main_queue(), {

                    // setting img via inout doesn't work
                    image = UIImage(data: data) // inout img should be set but its not
                    println(img) 

                    // setting img directly works, but don't want it hard coded
                    img = UIImage(data: data)
                    println(img)

                })
            }
            else {
                println("Error")
            }
        })
    }
}

downloadImage(<<IMAGE URL HERE>>, &img) // passing img as an inout

Im expecting the variable img which is passed as an inout to the downloadImage function to be set after the image is downloaded, but it never gets updated.

I'm expecting the line: image = UIImage(data: data) to update the img inout variable but it doesn't.

However, the line: img = UIImage(data: data) which references the img variable directly gets updated.

But I don't want to hard code the img variable directly within the function, I want to be able to pass any variable I want via inout.

Any idea why I cant update the inout variable and how I can fix it. Thanks.

GoZoner
  • 67,920
  • 20
  • 95
  • 145
Bingo
  • 375
  • 6
  • 17
  • 1
    Asynchronous code doesn't run synchronously. – nhgrif Apr 02 '15 at 01:09
  • your comment is not very helpful - some advice would be appreciated – Bingo Apr 02 '15 at 01:12
  • Your function runs asynchronous code. The code in the asynchronous block won't be finished before the function returns. There's nothing wrong with your code except the expectation that your asynchronous code will be finished before the function returns. – nhgrif Apr 02 '15 at 01:13
  • Do you notice that if you put a `println()` statement after calling `downloadImage` that *that* statement gets called before your `println(img)` within the block (or `println("error")`)? – nhgrif Apr 02 '15 at 01:14
  • sorry, but I'm not convinced this is the issue - I know if you put println(img) after the downnloadImage function that img will still be nil - BUT, if I directly set img = UIImage(data: data) instead of image = UIImage(data: data) then it actually sets img correctly - but I dont want to hardcode the img variable in the function I want to update it via inout – Bingo Apr 02 '15 at 01:20
  • I don't think you've understood my issue - if what you are saying is correct, then I should not be able to set the img variable directly by changing the code to img = UIImage(data: data) - this works perfectly fine. The issue is that I cannot update the img variable when its passed as an inout variable when it should. – Bingo Apr 02 '15 at 01:33
  • Which variable you want to get populated, img or image? Correct me if I am wrong, you want to get rid of img variable and just have image variable that is passed as inout parameter to the method. – Abdullah Apr 02 '15 at 01:42
  • I want img to be set, but via an inout - in this case: inout image: UIImage. In other words I want img to be set and available outside of the function after the image has downloaded - I would have expected inout to allow this – Bingo Apr 02 '15 at 01:55
  • ok, that settles it - inout parameter don't work with async-callbacks - not sure why my question was down voted – Bingo Apr 02 '15 at 02:21

1 Answers1

2

You need to pass a 'continuation' to your downloadImage() function; the continuation is what to do with the downloaded image: Like this:

func downloadImage(url: String?, cont: ((UIImage?) -> Void)?) {
  if url != nil {

    var imgURL: NSURL = NSURL(string: url!)!
    var request: NSURLRequest = NSURLRequest(URL: imgURL)

    NSURLConnection.sendAsynchronousRequest (request,
        queue:NSOperationQueue.mainQueue(),
        completionHandler: {
            (response: NSURLResponse!, data: NSData!,error: NSError!) -> Void in
          if error == nil {
            dispatch_async(dispatch_get_main_queue(), {
              // No point making an image unless the 'cont' exists
              cont?(UIImage(data: data))
              return
            })
          }
          else { println ("Error") }
      })
  }
}

and then you use it like this:

var theImage : UIImage?

downloadImage ("https://imgur.com/Y6yQQGY") { (image:UIImage?) in
  theImage = image
} 

where I've exploited syntax for a trailing closure and simply assign the optional, downloaded image to the desired variable.

Also, please note that I have not examined your thread structure; it may be that the cont function should be called on some other queue - but, you get the point of passing in a continuation.

GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • thanks. Im not sure if this solution works, it crashes playground. Are you able to run it? – Bingo Apr 02 '15 at 02:26
  • Im getting a compiler error on the dispatch_async line: cannot invoke 'init' with an argument list of type '(dispatch_queue_t!, () - () -> $T3)' ... any idea how to fix this? – Bingo Apr 02 '15 at 02:33
  • Add a `return` after the `cont?()` line. The type inferencer has decided that `cont?()` might be returning something when it shouldn't. – GoZoner Apr 02 '15 at 02:34
  • But, importantly, the details don't matter. You now know how to handle multi-processing! The above approach is pervasive because it is universally applicable. It is exactly why closures are so important in programming. – GoZoner Apr 02 '15 at 02:36
  • I cant get this to work - I know you're on the right path with this, but it wont compile: command failed due to signal Segmentation fault: 11 – Bingo Apr 02 '15 at 02:43
  • "I'm on the right path".... Good luck to you. – GoZoner Apr 02 '15 at 02:44