2
votes

The following code produces a compile error of "Generic parameter "T" cannot be bound to non-@objc protocol type 'AAA' on the fail line. When I use a class instead of a protocol, it works ok. Also, if I add an @objc to the protocol it also works, but only in 6.4 beta. Any suggestions would be helpful.

protocol AAA {
    var id: String { get set }
}

class BBB: AAA {
    var id: String = ""
}

class BBBService {
    func getAll<T:AAA>() -> [T] {
        var returnArray:[T] = [T]()

        return returnArray
    }
}

class TestIt
{
    func myTest() {
        var service = BBBService()
        var fail:[AAA] = service.getAll() // fails with Generic parameter "T" cannot be bound to non-@objc protocol type AAA
        var succeed:[BBB] = service.getAll()
    }
}

this also fails:

<T where T:AAA>

Update - from a practical perspective, adding the @objc causes other problems in my app. So, that is not an option at this point and time.

1
This is a trivial example to show the error - it's not what I'm doing in the actual app. I'm looking for help on whether this a known limitation of Swift, or if I'm doing something wrong.Randy Ott

1 Answers

0
votes

The trouble is with this line:

 getAll<T: AAA>() -> [T]

you are declaring that T must be a concrete type that implements the protocol AAA. It’s important to distinguish between the protocol AAA, i.e. code like this:

func getAll() -> [AAA] {
    var returnArray: [AAA] = [BBB()]

    return returnArray
}

which works fine (returns an array of references to AAA-conforming objects, which could be of type BBB, or type CCC), and this:

func getAll<T: AAA>() -> [T] {
    var returnArray: [T] = [] // what is T?  No idea.

    return returnArray
}

in which you are saying to the compiler “write me a version of getAll, in which T can be replaced by any specific type that implements AAA”.

This is why your second version compiles - you’re fixing T to be the actual type BBB.

Bear in mind, T might be a struct. In which case the array returned must be sized specifically for whatever struct is being held, right there as a value within the array. As opposed to if the protocol was @objc in which case it would at least be known to only be a class reference of a fixed size.

If what you actually want is an array of protocols, you should remove the generic placeholder and just return an array of protocols:

func getAll() -> [AAA] {
    var returnArray: [AAA] = []

    return returnArray
}