TBCacao is a small and simple Cocoa framework for dependency injection.
- OS X:
pod 'TBCacao' - iOS:
pod 'TBCacaoIOS'
My other project Qmind — a mind mapping app for OS X compatible with FreeMind — uses TBCacao quite extensively. Have a look for a real life example.
First, include TBCacao in your project by either adding it as a Framework or copying all TBCacao classes into your project. To initialize TBCacao you add the following line to main.m before you return:
[[TBContext sharedContext] initContext];Assume that we have the following two classes:
MyLayoutManagerMyDrawingManager
Assume further that you want to have MyLayoutManager automatically injected—autowired in the language of Spring—in MyDrawingManager. The two classes look as follows
@interface MyLayoutManager : NSObject <TBBean>
- (void)doLayout;
@end
@implementation MyLayoutManager
- (void)doLayout {
NSLog(@"doLayout");
}
@end@interface MyDrawManager : NSObject <TBBean>
@property (weak) MyLayoutManager *layoutManager;
- (void)doDraw;
@end
@implementation MyDrawManager
TB_AUTOWIRE(layoutManager)
- (void)doDraw {
[self.layoutManager doLayout];
}
@endThe protocol TBBean marks both classes as beans such that they are scanned by TBCacao. The line TB_AUTOWIRE(layoutManager) lets TBCacao automatically set the property layoutManager with an instance of MyLayoutManager.
Oftentimes you use singltons of Foundation, AppKit/UIKit or other 3rd party frameworks in your classes. For many purposes it is beneficial to have them injected, ie not call messages like [SomeManager sharedManager]. This can easily limit the unit-testability of your classes. Let's assume that you have MyManager and this class uses [NSNotificationCenter defaultCenter]. To inject the default notification center, you do the following
@interface MyManualBeanProvider : NSObject <TBManualBeanProvider>
@end
@implementation MyManualBeanProvider
+ (NSArray *)beanContainers {
static NSArray *manualBeans;
if (manualBeans == nil) {
manualBeans = @[
[TBBeanContainer beanContainerWithBean:[NSNotificationCenter defaultCenter]],
[TBBeanContainer beanContainerWithBean:[SomeOtherManager sharedManager]],
];
}
return manualBeans;
}
@end@interface MyManager: NSObject <TBBean>
@property (weak) NSNotificationCenter *notificationCenter;
@end
@implementation MyManager
TB_AUTOWIRE(notificationCenter)
@endNow, you can easily mock the notification center in your unit test for MyManager.
Since TBCacao automatically scans all classes implementing the protocol TBManualBeanProvider, you don't need an interface file; put both @interface and @implementation in MyManualBeanProvider.m.
TODO: Describe TB_MANUALWIRE.