I am trying to bootstrap Typhoon
using the PList integration method but my ApplicationDelegate
is being created twice. The first time it is created, it is obviously being created by Typhoon
. That time, it uses the special initializer initWithAssembly:
and Typhoon
feeds it the assembly.
The second time, the time that matters, it is created using init
. It never gets a reference to the assembly.
Just in case, I also injected the assembly
via the property method. No go.
Here is the code:
Assembly
- (UIApplication *)sharedApplication {
return [TyphoonDefinition withClass:[UIApplication class] configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(sharedApplication)];
}];
}
- (CTISApplicationDelegate *)appDelegate {
return [TyphoonDefinition withClass:[CTISApplicationDelegate class]
configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(initWithAssembly:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:@(3)];
}];
definition.scope = TyphoonScopeSingleton;
}];
}
AppDelegate
@property (nonatomic, strong, readwrite) ApplicationAssembly *assembly;
@property (nonatomic, strong, readwrite) UIWindow *window;
- (instancetype)initWithAssembly:(ApplicationAssembly *)assembly;
...
// This gets called once, the first time, and assembly is NOT nil.
- (instancetype)initWithAssembly:(ApplicationAssembly *)assembly {
self = [super init];
if (self) {
self.assembly = assembly;
}
return self;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// This gets ca
lled once (after second init) and self.assembly is nil.
AcceptDisclaimerAppInfoModule *disclaimer = [[self.assembly applicationInformationModuleAssembly] acceptDisclaimerModule];
[disclaimer launchModuleFromWindow:self.window];
[self.window makeKeyAndVisible];
return YES;
}
After looking online and going a bit crazy over-thinking this problem, I came to a number of conclusions.
The root of the problem is that
Typhoon
and mymain.m
entry point were not in sync in any form. So,main.m
callsUIApplicationMain()
and one of the arguments is a string that specifies the kind ofid<UIApplicationDelegate>
you want. I've never seen any deviation from this pattern so I was not willing to change that up.Therefore, it is a given that the
id<UIApplicationDelegate>
is not going to be constructed viaTyphoon
in a way that is "built-into" the framework. And while you could do one of the following, I don't recommend any: they all seem wrong.TyphoonAssembly
directly from your app delegateIContainer
object that can hug theTyphoonAssembly
instance created on startupThe problem is... at some point, you're going to need to do one of these evil things no matter what if you don't get it right.
The reason is...
Typhoon
is clearly designed to work in the context of "object graphs," so the entireTyphoonAssembly
and any connected assemblies can be thought of as webs of graphs. Once you get in the web, you're fine - you can take it from there. You just need to get in...So, I decided to do as follows:
IContainer
, even if they spanned multiple assemblies or if were smaller than an assembly. This disconnected the idea ofTyphoon
from theIContainer
and makes it possible to debug withoutTyphoon
by substituting a mockIContainer
in place.IContainer
in question.IContainer
, because you've already broken encapsulation and you might as well make it easy on yourself.Typhoon
's default scope works they way you think it does. I implemented a few "alerts" whenever I detected multiple calls to any constructor in the same object graph.id<nonatomic, weak>
for delegate types, notid<nonatomic, assign>
as I had done for the past year. Something about the wayTyphoon
works under the hood must make it constantly be letting go of delegates.In your Info.plist, add a key called
TyphoonInitialAssemblies
withArray
type and values that are the class names of your assemblies. But...Don't forget to do the other half, which is to make sure you have a "root" assembly like
RootAssembly
and then someModuleAssembly
s that are stored by theRootAssembly
:In this case, your Info.plist would have:
TyphoonInitialAssemblies
(Array
)SubAssemblyA
SubAssemblyB