Rekindling a relationship with Javascript

Javascript was my first language. At the time, I had no idea what were good and bad qualities of a language. I had no idea what I was doing. I left Javascript for Flex and then traded in Flex for iOS and now I’ve come full circle back to Javascript. It’s been 8 years since I’ve written a line of meaningful Javascript.

I’m going to try to remember where I left off at. I’m going to see what I missed. I’m going to see if Javascript has evolved and matured over the years. My very own “History of Javascript”.

My thoughts on Reactive Programming in iOS

I’ve been seeing a lot this topic lately and I thought I’d share my opinion on it. The first thing I’d like to say is that binding is easily the #1 feature request that I want brought over to iOS from Cocoa on OS X. As a long time Flex developer I’ve had the enjoyment of binding data to UI controls. For front-end development, it saves so much time and you start wondering how you’ve ever developed front-ends without it.

Even though it’s been a complete dream of mine to have reactive programming on iOS, here are the key reasons for me on why I’m waiting:

Memory and Performance

Binding is expensive. On iOS, we don’t have the luxury of speed of a desktop or laptop computer. Over time this will change but there is still a large number of devices that are iPhone 4, 4S and older generation iPads. To me this makes sense as the #1 reason that Apple hasn’t included binding in iOS. Reactive Cocoa, the popular framework for iOS is seeing a little bit of the performance issues that binding presents. I strongly believe in Larry Pages quote about speed:

“Speed is product feature number one”

I’m not talking about premature optimization here but if your software performs faster your users will have a better user experience, and that is what matters the most.  A lot of the early versions of libraries will have performance issues.

Maintainability

Reactive Cocoa is a great library for achieving binding on iOS, but what are you going to do when Apple finally includes binding natively on iOS?  Will you update all of your apps? One example I have is NSJSONSerialization. There were a few libraries out there that touted ease of use and performance benefits and you had to do your own analysis on each library and decide.  When it released, it was the fastest JSON serializer available on iOS and it shipped with Cocoa so you don’t have to worry about updating the library. It’s very easy for developers to ignore changes that should be made internally to an app that will be better long term.

Know when to use a hammer and when to use an axe

It’s very easy to complicate application architecture with such a powerful tool. In my Flex days, there were developers using binding at an entire class level versus property level and they kept all of the models in the entire application in one class to seem “tidy” but was suffering huge performance issues over time because the binding had to react to every single property in that class whenever binding fired.  This would be an absolute nightmare on a mobile device with smaller resources.

There are some benefits to using reactive cocoa or other binding frameworks on iOS, just use it wisely and program on purpose.

It takes a lot of discipline to go against the popular trend right now but with these trade-offs, I think I’ll wait a little longer. I’m confident that reactive programming on iOS will be as common as IBOutlet.

Objective-C OAuth2 with Google

It’s almost a necessity for any application these days to connect to a third party system for an external service. The authentication method of choice used by many of these third party companies is oAuth2.  The Google API in some cases requires you to use OAuth2 so a user can authenticate with Google and return your application an authorized token which you can make requests to Google on behalf on that authenticated user.

For Objective-C, Google has an official library (although version 0.0.2 at the time of this post) but it’s pretty outdated. Lets go into what makes it great:

  • Contains a view controller with a UIWebView with browser back and forward buttons built in
  • Has the ability to check if the token is still valid
  • Saves the access token to NSUserDefaults
  • Allows you to clear out the oauth token

Here’s what could be improved:

  • The browser back and forward buttons no longer work in iOS7
  • Default back button in view controller is hidden in iOS7
  • Uses a third party library called SBJSON for JSON serialization. iOS now has native serialization with NSJSONSerialization and we don’t need this third party dependency.  NSJSONSerialization has also been benchmarked to be the fasted serializer.

The first two are unfortunately deal breakers as it provides a frustrating experience for the user.  Lets look at how we would create our own alternative.

Google Console

