0

The api I am calling is designed to be asynchronous but also has synchronous mode. I am using PHImageManager.requestImageDataAndOrientation (docs) to request image files, while writing code that is more readable (linear).

let requestImageOptions = PHImageRequestOptions()
requestImageOptions.isSynchronous = true

var photos: [Data] = []

let requestIdentifier = PHImageManager.default().requestImageDataAndOrientation(for: asset, options: requestImageOptions) { [weak &photos] (imageData, dataUTI, orientation, info) in
    photos.append(imageData)
}

// do stuff with photos here because the previous call is synchronous

Unfortunately the error message is not helpful: Expected name of in closure capture list. My attempt with &photos is to use a reference of the value typed variable, because value types cannot be referenced directly. Could someone point me in the right direction?


This may be a common question for people who mistakenly think they can get value immediately below the method call, where they think an call is synchronous but is not. In my case though, I have configured the call to be synchronous. I also know that I can do all the work in the closure, but then I won't be able to return from the function with data. And sure, I can try a class (value type) to transfer the data out of the closure.

Ben Butterworth
  • 22,056
  • 10
  • 114
  • 167

1 Answers1

1

The compiler is objecting because [weak &photos] at the start of the closure isn't valid syntax.

You don't need to be concerned with the closure capturing the local variable photos; it can't cause a retain loop.

You do need to conditionally unwrap imageData though.

It is also important to note that you can only use the synchronous option if you are calling requestImageDataAndOrientation from a background queue. Your code doesn't show a background queue dispatch. If you do execute the call on a background queue you will need to dispatch any UI updates back onto the main queue.

Given this, it is probably simpler to use the asynchronous form and simply perform your required processing in the completion closure.

let requestIdentifier = PHImageManager.default().requestImageDataAndOrientation(for: asset, options: requestImageOptions) {(imageData, dataUTI, orientation, info) in
    guard let imageData = imageData else
    { return }
    photos.append(imageData)
}
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • I was afraid the closure will get a copy of the variable, and mutate that instead. I think you're saying that doesn't happen, and it's not copied but used directly. Also, I'm actually using an OperationQueue, so all this code is in an class subclassing Operation which is automatically thread safe. I'm also not doing anything immediately with UI but doing more image processing, so i think you'd agree the synchronous mode is better. Thanks for your help :) – Ben Butterworth Jan 21 '21 at 12:50
  • An operation queue doesn't automatically make a swift array thread safe, however in this instance your array is a local variable, so thread safety isn't a concern. Even if the `[weak photos]` was valid it wouldn't change the semantics (value vs reference) of the variable, just how the variable is captured. Swift array value semantics only apply when you use an assignment. If, in the closure you said `var otherPhotos = photos; otherPhotos.append(data)` then `photos` would still be empty but the `photos` captured by the closure is the local variable from the nckosing function – Paulw11 Jan 21 '21 at 19:18