I'm using a serial DispatchQueue to read a set of PHAssets and upload them to a remote host one by one.
The code uses PHAsset.fetchAssets() and then within enumerateObjects I add a task to the queue using queue.async { .. }
. Within the task, I am using PHImageManager.default().requestImageData
, which reads the contents of the asset and returns it as Data
by executing a closure.
So, the code that ends up within each of the queued tasks looks like this:
self.queue.async {
PHImageManager.default().requestImageData(
for: asset,
options: self.imageRequestOptions()) { (data: Data?, dataUTI: String?, orientation: UIImageOrientation, info: [AnyHashable : Any]?) in
print( "--> Upload data: \(data?.description ?? "n/a")" )
usleep( 2000000 )
}
}
My queue is defined like this:
let queue = DispatchQueue(
label: "com.myapp.asset_upload_queue",
qos: .background
)
The issue I'm running into is that PHImageManager.requestImageData
returns right away, so the next task starts to read data immediately. The net result is I end up with multiple concurrent uploads and the CPU shoots through the roof the entire time.
I assume the issue is that the callback PHImageManager.requestImageData
takes is doesn't block the queue thread, so it moves on to the next task. That next task starts reading data right away.
I tried putting what's inside of the callback in a queue, as in:
self.queue.async {
PHImageManager.default().requestImageData(
for: asset,
options: self.imageRequestOptions()) { (data: Data?, dataUTI: String?, orientation: UIImageOrientation, info: [AnyHashable : Any]?) in
self.queue.async {
print( "--> Upload data: \(data?.description ?? "n/a")" )
usleep( 2000000 )
}
}
}
This starts executing the uploads serially, but the requestImageData
calls are still being executed one after the other, which puts huge strain on the CPU.
Question:
How would I control the flow so that requestImageData
only gets executed once the previous upload is complete?
Other things I've tried
My first approach was to try and get an asset path directly from the Photos framework, which reportedly can be done by using requestContentEditingInputWithOptions
. However, I could only get that to work on the simulator and not on any real device.