Typhoon does not introduce property (no storyboard)

I cannot get properties injected in view controllers using XIB 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 tested the samples and they differ from this approach. Please tell me what am I doing wrong?

Thank.

+3


source to share


2 answers


Typhoon is a dependency injection container, meaning it doesn't work, swizzle, and doesn't touch your classes. Therefore, in order to get an instance of a class with injected dependencies, we must ask "Typhoon".

Why not storyboard?

When using the plist integration, Typhoon registers a TyphoonStoryboard instead of a UIStoryboard to emit instances based on the definitions in the storyboard - works just like a regular storyboard with the added benefit that dependencies inject.

Elsewhere, to get instances with injected dependencies, we use the build interface and set Typhoon.

Solution steps:

Add a definition for ViewControllerB in your assembly. (In Objective-C, you can also use auto-injection macros if you like.)

- (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 property to ViewControllerB:

//Start with this, next task is to back this with a protocol
@property(nonatomic, strong) AppAssembly *assembly;

      

Now, change the button action that instantiates the 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 to?

All Typhoon assemblies can be supported by the protocol, so your application only sees the instance provider, not the typhoon. If you've ever wanted to migrate from Typhoon, just provide an alternative implementation of this protocol - you still have a robust architecture.

+1


source


So, I had to first ask my AppAssembly for a nested viewControllerB instance, and then I was able to use it to get a viewControllerC instance.

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 this, I was able to put the assembly in ViewControllerB and it allowed me to do this: ViewControllerC *viewControllerC = [self.assembly viewControllerC];

However, I noticed that I could have done it as well ViewControllerC *viewControllerC = [[[AppAssembly new] activate] viewControllerC];

, so not sure what the best approach is. Anyway, I think I had to call in a new way and activate at least once.

Thank.

0


source







All Articles