-
Notifications
You must be signed in to change notification settings - Fork 268
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
Injection in Swift works only with subclasses of NSObject #267
Comments
Hi Sebastian, Yes that's right. The nature of Swift (version 1.0) is: If a class extends
One shortcoming of this functionality is support for Swift optional value types. For example Int properties can be enumerated and modified but Int? properties cannot. Optional types can be enumerated partially using reflect/MirrorType, but still not modified. If a class does not extend When not extending NSObject, or using the '@objc' directive, Swift defaults to static- and vtable-based dispatch. This is faster, however, in the absence of a virtual machine does not allow runtime method interception. This interception is a fundamental part of Cocoa and is required for the following types of features:
Therefore its recommended that clases in Cocoa/CocoaTouch applications implemented with Swift:
Summary:
Reference data: Execution overhead for method invocations:
(actual performance depends on hardware, but the ratios will remain similar). Also, the dynamic attribute allows us to explicitly instruct Swift that a method should use dynamic dispatch, and will therefore support interception.
So this means:
Fortunately for Cocoa applications the majority of classes are UIViewController, etc. By the time this is not the case, Swift must surely support interception and reflection as these are the foundation of Cocoa's power and without these, a successor would be anemic in comparison. . . not that the focus is shifting though from runtime to the compiler (ala ARC). |
Ok, so for a class to be injectable in Swift, it has to subclass NSObject or something below and if it is supposed to be injected for a protocol property (as in One thing I just found out: I had the following initializer in a class that got injected:
By the time the initializer was called, all properties that should have been injected were nil. Calling the setup methods above in the The User Guide says you CAN use the default initializer if you can modify the class. But the default initializer in Swift seems to be called much earlier than the injection? |
initializers get called before property injection. This is one of the advantages of using intializer injection (called constructor injection in other languages) which you can do with Typhoon as follows: definition.useInitializer("initWithMainContentViewController:assembly:") {
(initializer) in
initializer.injectParameterWith(self.weatherReportController())
initializer.injectParameterWith(self)
} So its possible to ensure the instance is in a required state before proceeding. We can get most of the way there with property injection, using either definition.beforeInjections = "someMethod" |
As for protocols, unfortunately they need to have the '@objc' directive added, as in https://github.com/typhoon-framework/Typhoon-Swift-Example/blob/master/PocketForecast/Client/WeatherClient.swift |
Alright, thank you very much, I think I get it. Would you mind if I extend the Swift Guide a little to point out the Objective-C "dependency"? |
That would excellent. Its worth noting that the same rules apply as those in Apple's guide on using KVO with Swift. Unfortunately for "dependency injection on rails" that's as far as we're going to get until Swift has native reflection and introspection. Other alternatives:
|
Ok, I extended the Swift Quick Start a little with everything I learned and every pitfall I stepped into while using Typhoon the first time. Hope that helps other people getting started. |
Looks great! 👍 |
So I just spent a day trying to implement the Typhoon lib in my existing app, but I just couldn't get it to work. Whenever I activated a TyphoonDefinition for one class, the app never got to
didFinishLaunchingWithOptions
method.I made a minimal app example with nothing more than an empty class with one function that has an empty body. And then it came to me ... what if it has to be a subclass of
NSObject
.And voilà: It finally worked. So plain Swift objects cannot be injected (yet), is that correct?
The text was updated successfully, but these errors were encountered: