Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to to filter by media subtype using NSPredicate with PHFetchOptions

How do I filter by media subtype using NSPredicate with PHFetchOptions? 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 predicate field of PHFetchOptions.

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 mediaSubtypes (with an "s"), while their sample predicate is filtering on mediaSubtype (without an "s").

Trying to filter on mediaSubtypes 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?

like image 687
bcattle Avatar asked Jul 21 '15 00:07

bcattle


1 Answers

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.

like image 92
Tarun Tyagi Avatar answered Nov 09 '22 08:11

Tarun Tyagi