06/19 Update inspections/issues sent via email, and duplicated issue photos issues
This commit is contained in:
@@ -606,20 +606,36 @@ struct MultiLibraryPickerView: UIViewControllerRepresentable {
|
||||
picker.dismiss(animated: true)
|
||||
guard !results.isEmpty else { return }
|
||||
|
||||
var images: [UIImage] = []
|
||||
// PHItemProvider.loadObject's completion handler can be invoked on
|
||||
// arbitrary background queues, potentially concurrently for
|
||||
// different results. Appending to a shared `[UIImage]` from
|
||||
// multiple threads without synchronization is a data race —
|
||||
// Swift's Array is not thread-safe, and concurrent mutation can
|
||||
// corrupt its storage, manifesting as duplicated, dropped, or
|
||||
// reordered photos in the final result. This was the root cause
|
||||
// of duplicated Photo Library attachments on issues.
|
||||
//
|
||||
// Fix: pre-size the array to one slot per result and write each
|
||||
// loaded image into its own fixed index, guarded by a single
|
||||
// serial queue so writes never overlap. Each slot is written at
|
||||
// most once, so duplication is structurally impossible.
|
||||
var images = [UIImage?](repeating: nil, count: results.count)
|
||||
let writeQueue = DispatchQueue(label: "jqc.photopicker.write")
|
||||
let group = DispatchGroup()
|
||||
|
||||
for result in results {
|
||||
for (index, result) in results.enumerated() {
|
||||
guard result.itemProvider.canLoadObject(ofClass: UIImage.self) else { continue }
|
||||
group.enter()
|
||||
result.itemProvider.loadObject(ofClass: UIImage.self) { object, _ in
|
||||
if let img = object as? UIImage { images.append(img) }
|
||||
if let img = object as? UIImage {
|
||||
writeQueue.sync { images[index] = img }
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
|
||||
group.notify(queue: .main) {
|
||||
self.parent.onSelected(images)
|
||||
self.parent.onSelected(images.compactMap { $0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user