Search code examples
dependency-injectionstoryboardtyphoon

Typhoon using storyboard with view controller initializers


I want to use storyboards to create a view controller named "child", so I define:

- (TyphoonStoryboard *)storyBoard
{
    return [TyphoonDefinition withClass:[TyphoonStoryboard class] configuration:^(TyphoonDefinition* definition) {
        [definition useInitializer:@selector(storyboardWithName:factory:bundle:) parameters:^(TyphoonMethod *initializer) {
            [initializer injectParameterWith:@"Storyboard"];
            [initializer injectParameterWith:self];
            [initializer injectParameterWith:[NSBundle mainBundle]];
        }];
        definition.scope = TyphoonScopeSingleton; //Let's make this a singleton
    }];
}

- (ChildViewController *)childViewControllerFromStoryboard
{
    return [TyphoonDefinition withFactory:self.storyBoard selector:@selector(instantiateInitialViewController)];
}

I'll have a root view controller that will contain the child view controller

- (RootViewController *)rootViewController {
    return [TyphoonDefinition withClass:[RootViewController class] configuration:^(TyphoonDefinition *definition) {
        [definition useInitializer:@selector(initWithChildViewController:) parameters:^(TyphoonMethod *initializer) {
            [initializer injectParameterWith:self.childViewControllerFromStoryboard];
        }];
    }]; 
}

The last part is just to inject the dependencies for the child view controller:

- (ChildViewController *)childViewController {
    return [TyphoonDefinition withClass:[ChildViewController class] configuration:^(TyphoonDefinition *definition) {
        // Initilization doesn't work.
        [definition useInitializer:@selector(initWithData:) parameters:^(TyphoonMethod *initializer) {
            [initializer injectParameterWith:self.data];
        }];
        // Property injection does.
        // [definition injectProperty:@selector(data)]
    }];
}

Super normal, I'm just using its initializator but it never gets called. I think it's because initWithCoder: is always used, the one designated for storyboards.

Questions:

  1. Is possible to use view controller initializers with storyboards? It looks like no, similar to appDelegate.
  2. Is this the simplest approach to use storyboards directly from the assembly?
  3. If I have several storyboards, is there a way of defining the storyboards parametrically? I don't want to end up with something like storyBoard1, storyBoard2, ..., storyBoardN.

Thanks in advance.


Solution

  • Thanks for your interest in Typhoon!

    1. Is possible to use view controller initializers with storyboards? It looks like no, similar to appDelegate.

    No, you cannot init object twice (you can call init method twice but it's odd and incorrect and can be cause of errors).

    Each UIViewController and UIView created from Xib/Storyboard uses initWithCoder: initialization method.

    Instead you can use method injections and inject setData: or use property injection.

    1. Is this the simplest approach to use storyboards directly from the assembly?

    No. Simplest approach is using plist bootstrapping. (When initial storyboard name specified in the Info.plist)

    Check guide here:

    https://github.com/typhoon-framework/Typhoon/wiki/Obtaining-Built-Components#first-bootstrap-typhoon-using-either

    But if you have more that one storyboard, then you can specify initial in the plist, and other in assembly.

    1. If I have several storyboards, is there a way of defining the storyboards parametrically? I don't want to end up with something like storyBoard1, storyBoard2, ..., storyBoardN.

    Yes, you can use runtime arguments typhoon feature for that. Using runtime arguments your definition becomes to

    - (TyphoonStoryboard *)storyBoardWithName:(NSString *)name
    {
        return [TyphoonDefinition withClass:[TyphoonStoryboard class] configuration:^(TyphoonDefinition* definition) {
            [definition useInitializer:@selector(storyboardWithName:factory:bundle:) parameters:^(TyphoonMethod *initializer) {
                [initializer injectParameterWith:name]; // <-- runtime argument injection
                [initializer injectParameterWith:self];
                [initializer injectParameterWith:[NSBundle mainBundle]];
            }];
         }];
    }
    

    Is it what you asking for?