Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assembly property isn't filled with correct object in storyboard switch #337

Closed
th-zz-ord-zz-rive opened this issue Apr 10, 2015 · 8 comments

Comments

@th-zz-ord-zz-rive
Copy link

Hello!

I'm currently writing an app and until now we haven't had any trouble using Typhoon, but the application was becoming too big for us to use a single storyboard and we tried to divide it into several. We usually don't have any problems switching from storyboard to storyboard except when we try to get object in an assembly that is referring another.
I've pasted the whole classes but made some comments the parts of the code that are involved in the execution trace.
The actual error is: "No component matching id 'appleContactsService'.

The TyphoonInitialAssemblies in the plist goes like this:
StoryboardViewFactoryAssembly
MainStoryboardViewFactoryAssembly
ModelCoreAssembly
ServiceAssembly

we also tried to put QuickSearchStoryboardViewFactoryAssembly in the initial plist thinking that maybe like that the ServiceAssembly reference could be resolved but that also didn't work.

-------------------------------------------------- MAPWIREFRAME--------------------------------------------------

#import "MapWireframe.h"
#import "StoryboardViewFactoryProtocol.h"
#import "QuickSearchStoryboardViewFactoryProtocol.h"
#import "DetailsStoryboardViewFactoryProtocol.h"

@implementation MapWireframe

- (NSString*) getViewControllerIdentifier
{
    return @"MapViewController";
}

- (void) presentQuicksearchViewController
{

    //This is were we get the QuickSearchStoryboardViewFactory from the StoryboardViewFactory
    //Then we get the QuickSearchViewController
    id<QuickSearchStoryboardViewFactoryProtocol> vcf = [self.storyboardViewFactory quickSearchStoryboardViewFactory];
    id vc = [vcf quickSearchViewController];
    [self.viewController presentViewController:vc animated:NO completion:nil];
}

- (void) presentAnnotationDetailsViewController:(id)aContext
{
    id<DetailsStoryboardViewFactoryProtocol> vcf = [self.storyboardViewFactorydetailsStoryboardViewFactory];
    id vc = [vcf detailsViewControllerWithContext:aContext];
    [self.viewController presentViewController:vc animated:NO completion:nil];
}

@end

-----------------------------------------StoryboardViewFactoryAssembly--------------------------------------------

#import "StoryboardViewFactoryAssembly.h"
#import "MainStoryboardViewFactoryAssembly.h"
#import "QuickSearchStoryboardViewFactoryAssembly.h"
#import "SettingsStoryboardViewFactoryAssembly.h"
#import "DetailsStoryboardViewFactoryAssembly.h"

@implementation StoryboardViewFactoryAssembly

- (id<MainStoryboardViewFactoryProtocol>) mainStoryboardViewFactory
{
    return [TyphoonDefinition withClass:[MainStoryboardViewFactoryAssembly class] configuration:^(TyphoonDefinition *definition) {
        [definition setScope:TyphoonScopeLazySingleton];
    }];
}

- (id<SettingsStoryboardViewFactoryProtocol>) settingsStoryboardViewFactory
{
    return [TyphoonDefinition withClass:[MainStoryboardViewFactoryAssembly class] configuration:^(TyphoonDefinition *definition) {
        [definition setScope:TyphoonScopeLazySingleton];
    }];
}

  //We get the QuickSearchStoryboardViewFactory from here
- (id<QuickSearchStoryboardViewFactoryProtocol>) quickSearchStoryboardViewFactory
{
    return [TyphoonDefinition withClass:[QuickSearchStoryboardViewFactoryAssembly class] configuration:^(TyphoonDefinition *definition) {
        [definition setScope:TyphoonScopeLazySingleton];
    }];
}

- (id<DetailsStoryboardViewFactoryProtocol>) detailsStoryboardViewFactory
{
    return [TyphoonDefinition withClass:[DetailsStoryboardViewFactoryAssembly class] configuration:^(TyphoonDefinition *definition) {
        [definition setScope:TyphoonScopeLazySingleton];
    }];
}
@end

