/spring-objective-c

A Spring-like dependency injection container for Objective-C. Light-weight, yet full-featured.

Primary LanguageObjective-CApache License 2.0Apache-2.0

Description

A Spring-like dependency injection container for Objective-C and Cocoa. Light-weight, yet full-featured. Built during a typhoon.

Status? It's ready to use!

What is Dependency Injection?

Many people have trouble getting the hang of dependency injection, at first. And I think part of the problem is that it is actually so simple that we're inclined to look for something more complicated. "Surely that there has to be more to it?!", so to say.

So, with that in mind, imagine that you're writing an app that gives weather reports. You need a cloud-service (excuse the pun ;) ) to provide the data, and at first you go for a free weather report provider, but in future you'd like to integrate a weather service with better accuracy and more features. So, as do all good object-oriented developers, you make a WeatherClient protocol and back it initially with an implementation based on the free, online data provider.

Without dependency injection, you might have a View Controller like this:

-(id) init 
{
 self = [super init];
 if (self) 
 {
 //The class using some collaborating class builds its own assistant.
 //it might be one of several classes using the weatherClient. 
  _weatherClient = [GoogleWeatherClientImpl alloc] initWithParameters:xyz];
 }
 return self;
}

The thing with this approach is, if you wanted to change to another weather client implementation you'd have to go and find all the places in your code that use the old one, and move them over to the new one. Each time, making sure to pass in the correct initialization parameters.

A very common approach is to have a centrally configured singleton:

_weatherClient = [GoogleWeatherClient sharedInstance];

With either of the above approaches, in order to test your view controller, you now have to test its collaborating class (the weather client) at the same time, and this can get tricky, especially as your application gets more complex. Imagine testing Class A, depends on Class B, depends on Class C, depends on .... Not much fun!

So with dependency injection, rather than having objects make their own collaborators, we have them supplied to the class instance via an initializer or property setter.

And now, it simply becomes:

-(id) initWithWeatherClient:(id<WeatherClient>)weatherClient
{
 self = [super init];
 if (self) 
 {
     _weatherClient = weatherClient;
 }
 return self;
}

####Is that all they mean by 'injected'? Yes it is. And if you do this with significant collaborators throughout your app, it means that the GoogleWeatherClientImpl is now declared in a single place - the top-level assembly, so-to-speak. And all of the classes that need to use some kind of id<WeatherClient> will have it passed in. This means that:

  • If you want to change from one implementation to another, you need only change a single declaration.
  • Classes are easier to test, because we can supply simple mocks and stubs in place of concrete collaborators. Or the real collaborators, but configured to be used in a test scenario. (One of my design goals).
  • It promotes separation of concerns and a clear contract between classes.
  • Your app is easier to maintain and can accommodate new requirements.

Why Spring for Objective-C?

Well. . . it's not necessarily Spring, the well-known DI framework for Java, .NET and ActionScript. . . Just my personal take on what Dependency Injection should look like in Objective-C. I guess that I could've called it 'The Typhoon Framework'. Or Michael? I don't work at SpringSource (I did once) and I'm not getting any financial benefit from this. I'm doing it because I think it is the right thing to do.

Your Dependency Injection Options

If you proceed with the Dependency Injection pattern (assuming you're not one of the remaining "flat-earthers", who believe that Objective-C somehow magically alleviates the need for common-sense: "Oh, I don't do DI, I use swizzling class-clusters!"), then there are basically two options:

  • You can do dependency injection without a framework/library/container to help you. It is simple after all, and in fact I recommend you do this, at least as an exercise in software design. And yes, it is certainly possble that this will be adequate. But, I think its good to have help, if you can get it. You can also write tests without a test framework, mocks with out a mock library, software without a compiler.

  • So, going down the library/framework route, there's been quite a lot of action in Objective-C land, over the last three years. In fact, there are now around 15 Dependency Injection frameworks, many following in the footsteps of Google Guice. The authors have done a great job (Objection is especially good). However, I wanted an approach that allows the following:

Design Goals / Features

  • Dependencies declared in any order. (The order that makes sense to humans).

  • Allows both dependency injection (injection of classes defined in the DI context) as well as configuration management (values that get converted to the required type at runtime). Because this allows. . .

  • . . . ability to configure components for use in eg Test vs Production scenarios. This faciliates a good compromise between integration testing and pure unit testing. (Biggest testing bang-for-your-buck).

  • Application assembly - the wiring of dependencies and configuration management - is all encapsulated in a convenient document. Now you know where to look if you need to change something.

  • Non-invasive.

  • Encourages polymorphism and makes it easy to have multiple implementations of the same base-class or protocol. Supports both auto-wiring by type and wiring-by-reference.

  • Supports both initializer and property injection. In the case of the latter, it has customizable call-backs to ensure that the class is in the required state before and after properties are set.

  • Flexibility. Supports different approaches of dependency injection for different scenarios, including "annotations" and GUI tool-support.

  • Lean. It has a very low footprint, so is appropriate for CPU and memory constrained devices.

So, does this mean XML?

  • If you're interested in reading an architectural discussion, especially why xml is a good choice to declare components, their collaborating classes and their configurations, as well as what other styles of dependency injection this cotainer will be supporting: then please read this.

otherwise . . .

Usage

And then:

Reports

In the spirit of lean-methodologies, the API and Test Coverage reports below are published by my build server, after each commit. (If you'd like the script I will share it).

Feature Requests and Contributions

. . . are very welcome.

Frequently Asked Questions

. . . are here.

Who's using it?

  • Mod Productions : Two very exciting applications currently in development. Stay tuned for release.

If you're using it, please shoot me an email and let me know.

Authors

With contributions from:

  • Jeffrey Roberts, Mobile Software Engineer at Riot Games, previous contributor to Swiz for ActionScript : Advice, feedback and testing.
  • John Blanco of Rapture in Venice, LLC : contributed his lean and elegant XML library. A great example that full-featured is not the same as heavy.
  • Jetbrains, maker of very cool software development tools : Assistance with AppCode integration.
  • Your name here!!!!!!!

Thanks!!!

LICENSE

Apache License, Version 2.0, January 2004, http://www.apache.org/licenses/

  • © 2012 - 2013 jasper blues