You’ll need to get a client id and client secret from the Google API console. For this example we’ll be using the  Google Contacts CardDAV API. Under registered apps, create one for iOS to obtain the client id and client secret along with setting the redirect URI to localhost.

Creating the view controller

Create a view controller with a xib that will have a UIWebView. Don’t forget to set the vc as the delegate to the UIWebView. You’ll then need to create a delegate:

@class GNGoogleContactsOAuth2ViewController;
 
@protocol GNGoogleContactsOAuth2Delegate 
 
- (void)googleOAuth2:(__weak GNGoogleContactsOAuth2ViewController *)googleOAuth2 didRetrieveAccessToken:(NSString *)accessToken andRefreshToken:(NSString *)refreshToken;
 
@end
 
@interface GNGoogleContactsOAuth2ViewController : UIViewController
 
@property (weak) id delegate;
 
@end

Then in viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    _redirectURI = @"urn:ietf:wg:oauth:2.0:oob";
    [_myWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://accounts.google.com/o/oauth2/auth?state=/profile&redirect_uri=%@&response_type=code&client_id=%@&approval_prompt=force&access_type=offline&scope=https://www.google.com/m8/feeds", _redirectURI, GNGoogleCalendarClientID]]]];
}

*for some reason wordpress is html parsing the & in the url to & so please be aware if you are copying and pasting.

The redirect URI looks odd but it’s telling the oauth system that it’s a local application and not a web redirect after authentication. You should see this address in the Google console.

Now when you display this view controller, the UIWebView will load up Google’s account authentication screen.

Handling the UIWebViewDelegate

The next step is to handle the user authenticating properly to Google and extracting the oauth code. The code is in the documents title so we check the title for a success string and extract the code accordingly. Once we get the code, we have to make another request to Google, this time at their oauth2 endpoint to request an access token. This is where the user can grant permissions to your application.
 

#pragma mark -
#pragma mark UIWebViewDelegate
 
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
	return YES;
}
 
- (void)webViewDidFinishLoad:(UIWebView *)webView{
	[self dismissHUD];
    NSString *theTitle = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
	if( [theTitle rangeOfString:@"Success"].location != NSNotFound ) {
 
		NSArray *strings = [theTitle componentsSeparatedByString:@"&"];
		if( [strings count] > 0 ) {
			[self showBusyWithAllowedInteraction];
			NSString *code = [[strings objectAtIndex:[strings count]-1] substringFromIndex:5];
 
			__weak GNGoogleContactsOAuth2ViewController *weakSelf = self;
			NSURL *url = [NSURL URLWithString:@"https://accounts.google.com"];
			AFOAuth2Client *oauthClient = [AFOAuth2Client clientWithBaseURL:url clientID:GNGoogleCalendarClientID secret:GNGoogleCalendarClientSecret];
			[oauthClient authenticateUsingOAuthWithPath:@"/o/oauth2/token"
							       code:code
							redirectURI:_redirectURI
							    success:^(AFOAuthCredential *credential) {
 
								[_delegate googleOAuth2:weakSelf didRetrieveAccessToken:credential.accessToken andRefreshToken:credential.refreshToken];
								[_myWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://mail.google.com/mail/u/0/?logout&hl=en"]]];
 
							} failure:^(NSError *error) {
								[weakSelf dismissViewControllerAnimated:YES completion:nil];
							}];
			[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
		}
	}
}

 

For authenticating I’m using the AFOAuth2 library since I’m already using AFNetworking. In the success block we pass the accessToken to the delegate but I want to point out that we make another URL request to Google. This is a hack that actually logs the user out of Google services.
 
That’s it! It’s a simple alternative but it’s still missing some features like keeping the access token in NSUserDefaults. Hopefully in a future post I’ll get this in a lib and put it on GitHub.

 

 

 

Unit testing UITableViewCell

It might not seem obvious but unit testing data that exists or that is formatted in your UITableViewCells is important.  One example would be if you’re loading data from JSON via a dictionary, null objects serialize as [NSNull class] and if you’re displaying it as text – would use [NSNull description] which would show up as “<null>” in one of your strings. Worse, you could be trying to perform some action on the NSNull object like expecting an array and could be trying to execute [NSNull length] and your app would crash. There are other reasons but I won’t go into that discussion as this post is about my findings on how to properly setup testing for UITableViewCells. Lets take a look at the UITableViewCell implementation before we look at how we can test it.

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  TweetObj *tweet = [_tweets objectAtIndex:indexPath.row];
  if( tweet.type == TweetTypeImage ) {
    TweetImageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TweetImageTableViewCellIdentifier];
    [self configureCell:cell forIndexPath:indexPath];
    return cell;
  } else if( tweet.type == TweetTypeNormal ) {
    // do another cell
  } else {
    // another cell
  }
}
 