------------------------------------QuickSearchStoryboardViewFactoryAssembly-------------------------------
This class inherits from FactoryAssembly whose only method is

- (id) init{
    self = [super init];

    if (self) {
        //This call is extremely important without it the subclass methods would return TyphoonDefinition objects and not the proper object 
        [[TyphoonAssemblyActivator withAssemblies:@[self]] activate];
    }

    return self;
}

interface

#import <Typhoon/TyphoonAssembly.h>
#import "FactoryAssembly.h"
#import "QuickSearchStoryboardViewFactoryProtocol.h"

@class ServiceAssembly;
@class ModelCoreAssembly;

@interface QuickSearchStoryboardViewFactoryAssembly : FactoryAssembly<QuickSearchStoryboardViewFactoryProtocol>

@property(nonatomic, strong, readonly) ServiceAssembly      *serviceAssembly;


@end

implementation

#import "QuickSearchStoryboardViewFactoryAssembly.h"

#import "TableViewCellReuseIdentifierFactory.h"

#import "QuickSearchViewController.h"
#import "QuickSearchViewModel.h"
#import "QuickSearchWireframe.h"
#import "QuickSearchCellViewModelFactory.h"

#import "ServiceAssembly.h"

#import <Typhoon/TyphoonStoryboard.h>
#import <Typhoon/TyphoonFactoryDefinition.h>

@implementation QuickSearchStoryboardViewFactoryAssembly

- (id<QuickSearchWireframeProtocol>) quickSearchWireframe
{
    return [TyphoonDefinition withClass:[QuickSearchWireframe class] configuration:^(TyphoonDefinition *definition) {
        //[definition injectProperty:@selector(presentedViewController) with:[self quickSearchViewController]];
    }];
}

//This is the method where we try to obtain the QuickSearchViewController (which is also pointed as the initial view controller in its storyboard)
- (id<QuickSearchViewControllerProtocol>) quickSearchViewController
{
    return [TyphoonDefinition withFactory:[self quickSearchStoryboard]
                                 selector:@selector(instantiateInitialViewController)
                               parameters:^(TyphoonMethod *factoryMethod) {
                                   // no parameters
                               } configuration:^(TyphoonFactoryDefinition *definition) {
//the problem arrises from trying to resolve this definition
                                   [definition injectProperty:@selector(viewModel) with:[self quickSearchViewModel]];
                                   [definition injectProperty:@selector(cellReuseIdentifierFactory) with:[self quickSearchCellReuseIdentifierFactory]];
                               }];
}

   //The initialization of the new QuickSearchStoryboard
- (UIStoryboard*)quickSearchStoryboard
{
return [TyphoonDefinition withClass:[TyphoonStoryboard class] configuration:^(TyphoonDefinition* definition) {
        [definition useInitializer:@selector(storyboardWithName:factory:bundle:) parameters:^(TyphoonMethod *initializer) {
        [initializer injectParameterWith:@"QuickSearch"];
        [initializer injectParameterWith:self];
        [initializer injectParameterWith:[NSBundle mainBundle]];
        }];
        definition.scope = TyphoonScopeSingleton;
    }];
}

//The problem appears to be when we are trying to inicialize the view model object because the service _serviceAssembly isn't a ServiceAssembly object but an TyphoonCollaboratingAssemblyObject, in ower .plist we are already loading objects of this type in similar situations. If we don't try to do something like this and just pass nil, the view opens but without the service obviously...
- (id<QuickSearchViewModelProtocol>) quickSearchViewModel
{
return [TyphoonDefinition withClass:[QuickSearchViewModel class] configuration:^(TyphoonDefinition *definition) {
        [definition useInitializer:@selector(initWithCor3Service:appleContactsService:) parameters:^(TyphoonMethod *initializer) {
            [initializer injectParameterWith:nil];
            [initializer injectParameterWith:[self->_serviceAssembly appleContactsService]];
        }];
    }];
}

