Wednesday, December 29, 2010

OxenIoC: an IoC container for Objective C

Last year I've been working on two applications for IPhone with several views and core data entities, a lot of classes that really are hard to maintain. These projects were started on september of 2009 by other company. In 2010 we received this legacy and we are still adding features and new versions on the store.
I know that iPhone applications are not really big applications but we think that an IoC Container can help to do applications like these a little more maintainable. This is the reason because we decided make a proof of concept with an IoC container. Now we going to explain what we did.

The main concern is how we are going to specify the objects for the container. We took two approaches, the first one consist in specifying the objects from code and the second one in reading the context from XML.

Specifying context from code

For this approach we have the class IoCObjectDefinition wich can specify, a name, a class name, if it's singleton, if it's lazy and the references.

Here's an example:

- (IoCContainer *) buildContainerFromCode {
  IoCContainer *container = [[IoCContainer alloc] init];

  IoCObjectDefinition *serviceDefinition = [[IoCObjectDefinition alloc] init];
  serviceDefinition.name = @"theService";
  serviceDefinition.className = @"ExampleServiceImpl1";
  serviceDefinition.lazy = NO;
  [container addDefinition:serviceDefinition];
  [serviceDefinition release];

  IoCObjectDefinition *serviceExecutorDef = [[IoCObjectDefinition alloc] init];
  serviceExecutorDef.name = @"theExecutor";
  serviceExecutorDef.className = @"ServiceExecutorImpl1";

  // inject theService to serviceExecutor
  [serviceExecutorDef addPropertyReference:@"service" toObjectName:@"theService"];

  [container addDefinition:serviceExecutorDef];
  [serviceExecutorDef release];

  return [container autorelease];
}



Specifying references from code

Also you can specify the injection of objects in the class code. The macro called IoCContainerInject(property,objectName)


provides such functionality. Here's an example,

@implementation ServiceExecutorImpl2
  @synthesize service;

  //Inject theService object to service property
  IoCContainerInject(service, theService)

  - (void) executeService {
    [service execute];
  }
@end



Specifying objects from an XML

Also you can specify the same context by XML. Here's the example,

<contanier>  

  <object id="theService" class="ExampleServiceImpl2" lazy="false"/>  

  <object id="theExecutor" class="ServiceExecutorImpl1">
    <property name="service" ref="theService"/>  
  </object>

</contanier>




Getting objects from container

We have the class IoCContainer. This class have the method - (id) getObject: (NSString*) name;

Here's an example:

id<ServiceExecutor> executorFromCode = [containerFromCode getObject:@"theExecutor"];

Application example

We uploaded an app example in order to show these concepts.

This application has 3 contexts, one built from code other built from an XML and the last one built from code but inject the reference by the macro.

This app has 2 protocols,

  1. ExampleService with a method named execute.
  2. ServiceExecutor with a method named executeService. ServiceExecutors have an ExampleService.

There are 2 implementations for each of these protocols.


  • ExampleServiceImpl1 the execute method write in log Service Implementation 1.
  • ExampleServiceImpl2 the execute method write in log Service Implementation 2.
  • ServiceExecutorImpl1 is a class with a property of kind id.
  • ServiceExecutorImpl2 is a class with a property of kind id that specifies the injection of the object called "theService".

OxenIoCAppDelegate builds the 3 contexts wiring those objects in different ways, then it gets the 3 serviceExecutors and executes them.


What's next

We will try to find out how to reduce the code to write for adding objects to container. We think that conventions and default values could help.

1 comment:

  1. Interesting,

    Container based IoC seems to be trending downward. Because it requires a large upfront container that can be easily botched and hard to maintain over time. It is, for sure, better than the NIB version of dependency injection.

    However, I think "annotation" based DI frameworks are the way to go. Ala, Guice or a lightweight version of it for Objective-C called "Objection".

    ReplyDelete