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
Typhoonand mymain.mentry point were not in sync in any form. So,main.mcallsUIApplicationMain()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 viaTyphoonin 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.TyphoonAssemblydirectly from your app delegateIContainerobject that can hug theTyphoonAssemblyinstance 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...
Typhoonis clearly designed to work in the context of "object graphs," so the entireTyphoonAssemblyand 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 ofTyphoonfrom theIContainerand makes it possible to debug withoutTyphoonby substituting a mockIContainerin place.IContainerin 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 wayTyphoonworks under the hood must make it constantly be letting go of delegates.In your Info.plist, add a key called
TyphoonInitialAssemblieswithArraytype 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
RootAssemblyand then someModuleAssemblys that are stored by theRootAssembly:In this case, your Info.plist would have:
TyphoonInitialAssemblies(Array)SubAssemblyASubAssemblyB