- (id<TableViewCellReuseIdentifierFactoryProtocol>) quickSearchCellReuseIdentifierFactory
{
    return [TyphoonDefinition withClass:[TableViewCellReuseIdentifierFactory class] configuration:^(TyphoonDefinition *definition) {
        [definition useInitializer:@selector(initWithProtocolNibMapping:defaultNibName:) parameters:^(TyphoonMethod *initializer) {
            [initializer injectParameterWith:nil];
            [initializer injectParameterWith:@"SingleLineTableCell"];
        }];
    }];
}

- (id<CellViewModelFactoryProtocol>) quickSearchCellViewModelFactory
{
    return [TyphoonDefinition withClass:[QuickSearchCellViewModelFactory class]];
}
@end

thanks in advance!

@alexgarbarev
Copy link
Contributor

I haven't read whole code, but I noticed that code:

[[TyphoonAssemblyActivator withAssemblies:@[self]] activate];

In that case your self assembly (QuickSearchStoryboardViewFactoryAssembly) wouldn't wired with other assemblies.

If you use TyphoonAssemblyActivator interface, then you have to put all assemblies into one array and active them together.

If you using plist integration, then you have to add your QuickSearchStoryboardViewFactoryAssembly into plist and inject it later like this:

@property (nonatomic, strong) InjectedClass(QuickSearchStoryboardViewFactoryAssembly) quickSearchStoryboardViewFactory;

or inject that manually with

@property (nonatomic, strong) id<DetailsStoryboardViewFactoryProtocol> quickSearchStoryboardViewFactory;

And put inside MapWireframe definition

return [TyphoonDefinition withClass:[MapWireframe class] configuration:^(TyphoonDefinition* definition) {
        [definition injectProperty:@selector(quickSearchStoryboardViewFactory) with:self];
    }];
}

@th-zz-ord-zz-rive
Copy link
Author

Thanks for the answer!
I've made the changes you suggested, but the problem remains, with the same error message "No component matching id 'appleContactsService'."...

@alexgarbarev
Copy link
Contributor

Check that ServiceAssembly has appleContactsService method and all assemblies wired to each other.
What the changes you did? What is your plist now?

@th-zz-ord-zz-rive
Copy link
Author

  • I've changed the superclass of QuickSearchStoryboardViewFactoryAssembly from FactoryAssembly to TyphoonAssembly.
  • Then started injecting it into MapWireframe with : @property (nonatomic, strong) InjectedClass(QuickSearchStoryboardViewFactoryAssembly) quickSearchStoryboardViewFactory;
  • And added QuickSearchStoryboardViewFactoryAssembly to the plist file

The plist now has the following classes to be loaded under TyphoonInitialAssemblies:

MainStoryboardViewFactoryAssembly
ModelCoreAssembly
ServiceAssembly
QuickSearchStoryboardViewFactoryAssembly

@alexgarbarev
Copy link
Contributor

Did you remove this?

- (id) init{
    self = [super init];

    if (self) {
        //This call is extremely important without it the subclass methods would return TyphoonDefinition objects and not the proper object 
        [[TyphoonAssemblyActivator withAssemblies:@[self]] activate];
    }

    return self;
}

@th-zz-ord-zz-rive
Copy link
Author

yes, it's no longer part of the class

@alexgarbarev
Copy link
Contributor

Ah.. You also have to remove this:

  //We get the QuickSearchStoryboardViewFactory from here
- (id<QuickSearchStoryboardViewFactoryProtocol>) quickSearchStoryboardViewFactory
{
    return [TyphoonDefinition withClass:[QuickSearchStoryboardViewFactoryAssembly class] configuration:^(TyphoonDefinition *definition) {
        [definition setScope:TyphoonScopeLazySingleton];
    }];
}

From your StoryboardViewFactoryAssembly

@th-zz-ord-zz-rive
Copy link
Author

That's it, it's working now!

Many thanks! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants