I'm trying to integrate typhoon into my project.
I have three classes, DBManager, ProductDataManager, and CustomerDataManager. Both ProductDataManager and CustomerDataManager depend on DBManager for db operation.
@interface ProductDataManager ()
@property (nonatomic, strong) DBManager *dbManager;
@end
@implementation ProductDataManager
- (instancetype)initWithDBManager:(DBManager*)dbManager
{
self = [super init];
if (self) {
_dbManager = dbManager;
}
return self;
}
@interface CustomerDataManager ()
@property (nonatomic, strong) DBManager *dbManager;
@end
@implementation CustomerDataManager
- (instancetype)initWithDBManager:(DBManager*)dbManager
{
self = [super init];
if (self) {
_dbManager = dbManager;
}
return self;
}
Now I create three assemblies for them.
@interface DBManagerAssembly ()
@end
@implementation DBManagerAssembly
- (DBManager *)dbManager {
return [TyphoonDefinition withClass:[DBManager class] configuration:^(TyphoonDefinition *definition) {
}];
definition.scope = TyphoonScopeSingleton;
}];
}
@interface CustomerDataManagerAssembly ()
@property (nonatomic, strong) DBManagerAssembly *dbManagerAssembly;
@end
@implementation CustomerDataManagerAssembly
- (CustomerDataManager *)customerDataManager {
return [TyphoonDefinition withClass:[CustomerDataManager class] configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(initWithDBManager:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:self.dbManager];
}];
}];
}
- (DBManager *)dbManager {
return [self.dbManagerAssembly dbManager];
}
@interface ProductDataManagerAssembly ()
@property (nonatomic, strong) DBManagerAssembly *dbManagerAssembly;
@end
@implementation ProductDataManagerAssembly
- (ProductDataManager *)productDataManager {
return [TyphoonDefinition withClass:[ProductDataManager class] configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(initWithDBManager:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:self.dbManager];
}];
}];
}
- (DBManager *)dbManager {
return [self.dbManagerAssembly dbManager];
}
Activate them in AppDelegate
- (NSArray *)initialAssemblies
{
return @[[DBManagerAssembly class], [ProductDataManagerAssembly class], [CustomerDataManagerAssembly class]];
}
But on app startup, it throws an exception
[NSException raise:NSInvalidArgumentException format:@"Key '%@' is already registered.", _definition.key];
My question is... how to reuse assembly properly and avoid exception?
Typhoon allows you to create logical groupings of the key architectural actors in your system. This feature is called Assembly Modularization. All of the components defined go into a single TyphoonComponentFactory
- which contains recipes for emitting built instances.
Before activation the assembly returns definitions. At runtime you can use the assembly as a facade to return built instances. It uses Objective-C message forwarding to resolve method calls to [factory componentForKey:theMethodName]
. Therefore:
In your code, you've defined:
- (DBManager *)dbManager {
return [self.dbManagerAssembly dbManager];
}
Remove this definition, and edit customerDataManager
to instead have:
[initializer injectParameterWith:[self.dbManagerAssembly dbManager]];
All will be well.