- (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath
{
  if( [cell isKindOfClass:[TweetImageTableViewCell class]] ) {
    TweetObj *tweet = [_tweets objectAtIndex:indexPath.row];
    NSString *firstName = [tweet.data objectForKey:@"first_name"];
    NSString *lastName = [tweet.data objectForKey:@"last_name"];
    NSString *name = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
    [((TweetImageTableViewCell *)cell).nameLabel setText:name];
 
  } else if ( [cell isKindOfClass:SomeOtherTableViewCell class]] ) {
    // do other
  }
}

 

My first approach using OCMock

OCMock is great.  It can be used for mocking xibs and stubbing objects that return pre-determined values for specific method invocations and can verify interaction patterns. Naturally I thought about using it for unit testing a table view cell. I couldn’t get it working (and I’ll get to why) but here is the test I wrote out that would expect to work.

 

@implementation TableViewCellTests
 
  - (void)setUp
  {
    _controller = [[MySampleViewController alloc] init];
    _tableViewMock = [OCMockObject niceMockForClass:[UITableView class]];
    [_tableViewMock registerNib:[UINib nibWithNibName:@"MyTableViewCell" bundle:nil] forCellReuseIdentifier:MyTableViewCellIdentifier];
  }
 
    - (void)testTweetImageCell
    {
        TweetObj *tweet = [[TweetObj alloc] init];
        tweet.type = TweetTypeImage;
        tweet.data = [NSMutableDictionary dictionaryWithDictionary:@{ @"first_name" : @"firstname", @"last_name" : @"lastname" }];
        _mockTweets = [NSMutableArray arrayWithObject:tweet];
        [_controller setValue:_mockTweets forKey:@"_tweets"];
 
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
        [[[_tableViewMock expect] andReturn:[[[NSBundle mainBundle] loadNibNamed:@"TweetImageTableViewCell" owner:self options:nil] lastObject]] dequeueReusableCellWithIdentifier:MyTableViewCellIdentifier forIndexPath:indexPath];
 
        TweetImageTableViewCell *cell = (TweetImageTableViewCell *)[_controller tableView:_tableViewMock cellForRowAtIndexPath:indexPath];
        STAssertNotNil( cell, @"should not be nil" );
        STAssertTrue( [cell.nameLabel.text isEqualToString:@"firstname lastname"], @"should be equal" );
        [_tableViewMock verify];
    }
 
@end

If your’e familiar with OCMock, the test mockup looks legit. If you set breakpoints it’ll even go though configureCell:forIndexPath. The problem is isKindOfClass. The cell doesn’t match any of the cells in the conditional statement and skips over any of the cell rendering so the test fails. I also tried:

id mockCell = [OCMockObject partialMockForObject:[[[NSBundle mainBundle] loadNibNamed:@"MyTableViewCell" owner:self options:nil] lastObject]];
[[[mockCell stub] andReturnValue:OCMOCK_VALUE((BOOL) {YES})] isKindOfClass:[OCMConstraint isKindOfClass:[MyTableViewCell class]]];
[[[_tableViewMock expect] andReturn:mockCell] dequeueReusableCellWithIdentifier:MyTableViewCellIdentifier forIndexPath:indexPath];

