问题
I cannot get properties injected into view controllers using XIBs with initWithNibName:bundle:
Example:
This is my assembly:
@implementation AppAssembly
- (ViewControllerC *)viewControllerC
{
return [TyphoonDefinition withClass:[ViewControllerC class]
configuration:^(TyphoonDefinition *definition) {
[definition injectProperty:@selector(name) with:@"Injected string"];
}];
}
@end
ViewControllerA code:
@implementation ViewControllerA
- (IBAction)buttonAction:(id)sender
{
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNibName:@"ViewControllerB" bundle:nil];
[self.navigationController pushViewController:viewControllerB animated:YES];
}
@end
ViewControllerB code:
@implementation ViewControllerB
- (IBAction)buttonAction:(id)sender
{
ViewControllerC *viewControllerC = [[ViewControllerC alloc] initWithNibName:@"ViewControllerC" bundle:nil];
[self.navigationController pushViewController:viewControllerC animated:YES];
}
@end
ViewControllerC code:
@interface ViewControllerC : UIViewController
@property (copy, nonatomic) NSString *name;
@end
@implementation ViewControllerC
- (void)viewDidLoad
{
[super viewDidLoad];
// Why is this not being injected?
self.title = self.name;
}
@end
AppDelegate code:
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
ViewControllerA *rootViewController = [[ViewControllerA alloc] initWithNibName:@"ViewControllerA" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
}
@end
I have already checked the samples, and they are different to this approach. Please can you tell me what am I doing wrong?
Thanks.
回答1:
Typhoon is a dependency injection container, meaning it doesn't instrument, swizzle or otherwise touch your classes. Therefore to get an instance of a class with dependencies injected, we have to ask Typhoon.
Why not for Storyboards?
When using plist integration, Typhoon registers TyphoonStoryboard in place of UIStoryboard to emit instances based on definitions in the storyboard - works just like a normal storyboard with the added benefit that dependencies are injected.
In other places, to get instances with dependencies injected, we use the assembly interface, and ask Typhoon.
Solution Steps:
Add a definition for ViewControllerB in your assembly. (In Objective-C you can also use auto-injection macros if you wish).
- (ViewControllerB *)viewControllerB
{
return [TyphoonDefinition withClass:[ViewControllerB class]
configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(initWithNibName:bundle:)
parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:@"ViewControllerB"];
[initializer injectParameterWith:[NSBundle mainBundle]];
}];
[definition injectProperty:@selector(assembly) with:self];
}];
}
Add a property to ViewControllerB:
//Start with this, next task is to back this with a protocol
@property(nonatomic, strong) AppAssembly *assembly;
Now change the the button action that instantiates ViewControllerC:
ViewControllerC *viewControllerC = self.assembly.viewControllerC
[self.navigationController pushViewController:viewControllerC animated:YES];
Now my application class depends on my TyphoonAssembly, what if I don't want this?
All Typhoon assemblies can be backed by a protocol, so that your application sees only a provider of instances, and not Typhoon. If you ever wished to migrate from Typhoon, simply provide an alternative implementation of that protocol - you'll still have a robust architecture in place.
回答2:
So, I had to first ask my AppAssembly for an instance of viewControllerB with the assembly injected and then I was able to use it to get an instance of viewControllerC.
AppAssembly code:
- (ViewControllerB *)viewControllerB
{
return [TyphoonDefinition withClass:[ViewControllerB class]
configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(initWithNibName:bundle:)
parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:@"ViewControllerB"];
[initializer injectParameterWith:nil];
}];
[definition injectProperty:@selector(assembly) with:self];
}];
}
- (ViewControllerC *)viewControllerC
{
return [TyphoonDefinition withClass:[ViewControllerC class]
configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(initWithNibName:bundle:)
parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:@"ViewControllerC"];
[initializer injectParameterWith:nil];
}];
[definition injectProperty:@selector(name) with:@"Injected string"];
}];
}
ViewControllerA code:
@implementation ViewControllerA
- (IBAction)buttonAction:(id)sender
{
ViewControllerB *viewControllerB = [[[AppAssembly new] activate] viewControllerB];
[self.navigationController pushViewController:viewControllerB animated:YES];
}
@end
ViewControllerB code:
@implementation ViewControllerB
- (IBAction)buttonAction:(id)sender
{
ViewControllerC *viewControllerC = [self.assembly viewControllerC];
[self.navigationController pushViewController:viewControllerC animated:YES];
}
@end
ViewControllerC code:
@implementation ViewControllerC
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = self.name;
}
@end
As you can see I had to do this: ViewControllerB *viewControllerB = [[[AppAssembly new] activate] viewControllerB];
Not sure if there is another way.
Because of doing that I was able to have the assembly injected in ViewControllerB, and that let me do this: ViewControllerC *viewControllerC = [self.assembly viewControllerC];
However, I noticed that I could also do ViewControllerC *viewControllerC = [[[AppAssembly new] activate] viewControllerC];
, so not sure which the better approach is.
Anyway, I think I had to call new and activate at least once.
Thanks.
来源:https://stackoverflow.com/questions/31507439/typhoon-not-injecting-property-without-storyboard