z2k
z2k

Reputation: 10410

Filter PHAssetCollections within radius of CLLocationCoordinate2d

I'm using the Photos framework (aka PhotoKit). In my app I need to gather Moments (which are of type PHAssetCollection). PHAssetCollection has a property of CLLocation *approximateLocation.

However I cannot get the NSPredicate to work when I retrieve the Moments from PhotoKit.

-(void)getMomentsNearCoordinate:(CLLocationCoordinate2D)coordinate completionBlock:(PKAssetManagerArrayBlock)completionBlock{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        PHFetchOptions *options = [[PHFetchOptions alloc] init];
        options.predicate = [NSPredicate predicateWithFormat:@"approximateLocation.coordinate.latitude < 37.0"];

        self.moments = [PHAssetCollection fetchMomentsWithOptions:options];

        // Convert PHCollection into NSArray
        NSMutableArray *momentsArray = [[NSMutableArray alloc]initWithCapacity:self.moments.count];
        [self.moments enumerateObjectsUsingBlock:^(PHAssetCollection *moment, NSUInteger idx, BOOL *stop) {
            [momentsArray addObject:moment];
        }];

        dispatch_async(dispatch_get_main_queue(), ^{
            completionBlock([NSArray arrayWithArray:momentsArray]);
        });
    });
}

The debugger will stop on

self.moments = [PHAssetCollection fetchMomentsWithOptions:options];

with the error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate in fetch options: approximateLocation.coordinate.latitude < 37'

This seems strange since I can use NSPredicate to filter by startDate or endDate.

Anyhow, next I thought I'd try the NSPredicate with block:

-(void)getMomentsNearCoordinate:(CLLocationCoordinate2D)coordinate completionBlock:(PKAssetManagerArrayBlock)completionBlock{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        PHFetchOptions *options = [[PHFetchOptions alloc] init];
        options.predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
            // Logic goes here
            return YES;
        }];

        self.moments = [PHAssetCollection fetchMomentsWithOptions:options];

        // Convert PHCollection into NSArray
        NSMutableArray *momentsArray = [[NSMutableArray alloc]initWithCapacity:self.moments.count];
        [self.moments enumerateObjectsUsingBlock:^(PHAssetCollection *moment, NSUInteger idx, BOOL *stop) {
            [momentsArray addObject:moment];
        }];
        dispatch_async(dispatch_get_main_queue(), ^{
            completionBlock([NSArray arrayWithArray:momentsArray]);
        });
    });
}

Again the debugger stops at

self.moments = [PHAssetCollection fetchMomentsWithOptions:options];

with a different error message:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate in fetch options: BLOCKPREDICATE(0x27d338)'

Finally I remembered reading in CloudKit documentation that they added a new support for filtering by distance. However this is for CKQuery, not NSPredicate. I decided to give it a try anyhow. I use my coordinate to make a CLLocation then call:

-(void)getMomentsNearLocation:(CLLocation*)location completionBlock:(PKAssetManagerArrayBlock)completionBlock{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        PHFetchOptions *options = [[PHFetchOptions alloc] init];
        options.predicate = [NSPredicate predicateWithFormat:@"distanceToLocation:fromLocation:(%K,%@) < %f",
                             location,
                             2.0];

        self.moments = [PHAssetCollection fetchMomentsWithOptions:options];

        // Convert PHCollection into NSArray
        NSMutableArray *momentsArray = [[NSMutableArray alloc]initWithCapacity:self.moments.count];
        [self.moments enumerateObjectsUsingBlock:^(PHAssetCollection *moment, NSUInteger idx, BOOL *stop) {
            [momentsArray addObject:moment];
        }];
        dispatch_async(dispatch_get_main_queue(), ^{
            completionBlock([NSArray arrayWithArray:momentsArray]);
        });
    });
}

You guessed it. Error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CLLocation rangeOfString:]: unrecognized selector sent to instance 0x19e3e350'

In the mean time I'll just iterate through the list of PHAssetCollections and manually calculate if it's near the CLLocation. This will be far less efficient.

Upvotes: 7

Views: 628

Answers (1)

BlueskyMed
BlueskyMed

Reputation: 1305

If you look at Table 1 of the PHFetchOptions documentation, you will see that you can only use the following keys for predicate filtering of PHAssetCollections:

SELF
localIdentifier
localizedTitle (or title)
startDate
endDate
estimatedAssetCount

if you want to filter a PHCollection (as opposed to an PHAssetCollection), then you are limited to the following keys:

SELF
localIdentifier
localizedTitle (or title)
startDate
endDate

Nothing resembling "approximateLocation" is a support key. But wait! The localizedTitle is generally a place name. Have not seen it otherwise. So, you might be able to get assets by predicate filtering moment collections

Upvotes: 0

Related Questions