Blocks to the rescue once more

I am currently rewriting the network layer that synchronizes a Core Data database in our iOS app with a variety of web services which I recently placed behind a RESTful facade. Now I can throw away all the nasty SOAP and XML/RPC code and rewrite a nice clean JSON based synchronization framework. Or so I thought… It turns out that switching to JSON came with its own set of problems. One of them caused a not so DRY and smelly repeating pattern of code that started to annoy me to no end, so I had to do something about it. The problem is how JSON (or more likely my server side Jersey/JAXB implementation of it) handles arrays with a single element in them. The behavior I get is this:

  • empty lists become null
  • lists with one element get represented as just that element, without [ ] enclosure!
  • lists with multiple elements behave normally and become a JSON array

So after JSONKit (an awesome and blazing fast JSON parser/encoder) parses the JSON to the usual NSArray and NSDictionary objects, I find myself writing something like this over and over again:

id customers = [data objectForKey:@"customers"];
if ([customers isKindOfClass: [NSDictionary class]]) {
    [self syncCustomer: customers];
} else {
    for (NSDictionary *customer in customers) {
        [self processCustomer: customer];
    }
}

Same for reports, topics, subscriptions, issues, etc. you get the idea. Every time we process an array of records we have to check if it happens to be an array of size one in which case it is not an array at all… Not very nice! I don’t know who thought this was a nice “optimization”, but they were wrong!

What if we could just write what we want to do with each record in the array and pass it along to something that would apply it to either the single record or all of them if there are more? This is where blocks come to the rescue once again.

Here is an example code snippet using the solution I came up with:

[self withJSON: [jsonData objectForKey:@"comments"]
            do: ^(NSDictionary *comment) {
    NSString *commentId = [comment objectForKey:@"id"];
   // etc., sync comment with CoreData entity
}];

No more repeated logic to test for one or more elements in the NSArray, this is now all hidden in the withJSON:do: method, which accepts an objective-C block to apply to each record whether they appear in an array or solo. This method looks like this:

-(void)withJSON: (id)records
             do: (void (^)(NSDictionary *))block {
    if (!records) return;
    if ([records isKindOfClass:[NSArray class]]) {
        for (id record in records) {
            [self withJSON:record do:block];
        }
    } else  if ([records isKindOfClass:[NSDictionary class]]) {
        block(records);
    }
}

If records is nil, it does nothing and simply returns, if records is an NSArray it iterates over it and recurses for each element in the array, and finally if records is a NSDictionary it invokes the block with the dictionary as a parameter. The nice thing about using recursion here is that we don’t have to repeat the test for “NSDictionary-ness” in the loop and we get support for nested arrays for free!

So, once again blocks have saved my sensitive nose from another nasty code smell!

Alternative implementation using categories

I just thought of (and tested) another way to implement this using a category on NSArray and NSDictionary. Each of these implements the same method jsonDo:.

The NSDictionary implementation simply invokes the block passing itself as the parameter, and the NSArray implementation invokes jsonDo: on each of its elements:


@implementation NSObject (JSONParsing)
-(void)jsonDo: (void (^)(NSDictionary *))block {
   // do NOTHING
}
@end

@implementation NSDictionary (JSONParsing)

-(void)jsonDo: (void (^)(NSDictionary *))block {
    block(self);
}
@end

@implementation NSArray (JSONParsing)

-(void)jsonDo: (void (^)(NSDictionary *))block {
    for (id next in self) {
        [next jsonDo:block];
    }
}
@end

Why the empty implementation in NSObject? If we did not have that, our code would crash when we receive an array with things other than NSDictionary elements, like NSString or NSNumber, scenarios for which this code was not designed.

The same example used above now looks like this using the alternative implementation:


    [[jsonData objectForKey:@"comments"]  jsonDo:^(NSDictionary *comment) {
        NSString *commentId = [comment objectForKey:@"id"];
    // etc. sync with Core Data entity
    }];

And once again, objective-C blocks and categories turn out to be a pleasant perfume eliminating some pretty awful code smells!

Posted in CocoaTouch | Tagged , , , , | 3 Comments

Categories and Blocks; a winning combination

The app I am working on uses Core Data and when it first launches it passes the NSManagedObjectContext around and many view controllers retain a reference to it. Injecting the context this way is in my opinion a better pattern than creating dependencies everywhere to the app delegate in order to obtain the context. This pattern has already paid off in testability since I could now write unit tests that pass a different context for an in-memory store as opposed to the actual persistent store with the real data.

The problem I ran into recently is that this pattern was OK as long as the core data store remains valid for the entire lifetime (session) of the application. However, a new logout (change user) feature forced me to delete the entire core data store and I had to somehow find a way to refresh all the context objects that were now spread all over the place…not pretty. My thought was that NSNotificationCenter and some creative use of categories would be a nice solution. Here is what I initially came up with:

