Reputation: 5618
How do you specify that a generic type parameter can only be a protocol (or a protocol conforming to that protocol), and not a class conforming to that protocol?
For example:
import Foundation
@objc protocol MyProtocol {
var name: String { get }
}
@objc protocol MySubProtocol: MyProtocol {
func foo()
}
class MyImplementation: MySubProtocol {
var name: String
init(name: String) {
self.name = name
}
func foo() {
print("Foo! Name: \(self.name)")
}
}
class InterfaceMaker<T: MyProtocol> {
init() {}
func makeInterface() -> NSXPCInterface {
return NSXPCInterface(with: T.self) // Cannot convert value of type 'T.Type' to expected argument type 'Protocol'
}
func getProxy() -> T? {
// Some magic in here involving `NSXPCConnection.remoteObjectProxy`
}
}
InterfaceMaker<MySubProtocol>().makeInterface() // No error, as expected
InterfaceMaker<MyImplementation>().makeInterface() // No error, but should not work!
How do I specify that a generic type parameter should be a protocol?
I want to constrain T
to only protocols conforming to MyProtocol
(such as MySubProtocol
). But the problem is, I don't know how to prevent T from being a class (such as MyImplementation
).
I already tried constraining T.self
(I was trying to use where T.self : Protocol
, but that caused the error 'self' is not a member type of 'T'
).
So how do I specify that T
must be a protocol conforming to MyProtocol
, but not a class? If that's impossible, can I at least specify that T should be any protocol? It's also OK if I need to make either a class-only protocol.
Passing MySubProtocol
as a non-generic parameter is not what I am looking for as I would also like to be able to use that protocol as a type for the InterfaceMaker.getProxy()
function. Additionally, having that function simply return MyProtocol
(in other words, having InterfaceMaker
be non-generic) is also not an option.
NOTE: To be fully clear, the reason why I need T
to be a protocol is that I am going to pass it to NSXPCInterface.init(with:)
, which takes a Protocol
(which can be obtained by SomeProtocol.self
if SomeProtocol
is @objc
). This means that SomeProtocol.self.Type
is or conforms to
If this is not possible, please give a full explanation why. Also mention if it would be possible for this to be supported in a future Swift version.
EDIT: Another way of phrasing this is that T.self is AnyObject.Type
should never be true. I would rather check this at compile time rather than a run time check or assertion.
Upvotes: 5
Views: 1334
Reputation: 2488
You can check if T
conforms to the protocol AnyObject
which all classes implicitly conform, but not protocols.
So, in InterfaceMaker
:
class InterfaceMaker<T: MyProtocol> {
init() {}
func makeInterface() -> NSXPCInterface {
if T.self is AnyObject.Type {
// T is a class
} else {
return NSXPCInterface(with: T)
}
}
func getProxy() -> T? {
// Some magic in here involving `NSXPCConnection.remoteObjectProxy`
}
}
Upvotes: 2