iosdependency-injectiontyphoon

Reuse assembly in other assemblies


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?


Solution

  • 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.

    assembly modules

    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.