Typhoon
Poweful dependency injection for iOS & OSX (Objective-C & Swift)
Typhoon - Powerful Dependency Injection for Objective-C & Swift typhoon - a new dependency injection container for objective-c
How would I use Typhoon with iOS storyboards where view controllers are generated implicitly by the system? Would I have to do something special in the prepareForSegue methods?
Source: (StackOverflow)
I have an app that has about 11 different Singleton instances used throughout the many app's methods and classes; it's getting out of hand, and I want to replace all of them with dependency injection, like Typhoon. However, I am unable to find any doc, sample or mention of how to replace the singletons with dependency injection, including Typhoon. For instance, do I use multiple instances of Typhoon, replacing each singleton with an instance of Typhoon?
Source: (StackOverflow)
I've defined an ApplicationAssembly in Typhoon.
So what I want to do is say: "This class X needs to be injected with something conforming to the Foo protocol. This is a RealFoo, this is a TestFoo. When I'm running X in real life, I want it to get a RealFoo, but when I'm running my integration tests, I want it to get a TestFoo".
How can I do this?
Source: (StackOverflow)
I have very annoying problem with Typhoon Framework version 2.3.0 in Swift project.
I included Typhoon in Podfile as mentiond in tutorial, installed Pods, created bridging header and added #import <Typhoon/Typhoon.h>
in this header.
Then I created assembly subclass called ApplicationAssebly :
import Foundation
public class ApplicationAssembly: TyphoonAssembly {
public dynamic func appDelegate() -> AnyObject {
return TyphoonDefinition.withClass(AppDelegate.self) {
(definition) in
definition.injectProperty("myAssembly", with: self)
}
}
}
As you see I want to inject that assembly into AppDelegate.
I have also added TyphoonInitialAssemblies entry in Info.plist file. And in this moment my problems has started. I have tested few combinations resulting in NSException :
Can't resolve assembly for name xxx
This combinations are (typhtest is project/bundle name):
- ClassName in Info.plist: ApplicationAssembly, Defines Module property in Build Settings : No
- ClassName in Info.plist: ApplicationAssembly, Defines Module property in Build Settings : Yes
- ClassName in Info.plist: typhtest.ApplicationAssembly, Defines Module property in Build Settings : No
- ClassName in Info.plist: typhtest.ApplicationAssembly, Defines Module property in Build Settings : Yes
I have found this answer on StackOverflow so I've tried the last combination :
- ClassName in Info.plist: _TtC8typhtest19ApplicationAssembly, Defines Module property in Build Settings : Yes
This combination doesn't throw NSException but I have dyld_fatal_error, stack trace from iPhone 5s (iOS 7.1) below :
I get slightly different stack trace from iPhone simulator (iOS 7.1) :
What is strange that it works on iOS 8.1 simulator ! Also Typhoon Sample Application for Swift works well on my device.
I also tried to clean any Xcode and project caches and DerivedData directories, I've cleaned project and build folder and rebuilded the project, but it's not working. My Xcode version is 6.1 (6A1052d) and I'm using OSX Yosemite 10.10.1 .
GitHub repository with my code : https://github.com/papcio28/Typhoon-Dyld-Error
Edited 21.11.2014
What is also strange is that if I create the factory manually and inject something also manually, Typhoon works. Steps that I've made are :
- Removed
TyphoonInitialAssemblies
item from Info.plist
Changes AppDelegate.application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
to
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let factory = TyphoonBlockComponentFactory(assemblies: [AppAssembly()])
factory.inject(self)
return true
}
But it doesn't change a fact that I want to use Typhoon without defining factory manually, so the question is still actual.
Source: (StackOverflow)
i’m using Swift with Typhoon and Cocoapods. Everything worked well until i started to write an Integrationtest (according to the Typhoon-Example-App Test) for my Typhoon component. I wanted to setup the TyphoonFactory
in the Test setUp()
method in the same way as i did in the AppDelegate
. When i execute the test i always get a
TyphoonBlockComponentFactory assertIsAssembly:] + 244: ERROR: MyApp.MyAssembly is not a sub-class of TyphoonAssembly
error thrown by Typhoon (wich is using the kindOfClass
method under the hood.) The same code is working perfectly in the AppDelegate
and i can’t figure out whats wrong.
To verify this behavior i implemented the isKindOfClass
check in booth classes (see code below):
- AppDelegate -> true
- MyComponentTest -> false
Can someone pls help me further?
Thx a lot!
PodFile
inhibit_all_warnings!
target "MyApp" do
pod 'Typhoon', '2.1.0'
end
target "MyAppTests" do
pod 'Typhoon', '2.1.0'
end
MyAssembly.swift
public class MyAssembly : TyphoonAssembly{
//Some definitions
}
AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
…
var assembly : MyAssembly = MyAssembly()
//Always returns „true“
println("Is type of class: \(assembly.isKindOfClass(TyphoonAssembly))")
…
}
MyComponentTest.swift
import XCTest
import MyApp
class MyComponentTest: XCTestCase {
override func setUp() {
super.setup()
var assembly : MyAssembly = MyAssembly()
//Always returns „false“!
println("Is type of class: \(assembly.isKindOfClass(TyphoonAssembly))")
//Error is thrown „MyApp.MyAssembly is not a sub-class of TyphoonAssembly“
var factory : TyphoonComponentFactory = TyphoonBlockComponentFactory(assembly: assembly) as TyphoonComponentFactory
}
}
Source: (StackOverflow)
I am using a StoryBoard in my application. When I first started integrating Typhoon, I listed the Assemblies in the plist like so:
<key>TyphoonInitialAssemblies</key>
<array>
<string>ApplicationAssembly</string>
<string>CoreComponents</string>
</array>
This worked fine as I was injecting into the AppDelegate.
Now, if I need to inject into the various view controllers, it appears I have to remove the UILaunchStoryboardName
and UIMainStoryboardFile
from the application plist file, and use a TyphoonStoryboard
like so:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString *storyboardName = ...
TyphoonComponentFactory *factory = ...
TyphoonStoryboard *storyboard = [TyphoonStoryboard
storyboardWithName:storyboardName factory:factory bundle:nil];
self.window = ...
self.window.rootViewController = [storyboard instantiateInitialViewController];
[self.window makeKeyAndVisible];
return YES;
}
However, I'm confused where I obtain the TyphoonComponentFactory
. Since I already list the assemblies in the plist, can I somehow use that?
Source: (StackOverflow)
I'm using Typhoon library for Dependency Injection Framework. I use CocoaPod
for installing this library. Here is my pod file:
target "typhoon-swift-demo" do
pod 'Typhoon'
end
target "typhoon-swift-demoTests" do
end
I have installed successfully but when I open workspace project file. I type those line of code as Typhoon sample code:
public class ApplicationAssembly: TyphoonAssembly {
}
I meet error that my application doesn't recognize TyphoonAssembly
I have tried to use some lines such as:
import Typhoon // not recogize typhoon
import TyphoonAssembly // not regconize
Please tell me how to fix this problem. What should I add before I can use library. Thanks :)
Source: (StackOverflow)
I'm trying to create patcher for selector in assembly that uses run-time arguments feature but no luck. Has anyone solved similar issue or it can't be using Swift yet?
Method definition in assembly looks like this:
public dynamic func requestCodeApiGateway(phone: NSString) -> AnyObject {
return TyphoonDefinition.withClass(RequestCodeApiGatewayImpl.self) { (definition) in
definition.useInitializer("initWithApiService:apiRouter:phone:") { (initializer) in
// ...
}
}
}
And I'm creating Patcher like this:
let patcher = TyphoonPatcher()
patcher.patchDefinitionWithSelector("requestCodeApiGatewayWithPhone:") {
// ...
}
P.S. solutions with partially using Objective-C would be also appreciated
Source: (StackOverflow)
I am using Typhoon for dependency injection. But I have a problem. Occasionally, I get the following exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'No component matching id 'nemIdStoryboard'.'
The code, where this exception occurs, looks like this:
class PaymentApproveViewController : UIViewController {
var assembly : ApplicationAssembly!
//...
private func signPayment() {
let storyboard = assembly.nemIdStoryboard()
let controller = storyboard.instantiateInitialViewController() as NemIDViewController
//...
}
}
And my assembly code looks like this:
public dynamic func rootViewController() -> AnyObject {
return TyphoonDefinition.withClass(RootViewController.self) {
$0.injectProperty("assembly", with: self)
}
}
public dynamic func paymentApproveViewController() -> AnyObject {
return TyphoonDefinition.withClass(PaymentApproveViewController.self) {
$0.injectProperty("assembly", with: self)
}
}
public dynamic func nemIdStoryboard() -> AnyObject {
return TyphoonDefinition.withClass(TyphoonStoryboard.self) {
$0.useInitializer("storyboardWithName:factory:bundle:") {
$0.injectParameterWith("NemID")
$0.injectParameterWith(TyphoonBlockComponentFactory(assembly:self))
$0.injectParameterWith(nil)
}
}
}
I have the exact same code for injecting the assembly into my RootViewController, which also retrieves a storyboard and instantiates a view controller the same way as above. But this never fails.
I cannot identify any reason to why this should fail occasionally, and not be consistent. But I have a feeling, that the assembly might not be properly initialized in Swift code using optional types. Could that be the case? Or can you suggest anything I can do to make this code work?
Edit:
I have printed out TyphoonComponentFactory._registry
from rootViewController and from PaymentApproveViewController. The interesting result is that:
- In RootViewController, the
_registry
contains a list of all typhoon definitions in my ApplicationAssembly. That is, all objects and storyboard definitions
- In PaymentApproveViewController, the
_registry
contains all objects except my five storyboards. This is the reason that TyphoonComponentFactory.componentForKey throws an exception when it does not find the storyboard.
BTW: The address of the assembly is different in rootViewController
and PaymentApproveViewController
. So it is two different assemblies that are injected. Can this be avoided, or is it expected behavior?
Source: (StackOverflow)
Let say, I have the code below
self.customObj = self.assembly.customObj() as? NSObject
let temp3 = self.assembly.customObj() as NSObject
If I use TyphoonScopeObjectGraph
for customObj
, it should return the same instance.
But when I debug, the customObj
properties are not the same as shown:
As far as I understand, customObj
and temp3
should be the same instance. But as you see in the image, customObj
and temp3
have the same ObjectiveC.NSObject
address but all of its properties (_shortFormatter
, _longFormatter
) have different address. What happen? How we can get the same instance for customObj
and temp3
. An example is very helpful.
Thanks.
You can get the project source code from here
Source: (StackOverflow)
Use case:
I have an ApplicationAssembly. This assembly has a property called CoreAssembly of type TyphoonAssembly. I'm using PLIST integration and have registered both Assemblies. I'm injecting the ApplicationAssembly into the Appdelegate.
Afterwads in a ViewController, I want to get an object which will be generated by CoreAssembly
var appdelegate = UIApplication.sharedApplication().delegate as! AppDelegate;
tagHandler = appdelegate.assembly.coreAssembly.tagHandler() as! GoogleTagsHandler;
If I do this, I get an EXC_BAD_ACCESS
Source: (StackOverflow)
I am having problem implementing defaultAssembly() for my swift application. I need to access one of the dependencies directly from a legacy code.
The application is fully typhoon integrated - with Plist initialization and Storyboards.
The first problem was to set the assembly as default one because it's created automatically from plist. In the end I just made it default after activation. I don't know whether it's a correct way but it appears to be working.
public override func activate() -> AppAssembly! {
var instance = super.activate() as! AppAssembly
instance.makeDefault()
return instance
}
The real problem is when I retrieve it like this:
var assembly = TyphoonAssembly.defaultAssembly() as! AppAssembly
I get an error:
Could not cast value of type 'TyphoonBlockComponentFactory' (0x10f78bc40) to 'AppAssembly'
How should I then retrieve it? Or do I set the default assembly wrong?
Thanks
Tomas
Source: (StackOverflow)
When my application starts I fetch a remote config file containing information (URLs, etc) required to configure other dependencies.
After I fetch the remote config I have a Config
object that I need to supply to other TyphoonDefinition
s.
Now I am also using the plist storyboard integration.
I was originally going down the path of injecting the assembly into the ViewController that loads the Config object, and when I receive the remote config and create the Config object, I would somehow set it as a property on the assembly. I did this hoping that I could then use the property in the definitions, but this did not work and I got:
2014-10-22 21:18:06.203 four[39840:516543] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'No component matching id 'setConfig:'.'
*** First throw call stack:
(
0 CoreFoundation 0x000000010a3e63f5 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010a07fbb7 objc_exception_throw + 45
2 CoreFoundation 0x000000010a3e632d +[NSException raise:format:] + 205
3 four 0x00000001070a011d -[TyphoonComponentFactory componentForKey:args:] + 148
4 CoreFoundation 0x000000010a2de22c __invoking___ + 140
5 CoreFoundation 0x000000010a2de082 -[NSInvocation invoke] + 290
6 CoreFoundation 0x000000010a36d456 -[NSInvocation invokeWithTarget:] + 54
7 four 0x000000010709d358 -[TyphoonBlockComponentFactory forwardInvocation:] + 276
Is there any way for me to inject an object into an assembly at runtime?
Is there a cleaner way to do what I'm trying to do?
I was reading about run-time arguments which sounds like what I need, but I really don't understand the docs.
For example, I have this as a definition. I need to pass the runtime Config object as a parameter to the constructor.
- (id<ApiService>)apiService
{
return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {}];
}
Source: (StackOverflow)
I'm using typhoon with 'Plist integration'
I've defined the AppDelegate
as follows inside an assembly:
- (AppDelegate *)appDelegate {
return [TyphoonDefinition withClass:[AppDelegate class] configuration:^(TyphoonDefinition *definition) {
[definition injectProperty:@selector(window)];
definition.scope = TyphoonScopeSingleton;
}];
}
Inside window
, I have a rootViewController
with a delegate
that it's implemented by AppDelegate
.
- (RootViewController *)rootViewController {
return [TyphoonDefinition withClass:[RootViewController class] configuration:^(TyphoonDefinition *definition) {
[definition injectProperty:@selector(delegate)];
}];
}
The problem is that the delegate
is set with another instance of AppDeleaate
. I've set a breakpoint inside the AppDelegate
init
and indeed it's called twice.
I know that a solution would be to manually set the delegate
inside the AppDelegate
during runtime, but I would like this to be handled by typhoon.
Note: I haven't tried it but this same thing may happen with view controllers created by storyboards.
Source: (StackOverflow)
Using Typhoon and Swift, I am setting up my project and I have this problem. I have a class TPLAddInteractor
this way
class TPLAddInteractor: NSObject, TPLAddInteractorInput {
var output: TPLAddInteractorOutput?
var dataManager: TPLDataManagerInterface?
}
My assembly looks like this
class TPLAddAssembly: TyphoonAssembly {
var applicationAssembly: TPLApplicationAssembly?
dynamic func addInteractor() -> AnyObject {
return TyphoonDefinition.withClass(TPLAddInteractor.self) {
(definition) in
definition.injectProperty("output", with: self.addPresenter())
definition.injectProperty("dataManager", with: self.applicationAssembly?.dataManager())
}
}
dynamic func addPresenter() -> AnyObject {
return TyphoonDefinition.withClass(TPLAddPresenter.self) {
(definition) in
definition.injectProperty("interactor", with: self.addInteractor())
}
}
}
And then I receive this error right after running the app:
reason: 'Can't inject property 'dataManager' for object '<TPL.TPLAddInteractor: 0x7ff5b2d2bcf0>'. Setter selector not found. Make sure that property exists and writable'
I am reading the Swift example of Typhoon and I don't see anything unusual in my code. But I am new on Swift so maybe I'm missing something.
Thanks
Source: (StackOverflow)