-1

I am not sure if this is a duplicate or not, I searched and couldn't find an answer. If it is please point me to the similar question. I am setting the value of the imageURL variable inside a completion handler:

var imageURL: URL?

let options = PHContentEditingInputRequestOptions()

imageAsset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, _) in
            imageURL = contentEditingInput?.fullSizeImageURL
})
//prints nil       
print(imageURL)

Now, the value of imageURL inside the handler is not nil, but it becomes nil right after (in the print statement). Why is this happening and how can I fix it without having to write all of my subsequent code inside the completion handler block?

HemOdd
  • 697
  • 1
  • 7
  • 23
  • It **is** a duplicate and has been asked in many ways very often. `requestContentEditingInput` works asynchronously. Put the `print` line – and everything else what you need to do – into the completion handler – vadian Apr 16 '19 at 19:26
  • The completion handler is returning asynchronously. When you call `requestContentEditingInput`, the machine does not wait for the return before proceeding, it will continue executing code. And, obviously, the completion handler has not returned before the machine executed the very next line. – trndjc Apr 16 '19 at 19:26
  • I just answered this same question an hour ago. https://stackoverflow.com/questions/55712286/swift-returning-a-class-that-can-be-used-by-other-methods-from-an-api-call Please read http://www.programmingios.net/you-cant-use-a-value-after-it-has-been-set-by-asyncronous-code/ and the related articles – matt Apr 16 '19 at 19:37
  • Thank you all. @matt I understand that it must be frustrating but sometimes you don't know the correct keywords to search for when you have a problem. Probably, if I searched something like "asynchronous" I would have found that answer you are referring to but I didn't know I have to search for that. Also thanks for the article. – HemOdd Apr 16 '19 at 19:48

3 Answers3

1

You can't "fix" it in the way you'd like, unfortunately. The completion handler here might in theory be called synchronously (ie, at the time of the call to requestContentEditingInput), but can (and most likely will) be called at some time later, when the asset is ready. That could include actual downloading or any other unpredictable time-consuming preparation of the asset that happens on some other thread.

In other words, the function requestContentEditingInput returns to you right away (and your code continues executing), but the function also commences doing some work in the background. That background work, when it finishes, calls your handler block.

The nature of execution flow means that you simply cannot guarantee (and certainly cannot assume) that the handler will be called before execution moves on to your print(imageURL) line.

This kind of asynchronicity is a very common pattern, though! Nothing to be afraid of. You need to put any code that must run subsequently within that handler block (or call out from the handler block to another function if that is cleaner within your file).

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
0

It is likely that the completion is not called until later because the code is running on another thread. Try putting a print statement inside of the completion block to see what order the code is executed.

rpecka
  • 1,168
  • 5
  • 17
0

When you work with handlers, the time with the threads can be different every build.

I recommend you create a method that will be called inside the handler.

Like this:

func edit(){
    var imageURL: URL?

    let options = PHContentEditingInputRequestOptions()

    imageAsset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, _) in
          imageURL = contentEditingInput?.fullSizeImageURL
          display(url:imageURL)      
    })
}

func display(url: String) {
    print(imageURL)
}

Narlei Moreira
  • 256
  • 2
  • 10