When the logout gets triggered I invoke this:

- (void) resetCoreDataStack {
    [managedObjectContext_ release]; managedObjectContext_ = nil;
    [managedObjectModel_ release]; managedObjectModel_ = nil;
    [persistentStoreCoordinator_ release]; persistentStoreCoordinator_ = nil;
    [[NSNotificationCenter defaultCenter] postNotificationName: CoreDataResetEventName
                                                        object:[UIApplication sharedApplication]
                                                      userInfo:[NSDictionary dictionaryWithObject:self.managedObjectContext
                                                                                           forKey:@"context"]];
}

this will reset all CoreData related state and post a notification broadcasting the new context. Note that the managedObjectContext accessor lazily initializes the new context and this is also where the file gets deleted if no credentials are found in the keychain. Next challenge was to make it as easy as possible to handle this notification elsewhere in the code – everywhere a NSManagedObjectContext is retained. This is where the category came in. The signature I wanted to support was this:

#define CoreDataResetEventName @"ResetCoreData"

@interface UIViewController (CoreDataReset)

/*
 * aSelector must accept a NSManagedObjectContext as it single argument, e.g. a property setter
 */
-(void)registerForCoreDataResetEvent: (SEL)aSelector;

@end

The problem here is that it would have to retain the selector so it can invoke it with the new object context when the notification is received… But categories can not add any state and so this seemed to be a doomed approach….. Until I found an alternative method to add observers to the NSNotificationCenter that uses a block to handle the notification:
addObserverForName:object:queue:usingBlock:

Since blocks are true closures it is possible to use the selector passed into the register method in the block that handles the notification, like so:

-(void)registerForCoreDataResetEvent: (SEL)aSelector {
    [[NSNotificationCenter defaultCenter]
        addObserverForName: CoreDataResetEventName
                    object:[UIApplication sharedApplication]
                     queue:nil
                usingBlock:^(NSNotification *n) {
                  [self performSelector:aSelector withObject:[n.userInfo objectForKey:@"context"]];
                }
    ];
}

And this indeed works like a charm! Now all you have to do is something like this (assuming you have a @property named context that holds the NSManagedObjectContext:


@synthesize context;

- (id)initWithContext: (NSManagedObjectContext *)aContext {
    self = [super init];
    if (self) {
        self.context = aContext;
        [self registerForCoreDataResetEvent:@selector(setContext:)];
    }
    return self;
}

This is truly a winning combination of blocks and categories and this solution would not have been nearly as odorless without them!

Posted in CocoaTouch | Tagged , , , | 2 Comments

UIColor from integers instead of floats

One of the things you will find yourself writing over and over again when putting the finishing touches on your iPhone apps is the conversion of integer RGB numbers obtained from any number of tools or the web to CGFloat values between 0 and 1 for use with the standard UIColor API. Luckily Objective-C has the concept of categories which allows for a perfectly DRY solution to this highly repetitive nuisance:

#import "UIColor+NormalizedRGB.h"

@implementation UIColor (NormalizedRGB)

+(UIColor *) colorWithRedInt: (NSInteger)red greenInt: (NSInteger)green blueInt: (NSInteger)blue alpha: (CGFloat) alpha {
	return [self colorWithRed:red / 256.0 green:green / 256.0 blue:blue / 256.0 alpha:alpha];
}

@end

Note that I kept the alpha a CGFloat because normally you don’t get this number anyways when you are tinkering with colors and it feels more natural to think of a percentage when talking about opacity.

Of course you’ll have to publish this in a header file as well, which is trivial:

@interface UIColor (NormalizedRGB)

+(UIColor *) colorWithRedInt: (NSInteger)red greenInt: (NSInteger)green blueInt: (NSInteger)blue alpha: (CGFloat) alpha ;

@end

Now when you need to create colors you can simply invoke the above method:

	NSArray *colors = [NSArray arrayWithObjects:
					   [UIColor colorWithRedInt:30 greenInt:37 blueInt:111 alpha:1.0],		// dark blue
					   [UIColor colorWithRedInt:111 greenInt:143 blueInt:190 alpha:1.0],	// light blue
					   [UIColor colorWithRedInt:176 greenInt:47 blueInt:28 alpha:1.0],		// red-brown
					   [UIColor colorWithRedInt:40 greenInt:72 blueInt:52 alpha:1.0],		// dark-green
					   [UIColor colorWithRedInt:89 greenInt:177 blueInt:134 alpha:1.0],		// bluegreen
					   [UIColor colorWithRedInt:158 greenInt:151 blueInt:138 alpha:1.0],	// gray
					   nil];
Posted in CocoaTouch | Tagged , , , | Comments Off