Reputation: 170309
I'd like to implement a Swift method that takes in a certain class type, but only takes instances of those classes that comply to a specific protocol. For example, in Objective-C I have this method:
- (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter;
where GPUImageOutput
is a particular class, and GPUImageInput
is a protocol. Only GPUImageOutput
classes that comply to this protocol are acceptable inputs for this method.
However, the automatic Swift-generated version of the above is
func addFilter(newFilter: GPUImageOutput!)
This removes the requirement that GPUImageOutput
classes comply with the GPUImageInput
protocol, which will allow non-compliant objects to be passed in (and then crash at runtime). When I attempt to define this as GPUImageOutput<GPUImageInput>
, the compiler throws an error of
Cannot specialize non-generic type 'GPUImageOutput'
How would I do such a class and protocol specialization in a parameter in Swift?
Upvotes: 42
Views: 13146
Reputation: 151
From Swift 4 onwards you can do:
func addFilter(newFilter: GPUImageOutput & GPUImageInput)
Further reading: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
http://braking.github.io/require-conformance-to-multiple-protocols/
Multiple Type Constraints in Swift
Upvotes: 1
Reputation: 17364
Is swift you must use generics, in this way:
Given these example declarations of protocol, main class and subclass:
protocol ExampleProtocol {
func printTest() // classes that implements this protocol must have this method
}
// an empty test class
class ATestClass
{
}
// a child class that implements the protocol
class ATestClassChild : ATestClass, ExampleProtocol
{
func printTest()
{
println("hello")
}
}
Now, you want to define a method that takes an input parameters of type ATestClass (or a child) that conforms to the protocol ExampleProtocol. Write the method declaration like this:
func addFilter<T where T: ATestClass, T: ExampleProtocol>(newFilter: T)
{
println(newFilter)
}
Your method, redefined in swift, should be
func addFilter<T where T:GPUImageOutput, T:GPUImageInput>(newFilter:T!)
{
// ...
}
EDIT:
as your last comment, an example with generics on an Enum
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
Specialized with protocol conformance:
enum OptionalValue<T where T:GPUImageOutput, T:GPUImageInput> {
case None
case Some(T)
}
EDIT^2:
you can use generics even with instance variables:
Let's say you have a class and an instance variable, you want that this instance variable takes only values of the type ATestClass
and that conforms to ExampleProtocol
class GiveMeAGeneric<T: ATestClass where T: ExampleProtocol>
{
var aGenericVar : T?
}
Then instantiate it in this way:
var child = ATestClassChild()
let aGen = GiveMeAGeneric<ATestClassChild>()
aGen.aGenericVar = child
If child
doesn't conform to the protocol ExampleProtocol
, it won't compile
Upvotes: 22
Reputation: 24041
this method header from ObjC:
- (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter { ... }
is identical to this header in Swift:
func addFilter<T: GPUImageOutput where T: GPUImageInput>(newFilter: T?) { ... }
both method will accept the same set of classes
GPUImageOutput
class; andGPUImageInput
protocol; andnewFilter
is optional, it can be nil
;Upvotes: 13