Fetch only PHAssetCollections containing at least one photo - ios

In iOS PhotoKit, I can fetch all non-empty albums like this:
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "estimatedAssetCount > 0")
let albumFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: albumFetchOptions)
albumFetchResult.enumerateObjects({ (collection, _, _) in
// Do something with the album...
})
Then I can get only photos from the album like this:
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetResourceType.photo.rawValue)
let fetchResults = PHAsset.fetchAssets(in: collection, options: fetchOptions)
But the first part can give me albums with only videos, which means that after I apply the predicate to the second part, the album will be empty. Is there a way to filter out those albums in the first part, before I start using them?

It seems that collections cannot be filtered like this without also fetching the items in the collections. See the docs for available fetch options; none allow filtering by number of a specific type of media.
The way I would achieve this is by fetching all albums created by the user, and then fetching the assets from the albums with a predicate that returns only images.
So to put it in code:
var userCollections: PHFetchResult<PHAssetCollection>!
// Fetching all PHAssetCollections with at least some media in it
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "estimatedAssetCount > 0")
// Performing the fetch
userCollections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: options)
Next, fetch the assets from a collection that are images by specifying a predicate:
// Getting the specific collection (I assumed to use a tableView)
let collection = userCollections[indexPath.row]
let optionsToFilterImage = PHFetchOptions()
optionsToFilterImage.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
// Fetching the asset with the predicate to filter just images
let justImages = PHAsset.fetchAssets(in: collection, options: optionsToFilterImage)
Lastly, count the number of images:
if justImages.count > 0 {
// Display it
} else {
// The album has no images
}

Related

Swift: get assets from Photo Library with excluding subtypes

I want to get list of assets from Photo Library but exclude subtypes or assets from smartfolders such as smartAlbumBursts, smartAlbumLivePhotos, smartAlbumScreenshots
my code is
let options = PHFetchOptions()
options.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: true) ]
options.predicate = predicate
let assets = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: options)
I was trying to make predicate like that:
let predicateType = NSPredicate(format: "mediaSubtypes != %#",
PHAssetMediaSubtype.photoScreenshot as! CVarArg)
but 1. it crashes 2. i can add PHAssetMediaSubtype only for screenshot and livePhoto but not for burst photo.
I know that there is method
if let collection = PHAssetCollection.fetchAssetCollections(with: .smartAlbum,
subtype: .smartAlbumBursts, options: nil).firstObject {
but i am not sure how to use that metdhod or subtype for my purposes
Was trying to use representsBurst but have a crash:
let predicateType = NSPredicate(format: "representsBurst == %#", NSNumber(value: false))
reason: 'Unsupported predicate in fetch options: representsBurst == 0'
Please refer to the table of supported predicate and sort descriptor keys of PHFetchOptions.
I'd make an assumption that if an asset doesn't represent a burst then it doesn't have a burst identifier. You can combine it as you want:
let options = PHFetchOptions()
options.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: true) ]
// fetch all images with no burstIdentifier
options.predicate = NSPredicate(format: "burstIdentifier == nil")
var assets = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: options)
// fetch all images with photoScreenshot media subtype
options.predicate = NSPredicate(format: "((mediaSubtype & %d) != 0)", PHAssetMediaSubtype.photoScreenshot.rawValue)
assets = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: options)
// fetch all images with photoLive media subtype
options.predicate = NSPredicate(format: "((mediaSubtype & %d) != 0)", PHAssetMediaSubtype.photoLive.rawValue)
assets = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: options)
// fetch all non-photoScreenshot and non-photoLive images
options.predicate = NSPredicate(format: "NOT (((mediaSubtype & %d) != 0) || ((mediaSubtype & %d) != 0))", PHAssetMediaSubtype.photoScreenshot.rawValue, PHAssetMediaSubtype.photoLive.rawValue)
assets = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: options)
// fetch all non-photoScreenshot and non-photoLive images with no burstIdentifier
options.predicate = NSPredicate(format: "NOT (((mediaSubtype & %d) != 0) || ((mediaSubtype & %d) != 0)) && burstIdentifier == nil", PHAssetMediaSubtype.photoScreenshot.rawValue, PHAssetMediaSubtype.photoLive.rawValue)
assets = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: options)
NSPredicate is tricky. The way to say "not" is like this:
NSPredicate(
format: "!((assetCollectionSubtype & %d) == %d)",
PHAssetCollectionSubtype.smartAlbumBursts.rawValue)

How to fetch only image assets from smart albums with NSPredicate in Swift 4.2?

