Search code examples
arraysswiftuiimagepickercontrollerphasset

Removing duplicate PHAssets from an array?


I'm using third party library called BSImagePicker to select multiple images from the photo library. I can set maximum PHAssets to be selected, but the problem comes when I display the BSImagePickerController to choose more photos. Those PHAssets are just additionally added to the selectedAssets and practically duplicates the images. I tried to remove the duplicates PHAsset and images using but it's not working:

extension Array where Element: Equatable {
mutating func removeDuplicates() {
    var result = [Element]()
    for value in self {
        if !result.contains(value) {
            result.append(value)
        }
    }
    self = result
   }
}

func presentBSImagePickerController(vc: BSImagePickerViewController) {
        self.bs_presentImagePickerController(vc, animated: true,
                                             select: { (asset: PHAsset) -> Void in

        }, deselect: { (asset: PHAsset) -> Void in
            // User deselected an assets.

        }, cancel: { (assets: [PHAsset]) -> Void in
            // User cancelled. And this where the assets currently selected.

        }, finish: { (assets: [PHAsset]) -> Void in


            // User finished with these assets

            for i in 0..<assets.count  {
                self.selectedAssets.append(assets[i])
            }
            self.selectedAssets.removeDuplicates()

            self.convertAssetToImages()

        }, completion: nil)
    }

    func convertAssetToImages() -> Void {

        if selectedAssets.count != 0 {

            for i in 0..<selectedAssets.count {

                let manager = PHImageManager.default()
                let option = PHImageRequestOptions()
                var thumbnail = UIImage()
                option.isSynchronous = true

                manager.requestImage(for: selectedAssets[i], targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: option, resultHandler: {(result, info)->Void in
                    thumbnail = result!

                })

                let data = UIImageJPEGRepresentation(thumbnail, 0.7)
                let newImage = UIImage(data: data!)

                self.photosArray.append(newImage! as UIImage)
                self.photosArray.removeDuplicates()
                print(self.photosArray.removeDuplicates())

                DispatchQueue.main.async {
                    self.collectionView.reloadData() // reloads the collection view on main thread
                }
            }
        }
    }

Solution

  • The problem with

    for i in 0..<assets.count  {
        self.selectedAssets.append(assets[i])
    }
    self.selectedAssets.removeDuplicates()
    

    ...is that your removeDuplicates (apart from being intolerably slow!) simply assumes that PHAsset equality is the mark of a duplicated image. It isn't. An asset is just a temporary representative of what's in the library. What you want to know is whether the local identifier of an asset matches that of one that you already have. I would suggest this:

    var ids = Set(self.selectedAssets.map {$0.localIdentifier})
    for asset in assets  {
        if !ids.contains(asset.localIdentifier) {
            ids.insert(asset.localIdentifier)
            self.selectedAssets.append(asset)
        }
    }
    

    We make a list of all the local identifiers of the assets we already have. Then we append an asset (and its identifier) only if its identifier is not in the list. This will be much faster (because Set contains is much faster than Array contains), and should also prevent "the same photo" from appearing twice.