After lots and lots of googling, OCMock uses NSProxy to mock and stub objects and isKindOfClass isn’t going to work here.

So what are our options? What we’re trying to test is the data transformation and not necessarily the rendering of the table view cell. If we refactor this and apply a controller to manipulate the data, we can now unit test it without using OCMock.

@interface TweetModelController : NSObject
 
- (instancetype)initWithArray:(NSArray *)model;
- (NSString *)nameAtIndexPath:(NSIndexPath *)indexPath;
 
@end
 
@interface TweetModelController() {
  NSArray *_model;
}
 
@end
 
@implementation @TweetModelController
 
- (instancetype)initWithArray:(NSArray *)model
{
  if( self = [super init] ) {
    _model = [model copy];
  }
  return self;
}
 
- (NSString *)nameAtIndexPath:(NSIndexPath *)indexPath
{
  if( [_model count] &gt; indexPath.row ) {
    NSString *name = //build the name string
    return name;
  }
}
@end

Now lets see this implemented in the table view cell.

- (void)viewDidLoad
{
  [super viewDidLoad];
  // initalize data model
  _modelController = [[TweetModelController alloc] initWithArray:_tweets];
}
 
- (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath
{
  if( [cell isKindOfClass:[TweetImageTableViewCell class]] ) {
    [((TweetImageTableViewCell *)cell).nameLabel setText:[_modelController nameAtIndexPath:indexPath];
 
  } else if ( [cell isKindOfClass:SomeOtherTableViewCell class]] ) {
    // do other
  }
}

The key thing to remember is remember all data manipulation should now occur in the controller, if we don’t then we won’t be able to have tests.

@implementation TableViewCellTests
 
- (void)setUp
{
  Tweet *tweet = [[Tweet alloc] init];
  tweet.type = TweetTypeImage;
  tweet.data = [NSMutableDictionary dictionaryWithDictionary:@{ @"first_name" : @"firstname", @"last_name" : @"lastname" }];
  _stubTweets = [NSMutableArray arrayWithObject:tweet];
  _controller = [[TweetModelController alloc] initWithArray:_stubTweets];
}
 
- (void)testTweetImageCell
{
  NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
  NSString *testString = [_controller nameAtIndexPath:indexPath];
  STAssertTrue( [testString isEqualToString:@"firstname lastname"] );
}
@end

Refactoring so your code can be more testable is a very common practice and in this case it was the right solution.

Objective-C Map with index

I have a previous post on map in Objective-C and some simple use cases for it. Another variation is you might want to know the index position of the object you are mapping in the array.  In order to get the index, we’ll have to use enumerateObjectsUsingBlock:

[someArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  NSLog(@"current index is %d", idx);
}];

Now that we know how we’re going to get our index, we can continue on by creating the message signature.

-(NSArray *)arrayByMappingObjectsUsingBlockWithIndex:(id(^)(id, NSUInteger, BOOL*))blockWithIndex;

You can also create a typedef for the block.

typedef id(^MapBlockWithIndex)(id object, NSUInteger idx, BOOL *stop);
 
@interface NSArray (Map)
 
- (NSArray *)arrayByMappingObjectsUsingBlockWithIndex:(MapBlockWithIndex)blockWithIndex;
 
@end

The final step is to create the the implementation which requires us to have a temporary mutable array that we append to when executing enumerateObjectsUsingBlock:

- (NSArray *)arrayByMappingObjectsUsingBlockWithIndex:(id(^)(id, NSUInteger, BOOL *))blockWithIndex
{
	NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]];
	[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
		[result addObject:blockWithIndex(obj, idx, stop)];
	}];
	return result;
}

That’s it! Here is the updated link to the repo with the previous version of map as well.

Objective-c blocks as properties with AFNetworking

So you’ve decided on AFNetworking for your networking library. You are happily coding using blocks for success and failure handlers. You are however starting to get annoyed with having some duplicate code in the error handling block such as dismissing an existing HUD and/or displaying errors to the user. There is a solution for this code smell but lets first take a look at one of the AFNetworking samples:

