When my application starts I fetch a remote config file containing information (URLs, etc) required to configure other dependencies.
After I fetch the remote config I have a Config
object that I need to supply to other TyphoonDefinition
s.
Now I am also using the plist storyboard integration.
I was originally going down the path of injecting the assembly into the ViewController that loads the Config object, and when I receive the remote config and create the Config object, I would somehow set it as a property on the assembly. I did this hoping that I could then use the property in the definitions, but this did not work and I got:
2014-10-22 21:18:06.203 four[39840:516543] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'No component matching id 'setConfig:'.'
*** First throw call stack:
(
0 CoreFoundation 0x000000010a3e63f5 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010a07fbb7 objc_exception_throw + 45
2 CoreFoundation 0x000000010a3e632d +[NSException raise:format:] + 205
3 four 0x00000001070a011d -[TyphoonComponentFactory componentForKey:args:] + 148
4 CoreFoundation 0x000000010a2de22c __invoking___ + 140
5 CoreFoundation 0x000000010a2de082 -[NSInvocation invoke] + 290
6 CoreFoundation 0x000000010a36d456 -[NSInvocation invokeWithTarget:] + 54
7 four 0x000000010709d358 -[TyphoonBlockComponentFactory forwardInvocation:] + 276
Is there any way for me to inject an object into an assembly at runtime?
Is there a cleaner way to do what I'm trying to do?
I was reading about run-time arguments which sounds like what I need, but I really don't understand the docs.
For example, I have this as a definition. I need to pass the runtime Config object as a parameter to the constructor.
- (id<ApiService>)apiService
{
return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {}];
}
Using runtime arguments
For example, I have this as a definition. I need to pass the runtime Config object as a parameter to the constructor.
- (id<ApiService>)apiService { return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {}]; }
Try something like this:
- (id<ApiService>)apiServiceWithConfig:(Config *)config {
return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {
// inject into property:
[definition injectProperty:@selector(config) with:config];
// inject into constructor:
[definition useInitializer:@selector(initWithConfig:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:config];
}];
}];
}
Using factory definition
Take a look into next two definitions:
- (Config *)currentConfig {
return [TyphoonDefinition withFactory:[self configManager] selector:@selector(currentConfig)];
}
- (ConfigManager *)configManager {
return [TyphoonDefinition withClass:[ConfigManager class] configuration:^(TyphoonDefinition *definition){
definition.scope = TyphoonScopeSingleton;
}];
}
Imagine you have a ConfigManager which downloads remote config and stores it as 'currentConfig' property, 'configManager' definition describes that object.
Then check the 'currentConfig' definition. This definition just returns result of calling 'currentConfig'method on ConfigManager instance.
Then you can inject config as:
- (id<ApiService>)apiService {
return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {
[definition injectProperty:@selector(config) with:[self currentConfig]];
}];
}
But make sure that currentConfig loaded (not nil) during 'apiService' creation. (maybe better inject ConfigManager instead - then if currentConfig nil, it would be filled later)