I am trying to make a simple Dependency Injection system for our app in swift, for 2 day now. I'm flexible to whatever solution but I would like something so I can say "give me a instance of something that conforms to this protocol" and the actual type returned can be whatever as long as it conforms to the said protocol. I have tried a great many thing including generics but managed to figure out that that can not(?) really work so now I'm down to the bare basics, something like this:
protocol AProtocol {
}
class AClass: AProtocol {
}
class MyDiThing {
public static func objectConformingTo(aProtocol: Any) -> Any? {
// And here I want to do something like
if AClass is aProtocol {
return AClass()
}
return nil
}
}
// The calling code ..
let aObject = MyDIThing.objectConformingTo(AProtocol)
It's not beautiful, I know, but right now i'm not that picky about performance/bad code as long as it solves the decoupling problem (and preferably can be contained in the MyDIThing class). If this is impossible in swift I'm open to other solutions. I have used similar solutions with objective-c with good success, just having a dictionary with keys being NSStringFromProtocol and values being the class, subscripting the dictionary with the inbound protocol and instantiating the class. Super simple. In swift it feels impossible!
The comment given by nhgrif is the correct answer for Swift 2. You should use an optional binding:
if let aObjectWithAProtocol = aObject as? AProtocol {
// object conforms to protocol
someObject.someFunction(aObjectWithAProtocol)
} else {
// object does not conform to protocol
}
This if let something = obj as? type
statement is called optional binding and checks if the object can be type-casted to the given type/class/protocol/....
If so, you can use the optional (as?
) or force unwrap (as!
) the object.
If you import obj-c then you can do something like you used to.
Otherwise, it's hard because protocols don't exist in the same way. Consider a registration based system for your factory. Each of your classes would register themselves by supplying a function or closure that can be called to return a new instance of that class, and the registration is against a string or some other type of identifier. This is where it would be good to have a protocol type, but in obj-c you were really doing the same thing with a string conversion. You could register against anything that is Equatable
to keep things very generic.
This function will perfectly meet your requirement:
bool _swift_typeIsTargetType(id sourceType, id targetType)
It can check whether a swift type is the target type (is same type or subclass, or conforms to the target protocol), works like is
operator in swift. But it's for a type, not for an instance:
let sourceType: Any.Type = type(of: self)
let targetType: Any.Type = AnyProtocol.self
let result = _swift_typeIsTargetType(sourceType, targetType)
It uses private APIs in libswiftCore.dylib
, See swift source code Casting.cpp:
bool _conformsToProtocols(const OpaqueValue *value, const Metadata *type, const ExistentialTypeMetadata *existentialType, const WitnessTable **conformances)
Optional binding is not same. It can also check conformance, but you can't dynamically give the type to check:
let targetType: Any.Type = AnyProtocol.self
//error: use of undeclared type: 'targetType'
if let _ = aObject as? targetType {
// object conforms to protocol
} else {
// object does not conform to protocol
}
This function is used in ZIKRouter. It's a module router and also a Dependency Injection framework. It uses protocol to discover modules and inject dependencies. You can try it if you want to do something like 'finding a module conforming to some protocol'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With