Skip to content

What can be Injected

jasperblues edited this page Dec 25, 2014 · 70 revisions

##A definition can be injected with . . .

#Other Definitions

The most common scenario is of course that a definition wishes to be injected with the built instance of another definition.

By Type

For properties, the Objective-C run-time provides type-introspection. Therefore, injection can be done by matching the required-type, as follows:

- (Knight *)knight
{
    return [TyphoonDefinition withClass:[Knight class] configuration:^(TyphoonDefinition *definition) {
        [definition injectProperty:@selector(quest)];
    }];
} 

NB: When injecting by type, Typhoon searches within the bounds of the TyphoonComponentFactory, so it can match components declared in [other assemblies](Modularizing Assemblies)

. . Typhoon will find the component that matches the required type.

see also: Autowiring (Objective-C)

By Reference

Injection can be done by reference. This is useful in the common requirement you have multiple components matching the same class or protocol. The injection is done by referencing a method in the assembly, meaning you can use all of your IDE refactoring tools, as you normally would.

- (Knight *)knight
{
    return [TyphoonDefinition withClass:[Knight class] 
        configuration:^(TyphoonDefinition *definition) {
        [definition injectProperty:@selector(quest) with:[self quest]];
    }];
}

- (id<Quest>)quest
{
    return [TyphoonDefinition withClass:[SaveTheKingdomQuestImpl class]];
}

see also: Modularizing Assemblies


#Injecting Typhoon Itself

Typhoon can inject itself. This is useful in order to proceed from one object-graph to another, for example, loading a new view controller to proceed to from the existing one.

When injecting Typhoon . . .

  • The property or method parameter can be of type TyphoonComponentFactory
  • The property can be any one of your TyphoonAssembly sub-classes (or if you wish, a protocol that represents these).

- (RootViewController *)rootController
{
    return [TyphoonDefinition withClass:[RootViewController class] configuration:^(TyphoonDefinition* definition)
    {
        [definition injectProperty:@selector(assembly)];
    }];
}

The assembly can be injected either by type, as shown above, or explicitly:

[definition injectProperty:@selector(assembly) with:self];

Injecting a collaborating assembly: (see Modularizing Assemblies)

[definition injectProperty:@selector(assembly) with:self.networkComponents];

. . similarly, the assembly can be injected into initializers, properties or methods.


As an alternative, if you're not worried about your class having a direct dependency on Typhoon, you can also do the following:

#import Typhoon.h

- (void)typhoonSetFactory:(id)theFactory
{
    //_factory is can be of type TyphoonComponentFactory . . . 
    //. . .  or any of your TyphoonAssembly sub-classes. 
    _factory = theFactory;
}


#Injecting Configuration

Besides injecting other Typhoon-built components, Typhoon can perform configuration by injecting simple objects and primitive values.

###Injecting a Simple Object

[definition injectProperty:@selector(serviceUrl) with:
    [NSURL URLWithString:@"http://www.myapp.com/service"]]; 

###Using Auto-boxing / NSValue to inject primitives

Primitives can be injected using auto-boxing. C-style strings and structs can be injected as an NSValue, Typhoon will unpack the value onto the object instance.

[initializer injectParameterWith:@(NSPrivateQueueConcurrencyType)]; 
[initializer injectParameterWith:@(INT_MAX)];
[initializer injectParameterWith:@YES];
[initializer injectParameterWith:[SomeClass class]];
[initializer injectParameterWith:NSValueFromPrimitive(@selector(selectorValue))];
const char *cString = "Hello Typhoon";
[initializer injectParameterWith:NSValueFromPrimitive(cString)];
[initializer injectParameterWith:[NSValue valueWithRange:NSMakeRange(10, 20)]];
[initializer injectParameterWith:[NSValue valueWithPointer:primitiveStruct]];

More examples of injecting primitives with NSValue: wrap-primitive-values-into-NSValue

##Typhoon Config

If you wish, configuration information can also be extracted into a properties, plist or json file that can be stored locally or resolved dynamically.

####Create a properties file with the values, as follows:

#for primitive values just write the property value
damsels.rescued=12
hasHorseWillTravel=no

#for object instances, declare the required type:
service.url=NSURL(http://my.backend.net/service-gateway)

####Or plist format . . .

<plist version="1.0">
	<dict>
		<key>damsels.rescued</key>
		<integer>12</integer>
		<key>service.url</key>
		<string>NSURL(http://my.backend.net/service-gateway)</string>
	</dict>
</plist>

####Or json format . . .

{
    "config": {
        "damsels.rescued": 12,
        "service.url": "NSURL(http://my.backend.net/service-gateway)"
    }
}

####Attach your config to your assembly:

- (id)configurer
{
    return [TyphoonDefinition configDefinitionWithName:@"Configuration.properties"];}

####Or attach it at runtime . . .

TyphoonConfigPostProcessor* configurer = [[TyphoonConfigPostProcessor alloc] init];
[configurer useResourceWithName:@"Configuration.properties"]];
[factory attachPostProcessor:configurer];

. . And now reference a value as follows:

[definition injectProperty:@selector(serviceUrl) with:
    TyphoonConfig(@"service.url")

###Registering Type Converters

Notice in the example above how service.url is declared as NSURL(http://my.backend.net/service-gateway), and Typhoon injects as NSURL` for us. Typhoon contains pre-built type converters, or you can register your own, as follows:

Create a class that conforms to the following:

@protocol TyphoonTypeConverter <NSObject>

- (id)supportedType;

- (id)convert:(NSString*)stringValue;

@end

. . . And then register it with the container as follows:

- (id)myTypeConverter
{
    //Will participate as a type converter as it conforms to the required protocol. 
    return [TyphoonDefinition withClass:[YourConverterClass class]];
}