Passing the NSArray Test

The Setup and Crash

The other night I was working on the event image gallery for WhoNote and stumbled upon an interesting scenario. I am using UIPageViewController instead of an infinite UIScrollview to mimic the native photo app, I created a POC for this when I was at the WWDC so I thought I could just reuse that.

CRASH

The source of the crash wasn’t immediately obvious. I setup the controller properly and made all the necessary delegate/datasource associations. What was the problem? Turns out that in the the POC my image array was populated with photo objects, but the image array I’m passing around in WhoNote between view controllers contains NSDictionaries.

Consequently, when calling

NSUInteger currentIndex = [self.imageArray indexOfObject:currentPhotoObject];

I get an index that is not only out of bounds, but an incorrect object. The reason for getting the index of the object is that I can handle the forward and backwards pagination with the UIPageViewController and which image to display.

Time to rethink my implemenation approach.

Predicates

The first avenue I went for was NSPredicate. Though the background of NSPredicate is out-of-scope for this article I would suggest reading Cocoanetics’ Filtering Fun with Predicates for some pretty interesting usages of this class.

Search Criteria

Because I don’t have instances of WNPhoto in my array I needed a way to efficiently search the array of dictionaries for a specific key value and then stop.

Example of values:

Finding a predicate to satisify my search requirements was easy:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(ffUrl ==  %@)", @"ffUrlToPhoto"];
NSArray *filteredArray = [data filteredArrayUsingPredicate:predicate];

But that didn’t do me any good with the UIPageViewController pagination because now I an array with only one element. I really didnt’ want have to write some less than optimatal iterator to manually search the array for the value I was looking for.

When In Doubt Read the Docs

The methods that Apple provides for NSArray are extensive, and while on the surface it might appear they could be duplicated I’m going to go ahead and say that the engineers at Apple are smarter than me and figured out the best ways to find the needle in the haystack.

indexOfObjectWithOptions:passingTest:

Returns the index of an object in the array that passes a test in a given Block for a given set of enumeration options.

Sounds good to me. I do feel the method signature could be improved, but that is for another discussion. After fumbling the syntax a couple of times I ended up with logic that provided the solution I was needing without having to mutate the original dataset. More importantly I learned something new.

NSUInteger currentIndex = [self.imageArray indexOfObjectWithOptions:NSEnumerationConcurrent passingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)  {
       NSDictionary *metaDict = (NSDictionary *)obj;

       if ([metaDict[@"ffUrl"] isEqualToString:metaData.ffUrl]) {

         *stop = YES;

        return *stop;
     }

     return NO;
  }];