Closing a listener in func f2<T: SomeProtocol>(ofType: T.Type, listener: ([T]?) -> Void) {...} requires that its argument be an array of T , where T is a type that implements SomeProtocol . By writing <T: SomeProtocol> , you ensure that all elements of this array are of the same type.
Say, for example, you have two classes: A and B Both of them are completely different. However, both options implement SomeProtocol . In this case, the input array cannot be [A(), B()] due to the type constraint. The input array can be [A(), A()] or [B(), B()] .
But when you define l2 as let l2: ([SomeProtocol]?) -> Void = ... , you allow the closure to take an argument, for example [A(), B()] . Therefore, this closure and the closure that you define in f2 are incompatible and the compiler cannot convert between them.
Unfortunately, you cannot add type coercion to a variable such as l2 , as indicated here . What you can do if you know that l2 will work with class A arrays, you can override it as follows:
let l2: ([A]?) -> Void = { ... }
Let me try to explain this with a simpler example. Let's say you write a generic function to find the largest element in an array of compared values:
func greatest<T: Comparable>(array: [T]) -> T {
Now, if you try to call this function as follows:
let comparables: [Comparable] = [1, "hello"] print(greatest(array: comparables))
The compiler will complain, since there is no way to compare Int and string. Instead, you should do the following:
let comparables: [Int] = [1, 5, 2] print(greatest(array: comparables))