appsquickly / typhoon

Powerful dependency injection for Objective-C ✨✨ (https://PILGRIM.PH is the pure Swift successor to Typhoon!!)✨✨
https://pilgrim.ph
Apache License 2.0
2.7k stars 269 forks source link

Use parent definition with same selector name causes forever loop #514

Closed rock88 closed 7 years ago

rock88 commented 8 years ago

Hello, I want to use some default assembly with few definitions with few default property injection. This assembly may be like:

@implementation AssemblyA

- (UIViewController *)viewController {
    return [TyphoonDefinition withClass:[UIViewController class] configuration:^(TyphoonDefinition *definition) {
        [definition injectProperty:@selector(title) with:@"TitleA"];
        [definition injectProperty:@selector(output) with:[self presenter]];
        //other injections
    }];
}

- (Presenter *)presenter {
    return [TyphoonDefinition withClass:[Presenter class] configuration:^(TyphoonDefinition *definition) {
         [definition injectProperty:@selector(view) with:[self viewController]];
    }];
}

@end

Next I make AssemblyB (subclasses from AssemblyA) with same definition name and want to change UIViewController class (and maybe few new property injections):

@implementation AssemblyB

- (UIViewController *)viewController {
    return [TyphoonDefinition withClass:[LoginViewController class] configuration:^(TyphoonDefinition *definition) {
        definition.parent = [super viewController];
        //other injections
    }];
}

@end

In this case Typhoon framework entered in forever loop.

For resolve this issues I use several solutions:

  1. Just rename definition name in AssemblyB (For this options all definitions, which use original -viewController will be not use new, for example -[AssemblyA presenter] will be injected with original -viewController);
  2. Don't use definition.parent and copy/paste all injection from -[AssemblyA viewController] (copy/paste - bad solution 😕 )

Maybe you can explain me how best solution for this case, ideally for me - if definition.parent = [super viewController] will be work.

Thanks in advance for any help!

Przemyslaw-Wosko commented 8 years ago

first of all - typhoon have pretty good documentation there is page that might interest you: https://github.com/appsquickly/typhoon/wiki/types-of-injections

I would use base and abstract definitions, something like: 1) make interface without optionals to describe base components / settings 2) create base definition

- (id<ViewControllerRequiredShit> )abstractViewController
{
    return [TyphoonDefinition withClass:[UIViewController class] // change it to your base class 
    configuration:^(TyphoonDefinition* definition) {
       // i hope that you want to inject more common things than title! using base definitions like that is quite poor  
[[definition injectProperty:@selector(title) with:@"TitleA"];
       // don't inject it here! you don't know what presenter you will be need 
[definition injectProperty:@selector(output) with:[self presenter]];
    }];
}

3) override it and make it happen

- (YourVerySpecialViewController *)verySpecialViewController
{
    return [TyphoonDefinition withParent:[self abstractViewController] 
        class:[YourVerySpecialViewController class]
        configuration:^(TyphoonDefinition* definition) {

        definition.scope = TyphoonScopeWeakSingleton;
        [definition injectProperty:@selector(registered) with:@(NO)];
        [definition injectProperty:@selector(presenter) with:[self.presentersAssembly yourVerySpecialPresenter]];
    }];
}

it's probably little bit different than you planned but in this way it will work if it comes to building base definitions

something like injecting base presenter in base view controller - i have tried it but it was misconception of using dependency injection make ViewControllers assembly for some part of application, make assembly for presenters, make property in view controllers assembly of type presenters assembly and use it to inject presenter into view controller

rock88 commented 8 years ago

@CurlyHeir abstract definition seems like to my first option, with all minuses.

injecting base presenter in base view controller

For example - needs build assembly for login screen with loginViewController <-> loginPresenter relation. But in other target I need to use other loginViewController or loginPresenter class (or subclass). Maybe for this case best solution - create new loginViewController definition with other selector name and use origin as parent. But for complex relation like definitions for VIPER module this not usable.

make ViewControllers assembly for some part of application, make assembly for presenters, make property in view controllers assembly of type presenters assembly and use it to inject presenter into view controller

For this solution instead of one assembly for one VIPER module we will have four for each component and one for assembly all components in module. Maybe it's ok, I try it, thanks!

Przemyslaw-Wosko commented 8 years ago

@rock88 ok, if you will write some code, just post it here, maybe then i will try to make some workaround or try different approach

etolstoy commented 7 years ago

Closing due to inactivity. If you still experience this issue, feel free to reopen it.