bcattle bcattle - 1 month ago 9x
Objective-C Question

How to to filter by media subtype using NSPredicate with PHFetchOptions

How do I filter by media subtype using

I'm trying to exclude slow mo (high frame rate) and time lapse videos. I keep getting strange results when I try to use the
field of

My phone has a bunch (120+) regular videos, and one slow mo video. When I run the example from Apple's docs, I get the correct result back: 1 slow mo video.

PHFetchOptions *options = [PHFetchOptions new];
options.predicate = [NSPredicate predicateWithFormat:@"(mediaSubtype & %d) != 0 || (mediaSubtype & %d) != 0", PHAssetMediaSubtypeVideoTimelapse, PHAssetMediaSubtypeVideoHighFrameRate];

But I'm trying to exclude slow mo, rather than select it. However if I negate the filter condition, I get zero results back:

options.predicate = [NSPredicate predicateWithFormat:@"(mediaSubtype & %d) == 0", PHAssetMediaSubtypeVideoHighFrameRate];

<PHFetchResult: 0x1702a6660> count=0

Confusingly, the Apple docs list the name of the field as
(with an "s"), while their sample predicate is filtering on
(without an "s").

Trying to filter on
produces an error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't do bit operators on non-numbers'

Has anyone been able to make heads or tails of this predicate?


First of all, let me say about the s in mediaSubtypes available on PHAsset and only Photos team at Apple knows why and how mediaSubtype works without an s.

@property (nonatomic, assign, readonly) PHAssetMediaType mediaType;
@property (nonatomic, assign, readonly) PHAssetMediaSubtype mediaSubtypes;

Now let me try to explain why it doesn't work as expected. Here's how you compare using NS_OPTIONS or any other bitwise operation.

PHAsset* asset = // my asset;
if((asset.mediaSubtypes & PHAssetMediaSubtypePhotoScreenshot) == PHAssetMediaSubtypePhotoScreenshot) {
    // This is a screenshot

Here's how you would compare to see if asset is not a screenshot-

PHAsset* asset = // my asset;
if(!((asset.mediaSubtypes & PHAssetMediaSubtypePhotoScreenshot) == PHAssetMediaSubtypePhotoScreenshot)) {
// This is not a screenshot

Now in your case the predicate should be-

  • Fetch all videos with high frame rate

    [NSPredicate predicateWithFormat:@"((mediaSubtype & %d) == %d)", PHAssetMediaSubtypeVideoHighFrameRate, PHAssetMediaSubtypeVideoHighFrameRate];
  • Fetch all videos without high frame rate

    [NSPredicate predicateWithFormat:@"!((mediaSubtype & %d) == %d)", PHAssetMediaSubtypeVideoHighFrameRate, PHAssetMediaSubtypeVideoHighFrameRate];

This works beautifully in my case. Hope it helps somebody else.