NSURL *url = [NSURL URLWithString:@"https://alpha-api.app.net/stream/0/posts/stream/global"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
    success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSLog(@"App.net Global Stream: %@", JSON);
    } failure:^(NSURLRequest *, NSHTTPURLResponse *, NSError *, id ) {
        NSLog(@"fail whale!");
}];
[operation start];

Now lets see how we can handle this failure with a regular block first. Remember, blocks are C functions.

void (^failureBlock)(NSURLRequest *, NSHTTPURLResponse *, NSError *, id) = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
	NSLog(@"fail from a block");
	};
 
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
    success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSLog(@"App.net Global Stream: %@", JSON);
    } failure:failureBlock];

Or if we want to add additional functionality..

void (^failureBlock)(NSURLRequest *, NSHTTPURLResponse *, NSError *, id) = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
	NSLog(@"fail from a block");
	};
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
    success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSLog(@"App.net Global Stream: %@", JSON);
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON ) {
        NSLog(@"do other stuff");
        failureBlock( request, response, error, JSON);
    }];

Pretty awesome huh? Now lets get to making it a property. Be warned that when using blocks you can run into retain/release dance and can result in leaks so make sure you’re familiar with how to handle memory management with blocks or you’ll leak. At the time of this blog post, ARC doesn’t have a solution for this yet. With that said, see how the declaration uses copy because they need to outlive the scope they were created in. If you don’t use copy, you’ll get EXC_BAD_ACCESS.

@interface SomeBaseViewController : UIViewController
 
@property (copy) void (^jsonRequestOperationFailedBlock)(NSURLRequest *, NSHTTPURLResponse *, NSError *, id );
 
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    __weak MyCurrentClass *weakSelf = self;
    [self setJsonRequestOperationFailedBlock:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
        [weakSelf dismissHUD];
        [weakSelf presentErrorFromStatusCode:[response statusCode]];
    }];
}

That’s it! Now you’ll be able to use Objective-C blocks as properties for other things other than AFNetworking.

Flatten NSDictionary and Associated References

This is a great exercise using Objective-C associated references to apply instance variables to a category. I first started by creating a utility class but also thought it would be cleaner as a NSDictionary category. For clarity on flattening the NSDictionary, lets first take a look at the utility class version before we get into associated references.

Lets create a test first so we know what to expect.

#import <senTestingKit/SenTestingKit.h>
#import "NSDictionaryUtils.h"
 
@interface NSDictionaryUtilsTests : SenTestCase {
 
}
 
- (void)testFlatten;
 
@end
 
@implementation NSDictionaryUtilsTests
 
- (void)setUp {
	[super setUp];
}
 
- (void)tearDown {
	[super tearDown];
}
 
- (void)testFlatten {
	NSDictionary *collection1 = @{ @"firstName" : @"Foo", @"lastName" : @"Bar", @"middleName" : @"middle" };
	NSArray *collection2 = @[@"134234", @"234323"];
	NSSet *collection3 = [[NSSet alloc] initWithObjects:@"fooset", @"barset", nil];
 
	NSDictionary *flatten:dictionaryToFlatten = [[NSDictionary alloc] initWithObjectsAndKeys:collection1, @"key1", collection2, @"key2", collection3, @"key3", nil];
 
        NSDictionaryUtils *dictUtils = [[NSDictionaryUtils alloc] init];
	NSDate *startTime = [NSDate date];
	NSMutableArray *testArray = [dictUtils flatten:dictionaryToFlatten];
	NSDate *endTime = [NSDate date];
 
	NSTimeInterval executionTime = [endTime timeIntervalSinceDate:startTime];
	NSLog(@"execution took approx: %2f seconds", executionTime );
 
	STAssertTrue( [testArray count] == 7, @"should be equal");
}
 
@end

 

#import <foundation/Foundation.h>
 
