Wednesday, February 23, 2011

Repeat after me: enumerations should be type-safe!

We're doing some iPhone code maintenance at Oxen. It is a legacy application, created by another company.

As you may know, iPhone applications are built using Objective C and the Apple platform, Cocoa.

The customer requested me fixing an error that was related with the device orientation. I never have had to deal before with orientation handling, so I started looking at the code:

UIInterfaceOrientation currentOrientation = [[UIDevice currentDevice] orientation];




The UIInterfaceOrientation enumeration has 4 different values:

typedef enum {
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
} UIInterfaceOrientation;




Objective C treats enumeration as integers. The problem was that, when the device was face up, the orientation returned by UIDevice was 5. This didn't seem a valid value.

After hour struggling with this issue I realized that the enumeration wasn't correct. The correct one was UIDeviceOrientation:

typedef enum {
    UIDeviceOrientationUnknown,
    UIDeviceOrientationPortrait,            // Device oriented vertically, home button on the bottom
    UIDeviceOrientationPortraitUpsideDown,  // Device oriented vertically, home button on the top
    UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right
    UIDeviceOrientationLandscapeRight,      // Device oriented horizontally, home button on the left
    UIDeviceOrientationFaceUp,              // Device oriented flat, face up
    UIDeviceOrientationFaceDown             // Device oriented flat, face down
} UIDeviceOrientation;




which included all the possible values.

Some code used this enumeration, so I discovered it accidentally (not reading the documentation was my responsibility). At first time I thought I was crazy: I didn't noticed that there were two different enumerations. Magically, the missing options appeared. Finally I realized about the error and I felt better about my mental health.

My point here is that type-unsafe enumerations (such as the ones provided by Objective C) are very error prone. Java guys solved this problem with enums in Java 5 release. It would be nice to have something similar in Objective C. A silly error like this can make you loss valuable time.

Yes, I know. This is not the best example, because the first enumeration was defined using the second one, so the values were interchangeable. But it can be confusing when you're fixing bugs at 2:00 PM :)

Tuesday, February 15, 2011

OxenIoC: IoC container for Objective C.Part 2

We have improved our OxenIoC container. We have fixed one bug, the only one that we detected, and we have added some new features that help to write less code to define objects into container and their dependencies.

The main idea is to use a mechanism like java annotations or c# attributes. We developed some preprocessors macros for this.

The preprocessors macros defined are:
  • IoCName(name): Defines a name for the object. If you don't use this "annotation" then the object will be named like the class name.
  • IoCLazy: Defines if it is Lazy.
  • IoCSingleton: Defines if the object is Singleton.
  • IoCInject(property, objectName): Inject the object whose name is "objectName" into the property named "property".
Example:
#import "ServiceExecutorImpl3.h"
#import "IoCContainer.h"
@implementation ServiceExecutorImpl3
@synthesize service;
IoCName(ServiceExecutor3)
IoCLazy IoCSingleton
IoCInject(service, theService)
- (void) executeService {
     [service execute];
}
@end 
This example defines an object named ServiceExecutor3, lazy, singleton and injects into property "service" the object named "theService".

For getting the instance of this class we just call the getObject method

id executor = [container getObject:@"ServiceExecutor3"];

So, this is a third way to defines objects into container. The others two are by instancing a ObjectDefinition object and by xml.

To get the code click here

Part1 of this post click here