-
Notifications
You must be signed in to change notification settings - Fork 268
When to use runtime arguments
Back to [Types of Injections](Types of Injections)
Typhoon allows defining components with run-time arguments, in order to implement the factory pattern.
The factory pattern is a common good practice to create objects that mix both dependencies and parametrization. Factories are also very suited to “hide” dependencies of the created objects, which the creator does not have to care about. Factories are usually a lot of boilerplate code, prone to tedious repetition and human error. By defining components that mix static dependencies and run-time arguments, Typhoon can create these factories for you.
Here's a concrete example: Imagine having two view controllers with the following interfaces:
@interface FriendListViewController
- (instancetype)initWithUsersService:(UsersService *)usersService; // dependency
@end
@interface UserDetailsViewController
- (instancetype)initWithPhotoService:(PhotoService *)photoService // dependency
user:(User *)user;
@end
FriendListViewController
shows a list of users and UserDetailsViewController
shows the details of the user passed as an argument, including a nice photo of the user retrieved from PhotoService
.
Clearly, if we want FriendListViewController
to instantiate UserDetailsViewController
, then it's lacking a dependency: PhotoService
.
To solve this, one way would be to make FriendListViewController
accept a PhotoService
as a parameter to its initializer, thereby creating an artificial dependency. This is not the best approach as it might cause a "dependency explosion" — the early instantiation of all of PhotoService
's dependencies, which could lead to excessive memory consumption, for instance.
The best solution is to let FriendListViewController
use a factory that when invoked that returns an instance of UserDetailsViewController
for some specific user.
Run-time arguments allow defining a factory on the assembly interface. Here is an example:
- (id)userDetailsControllerForUser:(User *)user
{
return [TyphoonDefinition withClass:[UserDetailsViewController class]
configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(initWithPhotoService:user)
parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:[self photoService];
[initializer injectParameterWith:user];
}];
}];
}
So having defined the UserDetailsViewController
with a runtime argument, we can now inject the assembly on the FriendListViewController
as follows:
- (id)friendListController
{
return [TyphoonDefinition withClass:[FriendListViewController class]
configuration:^(TyphoonDefinition *definition) {
[definition injectProperty:@selector(assembly) with:self];
}];
}
We can obtain a UserDetailsViewController
with both the static and runtime dependencies as follows:
User* aUser = friendListController.selectedUser;
UserDetailsViewController* detailsController = [friendListController.assembly
userDetailsControllerForUser:aUser];
User* aUser = self.selectedUser;
Something still not clear? How about posting a question on StackOverflow.
Get started in two minutes.
Get familiar with Typhoon.
- Types of Injections
- What can be Injected
- Auto-injection (Objective-C)
- Scopes
- Storyboards
- TyphoonLoadedView
- Activating Assemblies
Become a Typhoon expert.
For contributors or curious folks.