43

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?

gotnull
  • 26,454
  • 22
  • 137
  • 203
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
  • what is the problem with the _generic_ way? – holex Jun 27 '14 at 15:42
  • I believe the code I posted here should work, if it didn't crash the compiler. I wasn't able to find a way to use the type as a variable, unless it's stored as `var filter: T` in a generic class. http://openradar.appspot.com/radar?id=5258642664194048 – wjl Jul 03 '14 at 22:53
  • @wjl I have noticed the same problems consistently occurring with generics. Xcode will compile the code fine but the program will crash as soon as the class is used. Did apple reply at all to your bug report? – Zag Jul 06 '14 at 12:56
  • @Zag: No response yet (I'll probably get a "hey, is this still broken?" for beta 3). Do you have a test case / openradar for your issue? – wjl Jul 07 '14 at 02:02

3 Answers3

23

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

LombaX
  • 17,265
  • 5
  • 52
  • 77
  • Yes, this does appear to work well for functions (and would probably be a better mapping for Clang when bringing the Objective-C header into Swift). However, is it possible to use these generics for properties and parameters within enums? I tried to do this now and the compiler is telling me that "only syntactic function types can be generic". Maybe I'm getting the syntax on them wrong. – Brad Larson Jun 27 '14 at 15:54
  • If I understand what you are asking, it's possible. I attach an example from the book at the end of my answer – LombaX Jun 27 '14 at 15:56
  • While it's not specifically what I'd asked above, I guess I'm just trying to cover all the cases where I would have used a GPUImageOutput type in Objective-C. Functions are one case, but another would be for a property of that type. Can you still use this style of generic for a property? I guess I could split that off as a separate question. – Brad Larson Jun 27 '14 at 16:05
  • Yes you still can use generic with enums / struct the same way : `enum SomeEnum {}`. Then just declare a property inside this enum with the type of `T` – Yaman Jun 27 '14 at 16:15
  • @BradLarson, you can use generics even with a property, I attach another example – LombaX Jun 27 '14 at 16:28
  • After working with this for a bit, I'm not sure the use of generics can fully replicate Class for properties. The problem is that you need to know at compile time what class you're going to use for your property. There's no way to allow for a property that can be have various classes set to it at runtime, with the only stipulation being that they are both subclasses of a particular class and that they comply to a particular protocol. Also, it doesn't appear you can use a generic type in something like a UIViewController that has to interact with a UI via IBOutlets. – Brad Larson Jun 30 '14 at 00:20
13

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

  • which is based on GPUImageOutput class; and
  • conforms GPUImageInput protocol; and
  • the newFilter is optional, it can be nil;
holex
  • 23,961
  • 7
  • 62
  • 76