2

I have been migrating my code from Swift 2 to Swift 4. I have the following code that worked fine in Swift 2:

func fetchUserThumbnailAvatar(_ task : inout URLSessionTask?, completion : @escaping (_ image : UIImage?) -> ()) {
    fetchUserAvatar(Session.currentUser?.avatar?.thumbnailURL as URL? ?? URL(string:"")!, externalUrl: URL(string: thumbnailAvatar) ?? URL(string:"")!, &task, completion: completion)
}

fileprivate func fetchUserAvatar(_ internalUrl : URL, externalUrl : URL,_ task : inout URLSessionTask?, completion : @escaping (_ image : UIImage?) -> ()) {
    fetchImage(externalUrl, task: &task, completion: { image in
        if image == nil {
            self.fetchImage(internalUrl, task: &task, completion: completion)
        } else {
            self.cache.removeObject(forKey: (internalUrl.path as AnyObject?)! )
            completion(image)
        }
    })
}

However, after the conversion I get the following error:

Escaping closures can only capture inout parameters explicitly by value

at line:

if image == nil {
            self.fetchImage(internalUrl, task: &task, completion: completion)
        }

It would be great if someone could help me. Thanks.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
vjeta
  • 1,178
  • 2
  • 11
  • 18
  • Please show code for the `fetchImage` function – George May 10 '18 at 22:06
  • @George_E_2: `fetchImage` is not the problem. The problem is that `task` is given as an inout parameter to `fetchImage`, and the surrounding closure is already escaping. See my answer. – Michael May 10 '18 at 22:15

1 Answers1

3

The error is described in detail in this answer.

The problem with your code is that the first closure

fileprivate func fetchUserAvatar(_ internalUrl : URL, externalUrl : URL,_ task : inout URLSessionTask?, completion : @escaping (_ image : UIImage?) -> ()) {
    fetchImage(externalUrl, task: &task, completion: { image in // <-- HERE --
        if image == nil {

is an escaping closure. So when the code

        if image == nil {
            self.fetchImage(internalUrl, task: &task, completion: completion) // <-- HERE --
        } else {

tries to write to the task variable, the original fetchUserAvatar call has already completed.

Note: I have written comments like this <-- HERE -- into the snippets, to clarify which line I am talking about. Also, please make sure to check out the answer that I linked above, because it will clarify everything..

The bad news is that you will have to refactor the code a bit to fix the error. You'll need to change the signatures of both fetchUserThumbnailAvatar, as well as fetchUserAvatar for that, and that will break callers; so the callers have to be changed as well. Therefore I cannot fix it for you, because the fix depends on code that I don't have.

Michael
  • 6,451
  • 5
  • 31
  • 53