Follow up on @Majotron's answer, I want to fetch all user's photos in the exact order as the All Photos
albums in Photos app, and with only image assets.
Here is how I set up my PHFetchOptions
:
let allPhotosOption = PHFetchOptions()
// Fetch only image asset
allPhotosOption.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
// Get All Photos album collection
let userLibrary = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil).firstObject
allPhotosResult = PHAsset.fetchAssets(in: userLibrary!, options: allPhotosOption)
And obtain specific asset using reversed index inside your TableView/CollectionView:
let asset = allPhotosResult.object(at: allPhotosResult.count - indexPath.item - 1)
Also be careful if you're listening to photo library changes and rearrange your view accordingly. Obtain reversed index using fetch result before changes when changes are related to removal, using .fetchResultAfterChanges
otherwise.
Here is how I handle it:
func photoLibraryDidChange(_ changeInstance: PHChange) {
...
DispatchQueue.main.sync {
let originalCount = fetchResult.count
fetchResult = changes.fetchResultAfterChanges
if changes.hasIncrementalChanges {
guard let collectionView = self.collectionView else { return }
collectionView.performBatchUpdates({
if let removed = changes.removedIndexes, removed.count > 0 {
collectionView.deleteItems(at: removed.map({ IndexPath(item: originalCount - $0 - 1, section: 0) }))
}
if let inserted = changes.insertedIndexes, inserted.count > 0 {
collectionView.insertItems(at: inserted.map({ IndexPath(item: fetchResult.count - $0 - 1, section: 0) }))
}
})
// Avoid deleting and reloading same index path at one update
collectionView.performBatchUpdates({
if let changed = changes.changedIndexes, changed.count > 0 {
collectionView.reloadItems(at: changed.map({ IndexPath(item: fetchResult.count - $0 - 1, section: 0) }))
}
changes.enumerateMoves { fromIndex, toIndex in
collectionView.moveItem(at: IndexPath(item: fetchResult.count - $0 - 1, section: 0),
to: IndexPath(item: fetchResult.count - $0 - 1, section: 0))
}
})
} else {
collectionView!.reloadData()
}
...
}
}