EzDevInfo.com

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

Typhoon: Assembly and Storyboard-Created ViewControllers

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)

Can dependency injection like Typhoon replace multiple singletons?

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)

Advertisements

Typhoon: How to get an instance conforming to a protocol for production, and another for tests?

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)

dyld_fatal_error with Typhoon+Swift+iOS7.x+Plist-bootstrapping

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 :

iPhone 5s - iOS 7.1 error stack trace

I get slightly different stack trace from iPhone simulator (iOS 7.1) :

iOS 7.1 emulator error stack trace

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 :

  1. Removed TyphoonInitialAssemblies item from Info.plist
  2. 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)

Swift TyphoonBlockComponentFactory Error in XCTest

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)

Typhoon Storyboard Integration

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)

Swift: Cannot use library from Pod file

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)

Swift - patch definition with selector using run-time arguments

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)

Typhoon: Exception when retrieving storyboard from assembly

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)

How TyphoonScopeObjectGraph works

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: image

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)

Accessing sub assembly of an assembly will generate EXC_BAD_ACCESS

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)

Typhoon defaultAssembly() in Swift

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)

Typhoon - Runtime Configurable Components Using Storyboard

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 TyphoonDefinitions.

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)

Typhoon with singleton AppDelegate

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)

Can't inject property, setter selector not found

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)