I have created a project to test the Typhoon framework , I have created two classes ApplicationAssembly and CoreAssembly where I inject some properties and constructors and a default Configuration.plist to load data from it.
ApplicationAssembly
public class ApplicationAssembly: TyphoonAssembly {
public dynamic func config() -> AnyObject {
return TyphoonDefinition.configDefinitionWithName("Config.plist")
}
}
CoreAssembly
public class CoreAssembly: TyphoonAssembly {
public dynamic func apiHandler() -> AnyObject {
return TyphoonDefinition.withClass(ApiHandler.self) {
(definition) in
definition.useInitializer("initWithDebugging:debugProcess:mainURL:") {
(initializer) in
initializer.injectParameterWith(TyphoonConfig("debug_mode"))
initializer.injectParameterWith(TyphoonConfig("debug_path"))
initializer.injectParameterWith(TyphoonConfig("api_url"))
}
definition.scope = TyphoonScope.Singleton
}
}
public dynamic func viewController() -> AnyObject {
return TyphoonDefinition.withClass(ViewController.self) {
(definition) in
definition.injectProperty("apiHandler", with:self.apiHandler())
}
}
}
I set in my Info.plist the TyphoonInitialAssemblies first the ApplicationAssembly and then the CoreAssembly.
Everything works fine without exceptions or anything except that the app never enters in AppDelegate neither in the ViewController class. I don't know maybe I missed something in the doc or anything.
What I'm missing here?
Why in debug not enter in the ViewController class that is the initial view controller in Storyboard?
The problem was that the ApiHandler class does not extend NSObject, which is a requirement. This is because Typhoon is an introspective Dependency Injection container. As Swift has no native introspection it uses the Objective-C run-time.
The App should not however have crashed in such an obfuscated way. I have opened an issue to look at how to fail with a meaningful error, rather than infinitely recurse.
After solving the initial problem, I also noted that the init method for ApiHandler passing in a Swift Bool object. This needs to be an NSNumber.
init(debugging : NSNumber, debugProcess : String, mainURL : String) {
self.debugging = debugging.boolValue
self.debugProcess = debugProcess
self.mainURL = mainURL
}
Given that Typhoon uses the Objective-C runtime, there are a few quirks to using it with Swift - the same kinds of rules outlined for using Swift with KVO apply.
Related
I tend to only put the necessities (stored properties, initializers) into my class definitions and move everything else into their own extension, kind of like an extension per logical block that I would group with // MARK: as well.
For a UIView subclass for example, I would end up with an extension for layout-related stuff, one for subscribing and handling events and so forth. In these extensions, I inevitably have to override some UIKit methods, e.g. layoutSubviews. I never noticed any issues with this approach -- until today.
Take this class hierarchy for example:
public class C: NSObject {
public func method() { print("C") }
}
public class B: C {
}
extension B {
override public func method() { print("B") }
}
public class A: B {
}
extension A {
override public func method() { print("A") }
}
(A() as A).method()
(A() as B).method()
(A() as C).method()
The output is A B C. That makes little sense to me. I read about Protocol Extensions being statically dispatched, but this ain't a protocol. This is a regular class, and I expect method calls to be dynamically dispatched at runtime. Clearly the call on C should at least be dynamically dispatched and produce C?
If I remove the inheritance from NSObject and make C a root class, the compiler complains saying declarations in extensions cannot override yet, which I read about already. But how does having NSObject as a root class change things?
Moving both overrides into their class declaration produces A A A as expected, moving only B's produces A B B, moving only A's produces C B C, the last of which makes absolutely no sense to me: not even the one statically typed to A produces the A-output any more!
Adding the dynamic keyword to the definition or an override does seem to give me the desired behavior 'from that point in the class hierarchy downwards'...
Let's change our example to something a little less constructed, what actually made me post this question:
public class B: UIView {
}
extension B {
override public func layoutSubviews() { print("B") }
}
public class A: B {
}
extension A {
override public func layoutSubviews() { print("A") }
}
(A() as A).layoutSubviews()
(A() as B).layoutSubviews()
(A() as UIView).layoutSubviews()
We now get A B A. Here I cannot make UIView's layoutSubviews dynamic by any means.
Moving both overrides into their class declaration gets us A A A again, only A's or only B's still gets us A B A. dynamic again solves my problems.
In theory I could add dynamic to all overrides I ever do but I feel like I'm doing something else wrong here.
Is it really wrong to use extensions for grouping code like I do?
Extensions cannot/should not override.
It is not possible to override functionality (like properties or methods) in extensions as documented in Apple's Swift Guide.
Extensions can add new functionality to a type, but they cannot override existing functionality.
Swift Developer Guide
The compiler is allowing you to override in the extension for compatibility with Objective-C. But it's actually violating the language directive.
😊That just reminded me of Isaac Asimov's "Three Laws of Robotics" 🤖
Extensions (syntactic sugar) define independent methods that receive their own arguments. The function that is called for i.e. layoutSubviews depends on the context the compiler knows about when the code is compiled. UIView inherits from UIResponder which inherits from NSObject so the override in the extension is permitted but should not be.
So there's nothing wrong with grouping but you should override in the class not in the extension.
Directive Notes
You can only override a superclass method i.e. load() initialize()in an extension of a subclass if the method is Objective-C compatible.
Therefore we can take a look at why it is allowing you to compile using layoutSubviews.
All Swift apps execute inside the Objective-C runtime except for when using pure Swift-only frameworks which allow for a Swift-only runtime.
As we found out the Objective-C runtime generally calls two class main methods load() and initialize() automatically when initializing classes in your app’s processes.
Regarding the dynamic modifier
From the Apple Developer Library (archive.org)
You can use the dynamic modifier to require that access to members be dynamically dispatched through the Objective-C runtime.
When Swift APIs are imported by the Objective-C runtime, there are no guarantees of dynamic dispatch for properties, methods, subscripts, or initializers. The Swift compiler may still devirtualize or inline member access to optimize the performance of your code, bypassing the Objective-C runtime. 😳
So dynamic can be applied to your layoutSubviews -> UIView Class since it’s represented by Objective-C and access to that member is always used using the Objective-C runtime.
That's why the compiler allowing you to use override and dynamic.
One of the goals of Swift is static dispatching, or rather the reduction of dynamic dispatching. Obj-C however is a very dynamic language. The situation you're seeing is borne out of the link between the 2 languages and the way they work together. It shouldn't really compile.
One of the main points about extensions is that they are for extending, not for replacing / overriding. It's clear from both the name and the documentation that this is the intention. Indeed if you take out the link to Obj-C from your code (remove NSObject as the superclass) it won't compile.
So, the compiler is trying to decide what it can statically dispatch and what it has to dynamically dispatch, and it's falling through a gap because of the Obj-C link in your code. The reason dynamic 'works' is because it's forcing Obj-C linking on everything so it's all always dynamic.
So, it isn't wrong to use extensions for grouping, that's great, but it is wrong to override in extensions. Any overrides should be in the main class itself, and call out to extension points.
There is a way to achieve a clean separation of class signature and implementation (in extensions) while maintaining the ability to have overrides in subclasses. The trick is to use variables in place of the functions
If you make sure to define each subclass in a separate swift source file, you can use computed variables for the overrides while keeping the corresponding implementation cleanly organized in extensions. This will circumvent Swift's "rules" and will make your class's API/signature neatly organized in one place:
// ---------- BaseClass.swift -------------
public class BaseClass
{
public var method1:(Int) -> String { return doMethod1 }
public init() {}
}
// the extension could also be in a separate file
extension BaseClass
{
private func doMethod1(param:Int) -> String { return "BaseClass \(param)" }
}
...
// ---------- ClassA.swift ----------
public class A:BaseClass
{
override public var method1:(Int) -> String { return doMethod1 }
}
// this extension can be in a separate file but not in the same
// file as the BaseClass extension that defines its doMethod1 implementation
extension A
{
private func doMethod1(param:Int) -> String
{
return "A \(param) added to \(super.method1(param))"
}
}
...
// ---------- ClassB.swift ----------
public class B:A
{
override public var method1:(Int) -> String { return doMethod1 }
}
extension B
{
private func doMethod1(param:Int) -> String
{
return "B \(param) added to \(super.method1(param))"
}
}
Each class's extension are able to use the same method names for the implementation because they are private and not visible to each other (as long as they are in separate files).
As you can see inheritance (using the variable name) works properly using super.variablename
BaseClass().method1(123) --> "BaseClass 123"
A().method1(123) --> "A 123 added to BaseClass 123"
B().method1(123) --> "B 123 added to A 123 added to BaseClass 123"
(B() as A).method1(123) --> "B 123 added to A 123 added to BaseClass 123"
(B() as BaseClass).method1(123) --> "B 123 added to A 123 added to BaseClass 123"
This answer it not aimed at the OP, other than the fact that I felt inspired to respond by his statement, "I tend to only put the necessities (stored properties, initializers) into my class definitions and move everything else into their own extension ...". I'm primarily a C# programmer, and in C# one can use partial classes for this purpose. For example, Visual Studio places the UI-related stuff in a separate source file using a partial class, and leaves your main source file uncluttered so you don't have that distraction.
If you search for "swift partial class" you'll find various links where Swift adherents say that Swift doesn't need partial classes because you can use extensions. Interestingly, if you type "swift extension" into the Google search field, its first search suggestion is "swift extension override", and at the moment this Stack Overflow question is the first hit. I take that to mean that problems with (lack of) override capabilities are the most searched-for topic related to Swift extensions, and highlights the fact that Swift extensions can't possibly replace partial classes, at least if you use derived classes in your programming.
Anyway, to cut a long-winded introduction short, I ran into this problem in a situation where I wanted to move some boilerplate / baggage methods out of the main source files for Swift classes that my C#-to-Swift program was generating. After running into the problem of no override allowed for these methods after moving them to extensions, I ended up implementing the following simple-minded workaround. The main Swift source files still contain some tiny stub methods that call the real methods in the extension files, and these extension methods are given unique names to avoid the override problem.
public protocol PCopierSerializable {
static func getFieldTable(mCopier : MCopier) -> FieldTable
static func createObject(initTable : [Int : Any?]) -> Any
func doSerialization(mCopier : MCopier)
}
.
public class SimpleClass : PCopierSerializable {
public var aMember : Int32
public init(
aMember : Int32
) {
self.aMember = aMember
}
public class func getFieldTable(mCopier : MCopier) -> FieldTable {
return getFieldTable_SimpleClass(mCopier: mCopier)
}
public class func createObject(initTable : [Int : Any?]) -> Any {
return createObject_SimpleClass(initTable: initTable)
}
public func doSerialization(mCopier : MCopier) {
doSerialization_SimpleClass(mCopier: mCopier)
}
}
.
extension SimpleClass {
class func getFieldTable_SimpleClass(mCopier : MCopier) -> FieldTable {
var fieldTable : FieldTable = [ : ]
fieldTable[376442881] = { () in try mCopier.getInt32A() } // aMember
return fieldTable
}
class func createObject_SimpleClass(initTable : [Int : Any?]) -> Any {
return SimpleClass(
aMember: initTable[376442881] as! Int32
)
}
func doSerialization_SimpleClass(mCopier : MCopier) {
mCopier.writeBinaryObjectHeader(367620, 1)
mCopier.serializeProperty(376442881, .eInt32, { () in mCopier.putInt32(aMember) } )
}
}
.
public class DerivedClass : SimpleClass {
public var aNewMember : Int32
public init(
aNewMember : Int32,
aMember : Int32
) {
self.aNewMember = aNewMember
super.init(
aMember: aMember
)
}
public class override func getFieldTable(mCopier : MCopier) -> FieldTable {
return getFieldTable_DerivedClass(mCopier: mCopier)
}
public class override func createObject(initTable : [Int : Any?]) -> Any {
return createObject_DerivedClass(initTable: initTable)
}
public override func doSerialization(mCopier : MCopier) {
doSerialization_DerivedClass(mCopier: mCopier)
}
}
.
extension DerivedClass {
class func getFieldTable_DerivedClass(mCopier : MCopier) -> FieldTable {
var fieldTable : FieldTable = [ : ]
fieldTable[376443905] = { () in try mCopier.getInt32A() } // aNewMember
fieldTable[376442881] = { () in try mCopier.getInt32A() } // aMember
return fieldTable
}
class func createObject_DerivedClass(initTable : [Int : Any?]) -> Any {
return DerivedClass(
aNewMember: initTable[376443905] as! Int32,
aMember: initTable[376442881] as! Int32
)
}
func doSerialization_DerivedClass(mCopier : MCopier) {
mCopier.writeBinaryObjectHeader(367621, 2)
mCopier.serializeProperty(376443905, .eInt32, { () in mCopier.putInt32(aNewMember) } )
mCopier.serializeProperty(376442881, .eInt32, { () in mCopier.putInt32(aMember) } )
}
}
Like I said in my introduction, this doesn't really answer the OP's question, but I'm hoping this simple-minded workaround might be helpful to others who wish to move methods from the main source files to extension files and run into the no-override problem.
Use POP (Protocol-Oriented Programming) to override functions in extensions.
protocol AProtocol {
func aFunction()
}
extension AProtocol {
func aFunction() {
print("empty")
}
}
class AClass: AProtocol {
}
extension AClass {
func aFunction() {
print("not empty")
}
}
let cls = AClass()
cls.aFunction()
Just wanted to add that for Objective-C classes, two separate categories can end up overwriting the same method, and it this case... well... unexpected things can happen.
The Objective-C runtime doesn't make any guarantees about which extension will be used, as described by Apple here:
If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes.
It's a good thing Swift prohibits this for pure Swift classes, since this kind of overly-dynamic behaviour is a potential source of hard to detect and investigate bugs.
I have a problem with Typhoon dependency injection framework.
My viewcontroller MainViewController depends on dataProvider property that I want to declare as AnyObject corresponding to protocol DataProviderProtocol
class MainViewController: UIViewController {
// Compiler error here
var dataProvider : DataProviderProtocol!
// something more
}
protocol DataProviderProtocol {
func fetchAllBanks(closure : ([BankObject]) -> Void)
}
class TestDataProvider: NSObject, CEDataProviderProtocol {
func fetchAllBanks(closure : ([CEBankObject]) -> Void) {
var resultBanks = ///////code that creates test data
closure(resultBanks);
}
I want this dataProvider property to be injected by the Typhoon and initialized to the corresponding instance of class TestDataProvider, that implements this protocol. But I also have RealDataProvider that also corresponds to the DataProviderProtocol and might be used sometimes
But this code crashes with the message
Can't inject property 'dataProvider' for object
''. Setter selector not
found. Make sure that property exists and writable'
I can inject this property without crashes if I use the property class of TestDataProvider, but this disables the ability to inject different DataProviderProtocol implementations.
I understand this this crash happens because DataProviderProtocol property type is not NSObject successor. But I just can't find a way to declare property as NSObject<DataProviderProtocol> in Swift
I would appreciate any help
P.S. My Assembly class
public class CEAppAssembly:TyphoonAssembly {
//uiviewcontrollers' components assembly
var mainComponentsAssembly : CEMainComponentsAssembly!
/**
UI Dependencies
**/
public dynamic func mainViewController() -> AnyObject {
return TyphoonDefinition.withClass(MainViewController.self) {
(definition) in
definition.injectProperty("dataProvider", with: self.mainComponentsAssembly.dataProvider())
}
}
Typhoon uses the Objective-C run-time introspection and dynamic features. Therefore:
Protocols must be marked with the '#objc' directive.
Types incompatible with Objective-C (ie 'pure swift') can't be injected.
There's more information about this in the Quick Start guide. If you're still having trouble after reviewing and making those changes, let us know.
We plan to release a Pure Swift version of Typhoon in the near future.
I have class:
class InformationTableViewController: UITableViewController {
private var cos: Int!
}
And I'm trying to inject property:
public dynamic func informationTableViewController() -> AnyObject {
return TyphoonDefinition.withClass(InformationTableViewController.self) {
(definition) in
definition.injectProperty("cos", with: 3)
}
}
When it's a simple class it works normal. But when I use InformationTableViewController on Storyboard (as some view class) I'm getting error:
'Can't inject property 'cos' for object 'Blah.InformationTableViewController: 0x7fca3300afe0'. Setter selector not found. Make sure that property exists and writable'
What's the problem?
Private access modifier restricts the use of an entity to its own defining source file.
So one problem is that you are trying to set your property from outside of it private scope. Remove private keyword from property declaration.
Another problem here is that you are trying to inject primitive type.
In Obj-C Typhoon has support of injecting primitive types but not in Swift yet.
Every class you want to inject has to be a subclass of NSObject in some way (either by subclassing or adding #objc modifier).
As a workaround you may use NSNumber instead of an Int type for your property.
class InformationTableViewController: UITableViewController {
var cos: NSNumber!
}
Assembly:
public dynamic func informationTableViewController() -> AnyObject {
return TyphoonDefinition.withClass(InformationTableViewController.self) {
(definition) in
definition.injectProperty("cos", with: NSNumber.init(int: 3))
}
}
In swift I'm implementing two protocols, GADCustomEventInterstitial and GADCustomEventBanner.
Both of these protocols require a property called delegate. delegate is a different type in each protocol, and thus a conflict arises.
class ChartBoostAdapter : NSObject, GADCustomEventInterstitial, GADCustomEventBanner, ChartboostDelegate{
var delegate:GADCustomEventInterstitialDelegate?; // Name conflict
var delegate:GADCustomEventBannerDelegate?; // Name conflict
override init(){
}
...
}
They are libraries/frameworks it's not my definition
Then obviously you cannot make the same class adopt both protocols. But you don't really need to. Just separate this functionality into two different classes, as is evidently intended by the designer of these protocols. You are supposed to have one class that adopts GADCustomEventInterstitial and has its delegate, and another class that adopts GADCustomEventBanner and has its delegate. What reason do you have for trying to force these to be one and the same class? As in all things where you are using a framework, don't fight the framework, obey it.
It is actually possible, I just encountered same situation. I had two different but kind of related protocols. In some cases I needed both to be implemented by delegate and in other cases only one and I didn't want to have two properties eg... delegate1, delegate2.
What you need to do is create another combined protocol that inherits from both protocols:
protocol ChartBoostAdapterDelegate: GADCustomEventInterstitialDelegate, GADCustomEventBannerDelegate { }
class ChartBoostAdapter : NSObject, GADCustomEventInterstitial, GADCustomEventBanner, ChartboostDelegate {
weak var delegate: ChartBoostAdapterDelegate?
override init(){
}
...
}
The simple answer is that you can't.
Maybe one protocol depends on another, in which case you would use the dependent protocol for the type of your delegate.
Note that this can be solved using Mixins (possible since Swift 2.0) if you are in a Swift-only environment. It just cannot be solved as long as you need to have the code bridged to Obj-C, as this problem is unsolvable in Obj-C. Yet that can usually be solved by a wrapper class, which I will show later on.
Let's break this down to a minimalist example:
import Foundation
#objc
protocol ProtoA {
var identifier: String { get }
}
#objc
protocol ProtoB {
var identifier: UUID { get }
}
#objc
class ClassA: NSObject, ProtoA, ProtoB {
let identifier = "ID1"
let identifier = UUID()
}
The code above will fail as no two properties can have the same name. If I only declare identifier once and make it a String, compiler will complain that ClassA does not conform to ProtoB and vice verse.
But here is Swift-only code that actually does work:
import Foundation
protocol ProtoA {
var identifier: String { get }
}
protocol ProtoB {
var identifier: UUID { get }
}
class ClassA {
let stringIdentifier = "ID1"
let uuidIdentifier = UUID()
}
extension ProtoA where Self: ClassA {
var identifier: String {
return self.stringIdentifier
}
}
extension ProtoB where Self: ClassA {
var identifier: UUID {
return self.uuidIdentifier
}
}
extension ClassA: ProtoA, ProtoB { }
Of course, you cannot do that:
let test = ClassA()
print(test.identifier)
The compiler will say ambigous use of 'identifier', as it has no idea which identifier you want to access but you can do this:
let test = ClassA()
print((test as ProtoA).identifier)
print((test as ProtoB).identifier)
and the output will be
ID1
C3F7A09B-15C2-4FEE-9AFF-0425DF66B12A
as expected.
Now to expose a ClassA instance to Obj-C, you need to wrap it:
class ClassB: NSObject {
var stringIdentifier: String { return self.wrapped.stringIdentifier }
var uuidIdentifier: UUID { return self.wrapped.uuidIdentifier }
private let wrapped: ClassA
init ( _ wrapped: ClassA )
{
self.wrapped = wrapped
}
}
extension ClassA {
var asObjCObject: ClassB { return ClassB(self) }
}
If you put it directly into the class declaration of ClassA, you could even make it a stored property, that way you don't have to recreate it ever again but that complicates everything as then ClassB may only hold a weak reference to the wrapped object, otherwise you create a retain cycle and neither of both objects will ever be freed. It's better to cache it somewhere in your Obj-C code.
And to solve your issue, one would use a similar wrapper approach by building a master class and this master class hands out two wrapper class, one conforming to GADCustomEventInterstitial and one conforming to GADCustomEventBanner but these would not have any internal state or logic, they both use the master class as storage backend and pass on all requests to this class that implements all required logic.
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
To work with Typhoon, it's necessary for Swift protocols to have the '#objc' directive. This is because Typhoon uses the Objective-C runtime as Swift has no native dynamism as yet.
This requirement is documented in the Swift Quick Start.
//Inject as follows it will give a warning but its working for me:
definition?.injectProperty(Selector(("cityInfo")), with: self.coreAssembly.cityInfo())