@interface NSDictionaryUtils : NSObject
 
- (NSMutableArray *)flatten:(NSDictionary *)dictionary;
 
@end
#import "NSDictionaryUtils.h"
 
@interface NSDictionaryUtils() {
	NSMutableArray *_tmpArray;
}
 
- (void)flattenWithRecursion:(id)object;
- (void)enumerate:(id)object depth:(int)depth parent:(id)parent;
 
@end
 
@implementation NSDictionaryUtils
 
- (NSMutableArray *)flatten:(NSDictionary *)dictionary {
	_tmpArray = [NSMutableArray array];
	[self flattenWithRecursion:dictionary];
	return _tmpArray;
}
 
- (void)flattenWithRecursion:(id)object {
	[self enumerate:object depth:0 parent:nil];
}
 
- (void)enumerate:(id)object depth:(int)depth parent:(id)parent {
 
	if( [object isKindOfClass:[NSDictionary class]] ) {
		for( NSString * key in [object allKeys] ) {
			id child = [object objectForKey:key];
			[self enumerate:child depth:depth+1 parent:object];
		}
 
	} else if( [object isKindOfClass:[NSArray class]] ) {
 
		for( id child in object ) {
			[self enumerate:child depth:depth+1 parent:object];
		}
 
	} else if( [object isKindOfClass:[NSSet class]] ) {
 
		for( id child in object ) {
			[self enumerate:child depth:depth+1 parent:object];
		}
 
	} else{
		//	not a collection/container it has ended
		//NSLog(@"Node: %@  depth: %d",[object description],depth);
		[_tmpArray addObject:object];
	}
}
 
@end

As you can see – (void)enumerate:depth:parent does all of the heavy lifting with recursion. It calls itself if the class is a collection type to enumerate through it again to get to the bottom most node. Also notice I have to use an instance variable here called _tmpArray to store all of the values while we still enumerate through other possible collections in the dictionary. Since we’re going to try to add this to a category of NSDictionary, we’ll have to use an associated reference variable as _tmpArray in our category since categories cannot have instance variables and we shouldn’t be extending NSDictionary.

Enter Associated References

Associated references are part of the Objective-c runtime that allow you to have property like functionality to a category. Lets take a look at the same bit of code but this time in a category for NSDictionary.

#import <foundation/Foundation.h>
 
@interface NSDictionary (Flatten)
 
- (NSMutableArray *)flattenedArray;
 
@end

 

#import "NSDictionary+Flatten.h"
#import <objc/runtime.h>
 
static char flattenedArrayKey;
 
@interface NSDictionary (FlattenPrivate)
 
- (void)flattenWithRecursion:(id)object;
- (void)enumerate:(id)object depth:(int)depth parent:(id)parent;
 
@end
 
 
@implementation NSDictionary (Flatten)
 
- (NSMutableArray *)flattenedArray {
	NSMutableArray *initArray = [[NSMutableArray alloc] init];
	objc_setAssociatedObject( self, &flattenedArrayKey, initArray, OBJC_ASSOCIATION_RETAIN );
	[self flattenWithRecursion:self];
	return (NSMutableArray *)objc_getAssociatedObject( self, &flattenedArrayKey );
}
 
- (void)flattenWithRecursion:(id)object {
	[self enumerate:object depth:0 parent:nil];
}
 
- (void)conquer:(id)object depth:(int)depth parent:(id)parent {
 
	if( [object isKindOfClass:[NSDictionary class]] ) {
 
		for( NSString * key in [object allKeys] ) {
			id child = [object objectForKey:key];
			[self enumerate:child depth:depth+1 parent:object];
		}
 
 
	} else if( [object isKindOfClass:[NSArray class]] ) {
 
		for( id child in object ) {
			[self enumerate:child depth:depth+1 parent:object];
		}
 
	} else if( [object isKindOfClass:[NSSet class]] ) {
 
		for( id child in object ) {
			[self enumerate:child depth:depth+1 parent:object];
		}
	}
	else{
		//	not a collection/container it has ended
		//NSLog(@"Node: %@  depth: %d",[object description],depth);
		NSMutableArray *assocObject = (NSMutableArray *)objc_getAssociatedObject(self, &flattenedArrayKey);
		[assocObject addObject:object];
		objc_setAssociatedObject( self, &flattenedArrayKey, assocObject, OBJC_ASSOCIATION_RETAIN );
	}
}
 
