Skip to content

What can be Injected

Jasper Blues edited this page Aug 9, 2017 · 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: Auto Injection

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 the assembly itself

Typhoon assemblies can be injected. 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.

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

We can then use this injected assembly to retrieve built instances from Typhoon 'on-demand', for example:

NewViewController *vc = [self.assembly customerListController]

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 plist file with the values, as follows:

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

Or properties format. . .

#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 json format . . .

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

Attach your config to your assembly:

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

Or attach it at runtime . . .

TyphoonConfigPostProcessor* configurer = 
    [TyphoonConfigPostProcessor forResourceNamed:@"Configuration.properties"];
[assembly attachPostProcessor:configurer];

. . And now reference a value as follows:

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

Configs can be of two types:

  • Global level (available from every assembly)
  • Per-assembly level (available only from one assembly)

Global TyphoonConfigs can be registered in four ways:

  • By listing all of the global config filenames in the Info.plist under the key TyphoonGlobalConfigFilenames.
  • By implementing the method -(NSArray *)globalConfigFilenames in the AppDelegate.
  • By adding the file with name config_BUNDLEID.plist, where BUNDLEID is the main bundle ID.
  • By attaching TyphoonConfigPostProcessor manually.

Scope of TyphoonConfigs created via the definition in an assembly is limited to TyphoonDefinitions from the same assembly.

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]];
}