JoeAllenWelsh
JoeAllenWelsh

Reputation: 51

PHPhotoLibrary - photoLibraryDidChange called multiple times in Swift

I have the following code:

override func viewDidLoad() {
    ........
    PHPhotoLibrary.shared().register(self)
}

and I've implemented the PHPhotoLibraryDidChangeObserver.

My problem is, that the method photoLibraryDidChange gets called multiple times if something in the device's gallery changes.
Here is the code for that function:

func photoLibraryDidChange(_ changeInstance: PHChange) {
    DispatchQueue.main.async(execute: {
        self.updateFetchResult();
    })
}

Do you know why this method gets executed multiple times?

Upvotes: 5

Views: 3835

Answers (5)

Mike
Mike

Reputation: 1

Personal summary and guesses: Usually it is called twice, once when the number of photos in the album library changes, and once when the number of photos in a single album changes. After I turn on the iCloud function, it is three times, because I will also be notified once when deleting or uploading from iCloud. If you want to avoid multiple calls, you need to first get the PHFetchResult in the photo album library, and then use changeInstance.changeDetails(for: fetchResult) in this method to know whether it has changed before or after.

Upvotes: 0

KuDji
KuDji

Reputation: 159

If you do it now you should add 2 row

let hasInsertionFromUser = changeInstance.changeDetails(for: resultDetailChanges.fetchResultAfterChanges) != nil
let hasAcessGrantedByUser = changeInstance.changeDetails(for: resultDetailChanges.fetchResultBeforeChanges) != nil

Otherwise, upon access, the method initially does not work and when adding new photos from the gallery, with picking concrete photo too.

Upvotes: 0

Leojin Bose S
Leojin Bose S

Reputation: 21

Try this way

extension YourViewController: PHPhotoLibraryChangeObserver {

        func photoLibraryDidChange(_ changeInstance: PHChange) {
            if let _fetchResult = self.fetchResult, let resultDetailChanges = changeInstance.changeDetails(for: _fetchResult) {
                let insertedObjects = resultDetailChanges.insertedObjects
                let removedObjects = resultDetailChanges.removedObjects
                let changedObjects = resultDetailChanges.changedObjects.filter( {
                    return changeInstance.changeDetails(for: $0)?.assetContentChanged == true
                })
                if resultDetailChanges.hasIncrementalChanges && (insertedObjects.count > 0 || removedObjects.count > 0 || changedObjects.count > 0){
                    DispatchQueue.main.async {
                        self.getPhotos()
                    }
                }
            }
        }

    }

Upvotes: 2

sanjay kumar
sanjay kumar

Reputation: 1

extension YourViewController: PHPhotoLibraryChangeObserver {

    func photoLibraryDidChange(_ changeInstance: PHChange) {
        if let _fetchResult = self.fetchResult, let resultDetailChanges = changeInstance.changeDetails(for: _fetchResult) {
            let addedItems = resultDetailChanges.insertedObjects
            let deletedItems = resultDetailChanges.removedObjects
            let changedItems = resultDetailChanges.changedObjects
            if resultDetailChanges.hasIncrementalChanges && (addedItems.count > 0 || deletedItems.count > 0 || changedItems.count > 0){
                DispatchQueue.main.async {
                    self.getPhotos()
                }
            }
        }
    }

}

Above check of resultDetailChanges.hasIncrementalChanges and counts of (added, removed, changed assets) will protect to avoid multiple calls.

Upvotes: 0

Javier Siancas
Javier Siancas

Reputation: 36

Did you find a solution? This solve mi problem:

First, save your PHFetResult<PHAsset> when calling PHAsset.fetchAssets(with:options:) if your using this method.

After that, use your PHFetResult<PHAsset> variable to validate if the fetch result has changes in the photoLibraryDidChange: method. The first time this method is called, you have to update to collection view or table view. After that, your fetch result has not changes so... you have nothing to update.

This is my full code:

let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

self.fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)

.
.
.

extension YourViewController: PHPhotoLibraryChangeObserver {

    func photoLibraryDidChange(_ changeInstance: PHChange) {
        if let _fetchResult = self.fetchResult, let _ = changeInstance.changeDetails(for: _fetchResult) {
            DispatchQueue.main.async {
                self.getPhotos()
            }
        }
    }

}

Hope this help you!

Upvotes: 0

Related Questions