5

I’m currently writing some Swift code in a project that is predominately Objective-C. In our ObjC code, we have a header that declares typedef GPUImageOutput<GPUImageInput> MyFilter;. We can then declare e.g. a @property that can only be a GPUImageOutput subclass that implements GPUImageInput.

(NOTE: GPUImageOutput and GPUImageInput are not defined by me; they are part of the GPUImage library)

Our Swift code doesn't seem to recognize this, even though the header is #imported in our Bridging Header. I’ve tried to replicate the declaration in Swift, but neither of these are proper syntax:
typealias MyFilter = GPUImageOutput, GPUImageInput
typealias MyFilter = GPUImageOutput : GPUImageInput

David Cairns
  • 603
  • 5
  • 18
  • 2
    I asked the same thing over here: http://stackoverflow.com/questions/24455327/how-do-i-specify-that-a-non-generic-swift-type-should-comply-to-a-protocol , for the same reasons. – Brad Larson Oct 21 '14 at 16:57

4 Answers4

4

You can't declare typealias like that.

The best we can do is something like this:

class MyClass {
    private var filter:GPUImageOutput

    init<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
        self.filter = filter
    }

    func setFilter<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
        self.filter = filter
    }

    func someMethod() {
        let output = self.filter
        let input = self.filter as GPUImageInput
        output.someOutputMethod()
        input.someInputMethod()
    }
}
rintaro
  • 51,423
  • 14
  • 131
  • 139
3

In Swift 4 you can achieve this with the new & sign (Below an example of a parameter confirming to UIViewController and UITableViewDataSource:

func foo(vc: UIViewController & UITableViewDataSource) {
    // access UIViewController property
    let view = vc.view
    // call UITableViewDataSource method
    let sections = vc.numberOfSectionsInTableView?(tableView)
}
Jeroen Bakker
  • 2,142
  • 19
  • 22
1

In Swift, something like the following should accomplish your task, but it's different than its ObjC counterpart:

typealias GPUImageOutput = UIImage
@objc protocol GPUImageInput {
    func lotsOfInput()
}

class GPUImageOutputWithInput: GPUImageOutput, GPUImageInput
{
    func lotsOfInput() {
        println("lotsOfInput")
    }
}

// ...

var someGpuImage = GPUImageOutput()
var specificGpuImage = GPUImageOutputWithInput()

for image in [someGpuImage, specificGpuImage] {
    if let specificImage = image as? GPUImageInput {
        specificImage.lotsOfInput()
    } else {
        println("the less specific type")
    }
}

UPDATE: now that I understand where/why you have these types ...

GPUImage seems to have a swift example that does what you want, as Swift-ly as possible.

See here:

class FilterOperation<FilterClass: GPUImageOutput where FilterClass: GPUImageInput>: FilterOperationInterface {
...

The type constraint syntax can be applied to functions, too, and with a where clause, that's probably as good as you're going to get directly in Swift.

The more I tried to understand how to port this somewhat common objc trope, the more I realized it was the most Swift-way. Once I saw the example in GPUImage itself, I was convinced it was at least your answer. :-)

UPDATE 2: So, besides the specific GPUImage example I linked to above that uses Swift, the more and more I think about this, either using a where clause to guard the setter function, or using a computable property to filter the set functionality seems the only way to go.

I came up with this strategy:

import Foundation

@objc protocol SpecialProtocol {
    func special()
}

class MyClass {}

class MyClassPlus: MyClass, SpecialProtocol {
    func special() {
        println("I'm special")
    }
}

class MyContainer {
    private var i: MyClass?

    var test: MyClass? {
        get {
            return self.i
        }
        set (newValue) {
            if newValue is SpecialProtocol {
               self.i = newValue
            }
        }
    }
}

var container = MyContainer()

println("should be nil: \(container.test)")

container.test = MyClass()
println("should still be nil: \(container.test)")

container.test = MyClassPlus()
println("should be set: \(container.test)")

(container.test as? MyClassPlus)?.special()

Outputs:

should be nil: nil
should still be nil: nil
should be set: Optional(main.MyClassPlus)
I'm special

(Optionally, you could also use precondition(newValue is SpecialProtocol, "newValue did not conform to SpecialProtocol") in place of the is check, but that will act like an assert() can crash the app if the case isn't met. Depends on your needs.)

@rintaro's answer is a good one, and is a good example of using a where clause as a guard (both nice functional-ly, and Swift-ly). However, I just hate to write a setFoo() function when computable properties exist. Then again, even using a computable property has code smell, since we can't seem to be able to apply a generic type-constraint to the set'er, and have to do the protocol conformance test in-line.

greymouser
  • 3,133
  • 19
  • 22
  • Sorry, but `GPUImageOutput` and `GPUImageInput` are [pre-existent](https://github.com/BradLarson/GPUImage), so I can't just re-declare them any old way. Will update my question to clarify! – David Cairns Oct 20 '14 at 22:23
  • That was just an example, because I didn't know what your base class was! The key is the @objc protocol, which allows you to detect conformance, is all. – greymouser Oct 21 '14 at 01:12
  • ... and also +1 for the question -- this is a very interesting case of Swift v. ObjC differences. – greymouser Oct 21 '14 at 01:27
-3

You can use typealias keyword. Here is how to do it:

typealias MyNewType = MyExistingType

It doesn't matter whether MyExistingType is protocol or function or enum. All it needs to be some type. And the best part is you can apply access control on it. You can say

private typealias MyNewType = MyExistingType

That makes MyNewType is only accessible the context that is defined in.

mustafa
  • 15,254
  • 10
  • 48
  • 57
  • This question is specifically asking how to declare a typealias for an object of a class that implements the protocol, not just one or the other. – David Cairns Oct 20 '14 at 22:18