Dear reader,

This site finally got its much-needed overhaul. I tried to keep existing pages at their original URLs.
Should you find an error, please do tell me at contact@aspyct.org.
Feel free to visit the new website, which may contain more documentation pertaining to your search.

Thank you for visiting aspyct.org, I hope you'll find what you're looking for.

Old Aspyct.org blog

 or  go to the new site.

APServiceBox

What is it ?

APServiceBox is a dependency injection container that can be used in your iOS or other Objective-C projects. It can be compared in some way to containers such as Spring, but a much more simple one.

Use this box when you have services or objects that must be available throughout the application. For example, if all your *ViewControllers need to report analytics, or depend on a single data source.

APServiceBox is open-source (MIT licensed) and therefore free to use.

How to use

Grab the whole thing from its github repository or download the zipball, and drop the classes into your project.

Usage example

The first thing you should do is register your services and other dependencies into the default service box.

1
2
3
4
5
AnalyticsManager *analyticsManager = [[AnalyticsManager alloc] init];
[[APServiceBox defaultBox] registerDependency:analyticsManager as:@"analyticsManager"];

// Or you can create your own box
APServiceBox *myBox = [[APServiceBox alloc] init];

Self-injection

The easiest way to inject dependencies is to call the fillWithDependencies method in the init of an object. This will use the default service box to fill the current object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface MyViewController
@property (strong, nonatomic) AnalyticsService *analyticsService;
@end


#import "NSObject+APServiceBox.h"
@implementation MyViewController

- (id)init {
    self = [super init];

    if (self) {
        // This will ask the [APServiceBox defaultBox] to provide available dependencies
        [self fillWithDependencies];
    }

    return self;
}

@end

Injection conditions

There are two conditions for a dependency to be injected:

  1. the @property name corresponds to the name given when registering the service
  2. the @property type matches the registered service, that is (one of the following):
    • service.class == @property.class
    • [service isKindOfClass:@property.class]
    • [service.class conformsToProtocol:@property.protocol]

Thus, the following two forms of properties are valid:

1
2
3
4
5
// Will match any AnalyticsService (or subclass) object
@property (...) AnalyticsService *analyticsService;

// Will match any object that conforms to <AnalyticsService> protocol
@property (...) id<AnalyticsService> *analyticsService;

Inject from outside the class

1
2
3
4
5
6
7
8
9
10
11
12
13
// Create the box
APServiceBox *box = [APServiceBox defaultBox];

// Register some services in it, here a fictive AnalyticsService object
[box registerDependency:analyticsService as:@"analyticsService"];

// Use the box to inject dependencies into object's properties
// The dependencies will be injected based on names and classes/protocols
[box fill:myViewController];

// For example, if myViewController has a property defined as follow:
@property (...) AnalyticsService *analyticsService;
// then the analyticsService from above will be injected.

Inject the box itself

If you need to inject the box itself into one of the objects (typically a UIViewController that will create other UIViewControllers), you can simply add a @property of type APServiceBox and named “serviceBox”.

1
@property (strong, nonatomic) APServiceBox *serviceBox;

Cross-dependency injection

When you call fill: for the first time, APServiceBox will scan and fill each of the registered dependencies. This allows the following scenario:

1
2
3
@interface StorageManager

@end
1
2
3
4
5
@interface PreferenceManager

@property (strong, nonatomic) StorageManager *storageManager;

@end
1
2
3
4
5
6
7
8
9
APServiceBox *box = [[APServiceBox alloc] init];
[box registerDependency:preferenceManger as:@"preferenceManager"];
[box registerDependency:storageManger as:@"storageManager"];

// And if you invoke...
[box fill:myObject];

// ...then the following is true:
preferenceManager.storageManager == storageManager;

Need more?

If you need more insights on how this works, have a look at the test file: it’s short and clear! Or contact me ;)

Report bugs

You can either

License

Feel free to use this for commercial works, and I’d be glad to hear from you, or why not see my name in a “thanks” section :)

This code is available under the MIT license.

Hope you like this little utility. Have fun!