@end

Here are a few things that are worth noting:

  1. You have to import the Objective-C runtime
  2. We have to create a static var of type char as that’s what the runtime uses to keep a track of the associated reference.
  3. The Objective-C runtime is written in C, so you’ll see the C method call to objc_setAssociatedObject() and objc_getAssociatedObject()
  4. We have to use the OBJC_ASSOCIATION_RETAIN for the object behavior

It can get confusing

I’d like to show you a sample of using an associated reference that could look confusing if you’re not used to it.

NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"foo", @"key", nil];
NSMutableArray *myArray = [[NSMutableArray alloc] init];
obj_setAssociatedObject( myDictionary, &myRef, myArray, OBJC_ASSOCIATION_RETAIN );

As you can see, the category implementation is a little cleaner because we use self but this can be done inline anywhere.

That’s it!. Full category source code is available on Github.

Map in Objective-C

Learning new languages is great because they can force you to think in other ways. I was recently introduced to map, a functional programming technique that applies a given function to each element of a list, returning a list of results.  I’ll let you check out the formal Wikipedia explanation of it. To clarify, map shouldn’t be confused with NSArray – (void)makeObjectsPerformSelector: as they two very different things. Lets take a look at an example of map:

NSArray *numbers = @[ @1, @2, @3, @4, @5];
[multipliedNumbers addObjectsFromArray [numbers map:^id(id object) {
    return object * 2;
}]];
 
// multipliedNumbers now contains [2, 4, 6, 8, 10];

Okay thanks for the academic explanation, what about a more real world example?

[fullNameArray addObjectsFromArray:[peopleArray map:^id(id object) {
    Person *person = (Person *)object;
    return [NSString stringWithFormat:@"%@ %@", person.firstName, person.lastName];
}]];

Another example using core data, combining two results.

NSArray *fetchedFriends = [myManagedObjectContext executeFetchRequest:friendsFetchRequest error:nil];
NSArray *fetchedEnemies = [myManagedObjectContext executeFetchRequest:enemyFetchRequest error:nil];
 
[frenemies addObjectsFromArray:[fetchedFriends map:^id(id object) {
    FriendEntity *friendEntity = (FriendEntity *)object;
    Frenemy *myFrenemy = [[Frenemy alloc] init];
    myFrenemy.fullName = friendEntity.firstName;
    myFrenemy.lastName = friendEntity.lastName;
    return myFrenemy;
}]];
 
[frenemies addObjectsFromArray:[fetchedEnemies map:^id(id object) {
    EnemyEntity *enemyEntity = (EnemyEntity *)object;
    Frenemy *myFrenemy = [[Frenemy alloc] init];
    myFrenemy.fullName = enemyEntity.firstName;
    myFrenemy.lastName = enemyEntity.lastName;
    return myFrenemy;
}]];

NSArray currently doesn’t have map, so we’ll have to implement it using a category.

#import "Foundation/Foundation.h"
 
typedef id(^MapBlock)(id object);
 
@interface NSArray (FP)
 
- (NSArray *)map:(MapBlock)block;
 
@end

As you can see we first have to define the actual block as a typedef. A block in Objective-C is essentially just a C function. We give it the name “MapBlock”.

#import "NSArray+FP.h"
 
@implementation NSArray (FP)
 
- (NSArray *)map:(MapBlock)block {
    NSMutableArray *resultArray = [[NSMutableArray alloc] init];
    for( id obj in self ) {
        [resultArray addObject:block(obj)];
    }
    return resultArray;
}

You can download the source on Github.