A Dependency Injection (DI) framework for iOS written in Objective-C
Objective-C
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
InDependence.xcodeproj
InDependence
Tests
docs/img
.gitignore
LICENSE
README.md

README.md

InDependence

InDependence is a Dependency Injection (DI) framework for iOS written in Objective-C. Key features are:

  • Support for child-to-parent and other object-to-relatives references. It makes integration of DI to old legacy code much easier.
  • Extendable design of framework encourages writing custom extensions for adapting InDependence to own needs.

Project status

Álpha. Public interface may change.

Features

  • "Annotation" based Dependency Injection
  • Binding system
    • Class bindings
    • Protocol bindings
  • Bindings may be grouped into modules
  • Child-to-parent references
  • Object-to-relatives references
  • Initializer support
    • Default and custom arguments
  • Singleton annotations
  • Objects are notified when injection is complete
  • Extensions for adding own features

How to use InDependence

Basic Usage

The independence_requirements macros used to declare what dependencies InDependence should inject to all instances it creates of that class. independence_requirements can be used safely with inheritance.

@class Wheel;

@interface Chassis : NSObject
// Will be injected by InDependence
@property(nonatomic,strong) Wheel *wheel;
@end

@interface Car : NSObject
// Will be injected by InDependence
@property(nonatomic,strong) Chassis *chassis;
// Will be injected by InDependence
@property(nonatomic,strong) Engine *engine;
@end

@implementation Chassis
independence_requirements(@"wheel");
@end

@implementation Car
independence_requirements(@"chassis",@"wheel");
@end

Fetching objects from injector

An object can be fetched by asking injector for an instance of a particular class or protocol. An injector manages its own context. It means that a singleton is per injector and is not necessarily a true singleton.

- (void)someMethod {
    INDInjector *injector = [INDInjector sharedInjector];
    Car *car = [injector getObject:[Car class] 
                            parent:nil]; // Car is root object
}

Shared injector may be changed at any time:

INDInjector newInjector = [INDInjector new];
[INDInjector setSharedInjector:newInjector];

References

InDependence can automatically fill in references to parents, ancestors and any other relatives. References must be declared as weak.

@interface Wheel()
// Will be filled in by InDependence
@property(nonatomic,weak) Chassis *chassis;
// Will be filled in by InDependence
@property(nonatomic,weak) Car *car;
@end

@interface Engine()
// Will be filled in by InDependence
@property(nonatomic,weak) Wheel *wheel;
@end

@implementation Wheel
independence_references(@"chassis",@"car");
@end

@implementation Engine
independence_references(@"wheel");
// ...
@end

Awaking from Injector

If an object is interested in knowing when it has been fully instantiated by InDependence it can implement the method awakeFromInjector.

@implementation Car
// ...
-(void)awakeFromInjector{
    NSLog(@"%@ awaked",self);
}
@end  

Modules

A module is a set of bindings which contributes additional configuration information to the injector. It is especially useful for integrating external dependencies and binding protocols to classes or instances.

Class bindings

@interface MyModule : INDModule
@end

@implementation MyModule
- (void)configure {
    [self bindClass:[UnibodyChassis class] toClass:[Chassis class]];
    [self bindClass:[HybridEngine class] toClass:[Engine class]];
}
@end
[[INDInjector sharedInjector] addModule:[MyModule new]];
Car *car = [[INDInjector sharedInjector] getObject:[Car class]
                                            parent:nil];
NSLog(@"%@",NSStringFromClass([car.chassis class])); // UnibodyChassis
NSLog(@"%@",NSStringFromClass([car.engine class])); // HybridEngine

Recursive bindings

There may be binding rules for already binded classes (for example, in another module). InDependence finds appropriate class by recursion.

-(void)configure{
    [self.injector bindClass:[Ford class] toClass:[Car class]];
    [self.injector bindClass:[FordFocus class] toClass:[Ford class]];
}
// ...
Car *car = [[InDependenceInjector sharedInjector] getObject:[Car class]];
NSLog(NSStringFromClass([car class])); // FordFocus

Initializers

By default, InDependence allocates objects with the default initializer init. If you'd like to instantiate an object with an alternate initializer the independence_initializer macros can be used to do so. The macros supports passing in default arguments (scalar values are not supported).

@interface ColoredCar : Car
-(id)initWithColor:(NSString *)color;
@property(nonatomic,copy) NSString *color;
@end

@implementation ColoredCar
independence_initializer(initWithColor:,@"Black");
-(id)initWithColor:(NSString *)color{
    if (!(self = [super init])) return self;
    _color = color;
    return self;
}
@end
ColoredCar *carWithDefaultColor = 
    [[INDInjector sharedInjector] getObject:[ColoredCar class] 
                                     parent:nil];
NSLog(@"%@",carWithDefaultColor.color); // Black

ColoredCar *carWithCustomColor = 
    [[INDInjector sharedInjector] getObject:[ColoredCar class] 
                                     parent:nil 
                                  arguments:@"Red",nil];
NSLog(@"%@",carWithCustomColor.color); // Red

Singletons

Any class may be annotated as a singleton using independence_singleton() macros.

@interface Road : NSObject
@end
@implementation Road
independence_singleton();
@end
@interface Car()
@property(nonatomic,strong) Road *road;
@end

@implementation Car
independence_requirements(@"chassis",@"wheel",@"road");
// …
@end
Car *car = [[INDInjector sharedInjector] getObject:[Car class] parent:nil];
Car *anotherCar = [[INDInjector sharedInjector] getObject:[Car class] parent:nil];
NSLog(@"%d",[car.road isEqual:anotherCar.road]); // 1

Installation

Add sources from InDependence folder to your project. Enable ARC for them. Add InDependence.h to your project's .pch file:

#import "InDependence.h"

Requirements

  • iOS 5.0 and later
  • ARC

License

MIT License.

TODO

  • Better README
    • Objects Tree and correct parent specifying
    • Protocol bindings
    • Binded arguments
    • Inline modules
    • Extensions
  • Bindings
    • Instance bindings
    • Custom factory blocks
    • Custom post-init blocks
  • References
    • Dynamically provided references

Acknowledgments

Some code and ideas derived from Objection by Justin DeWind.