Reputation: 51
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
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
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
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
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
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