I want to fetch image assets from all smart albums on the users phone. At the moment I am fetching images and videos, but thats not what I want.
On my research I have always found the same solution but it doesn't seem to work any more since the questions are several years old now. So I can't find any up-to-date solution.
This is the solution I have found
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .any, options: fetchOptions)
let fetchAssetsResult = PHAsset.fetchAssets(in: collection, options: fetchOptions)
On executing this lines of code I get the following error message
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate in fetch options: mediaType == 1'
Is there any new working solution?
With the help of Bappaditya I have found out that I was using the predicate on the wrong asset fetch. The mediaType key is just for PHAsset.fetchAsset() and not for PHAssetCollection.fetchAssetCollection() as I was using it for.
This code is working now
let fetchOptions = PHFetchOptions()
let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .any, options: fetchOptions)
fetchOptions.predicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue)
let fetchAssetsResult = PHAsset.fetchAssets(in: collection, options: fetchOptions)
Try,
let albumName = "Your Album Name" or "Your Album Local Identifier"
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
let fetchResult: PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
The supported keys are,
For more details please refer PHFetchOptions

How to fetch photos album title, image count in swift?

I'm building Gallery app like a iOS standard Photos App. (Swift 4.1)
I want to fetch the thumbnails, titles, and total number of images that I can see when I launch the standard photo app.
The Photos framework seems more complex than I thought.
It is not easy to find a way to explain why, and what procedures should be approached.
Can you tell me about this?
The minimum number of steps to achieve what you are asking is:
// import the framework
import Photos
// get the albums list
let albumList = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
// you can access the number of albums with
albumList.count
// individual objects with
let album = albumList.object(at: 0)
// eg. get the name of the album
album.localizedTitle
// get the assets in a collection
func getAssets(fromCollection collection: PHAssetCollection) -> PHFetchResult<PHAsset> {
let photosOptions = PHFetchOptions()
photosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
photosOptions.predicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue)
return PHAsset.fetchAssets(in: collection, options: photosOptions)
}
// eg.
albumList.enumerateObjects { (coll, _, _) in
let result = self.getAssets(fromCollection: coll)
print("\(coll.localizedTitle): \(result.count)")
}
// Now you can:
// access the count of assets in the PHFetchResult
result.count
// get an asset (eg. in a UITableView)
let asset = result.object(at: indexPath.row)
// get the "real" image
PHCachingImageManager.default().requestImage(for: asset, targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: nil) { (image, _) in
// do something with the image
}
I also suggest to take a look at the Apple sample code for the Photos framework, is not hard to follow, together with the Photos framework documentation.

PHFetchOptions include only photos from iPhone camera

Right now I use that to exclude Screenshots:
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "NOT (mediaSubtype & \(PHAssetMediaSubtype.photoScreenshot.rawValue) != 0)")
var fetchResult: PHFetchResult<PHAsset> = fetchResult =
PHAsset.fetchAssets(with: PHAssetMediaType.image, options: options)
But I want to include only photos that were taken from iPhone Camera (not photos that were saved from other applications like Skype and so on). Is it possible?

PHFetchOptions() Photos Only Using PHAsset & PHAssetCollection

I am using a chunk from Apple's sample code here:
override func awakeFromNib() {
// Create a PHFetchResult object for each section in the table view.
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let allPhotos = PHAsset.fetchAssetsWithOptions(allPhotosOptions)
let smartAlbums = PHAssetCollection.fetchAssetCollectionsWithType(.SmartAlbum, subtype: .AlbumRegular, options: nil)
let topLevelUserCollections = PHCollectionList.fetchTopLevelUserCollectionsWithOptions(nil)
// Store the PHFetchResult objects and localized titles for each section.
self.sectionFetchResults = [allPhotos, smartAlbums, topLevelUserCollections]
self.sectionLocalizedTitles = ["", NSLocalizedString("Smart Albums", comment: ""), NSLocalizedString("Albums", comment: "")]
PHPhotoLibrary.sharedPhotoLibrary().registerChangeObserver(self)
}
This lists all Albums successfully.
What I Need:
I want to only list albums with photos, exclude videos. Also, exclude video's from getting listed inside of albums, such as inside "All Photos".
What I tried:
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
This causes to crash saying 'Unsupported predicate in fetch options: mediaType == 1'
I know this is a very late answer, but in case someone like me gets here, you should use this method to retrieve a media type. In this case PHAssetMediaType.image
func fetchAssets(with mediaType: PHAssetMediaType, options: PHFetchOptions?) -> PHFetchResult<PHAsset>
So in Swift 5 it writes as
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchResult = PHAsset.fetchAssets(with: .image, options: allPhotosOptions)
So far it works fine to me :
let allAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
let someAlbum = allAlbums.object(at: 0)
let onlyPhotoOption = PHFetchOptions()
onlyPhotoOption.predicate = NSPredicate(format: "mediaType == %i", PHAssetMediaType.image.rawValue)
let photos = PHAsset.fetchAssets(in: someAlbum, options: onlyPhotoOption)

Resources