Getting the string representation of the protocol dynamically
I'm looking for a way to dynamically get the protocol name from a protocol type without using an attribute @objc
in the protocol declaration.
I know this works:
func keyForProtocol(aProtocol: Protocol) -> String {
return NSStringFromProtocol(aProtocol)
}
but only if the protocol has the attribute @obj
:
@objc protocol Test {}
var key = keyForProtocol(Test.self) // Key contains "TestApp.Test"
however, as soon as I remove the attribute @objc
, compilation fails with an error:
'Test.Protocol' is not convertible to 'Protocol'
Is there a way to achieve this?
Note: The reason I want to avoid @objc
is because it doesn't allow bound types (i.e. generics in the protocol) - in these cases, compilation fails with this error:Method cannot be marked @objc because the type of parameter xx cannot be represented in Objective-C
source to share
I recently found a solution by executing the method keyForProtocol
like this:
func keyForProtocol<P>(aProtocol: P.Type) -> String {
return ("\(aProtocol)")
}
It works with any type, not just protocols, but I'm fine with it.
Some examples from the playground:
protocol Test {}
keyForProtocol(Test.self) // Prints "__lldb_expr_92.Test"
class MyClass {}
keyForProtocol(MyClass.self) // Prints "__lldb_expr_92.MyClass"
keyForProtocol(Int.self) // Prints "Swift.Int"
keyForProtocol(UIView.self) // Prints "UIView"
source to share
As Antonio said:
let protocolDescription = "\(aProtocol)"
will return the (sometimes weird) protocol name.
I finally finished my task by converting this string to a protocol with:
let protocolReference = NSProtocolFromString(protocolDescription)
This method is ideal if you need to get the protocol type from a generic parameter, as in this example (lightweight / home dependency manager):
class DependencyManager<T> {
private static func inject(_ aProtocol: T.Type) -> T {
let application = UIApplication.shared.delegate as! LMApplication
let dependencies = application.applicationDependencies
let protocolReference = NSProtocolFromString("\(aProtocol)")
let dependency = dependencies!.object(for: protocolReference)
return dependency! as! T
}
}
Using:
let object: Protocol = DependencyManager.inject(Protocol.self)
source to share