I have a problem with calling the array method index(of:).
MyClass inherits from UIViewController and conforms to the MyDelegate protocol.
//self.viewControllers: [(UIViewController & MyDelegate)]
guard let myController = viewController as? MyClass,
let index = self.viewControllers.index(of: myController) else {return}
then I get the error:
Cannot invoke 'index' with an argument list of type '(of: (UIViewController & MyDelegate))'
How can I solve this problem and is there a better solution than implementing index(of:) in an extension?
extension Array where Element == (UIViewController & MyDelegate) {
func index(of: Element) -> Int? {
for i in 0..<self.count {
if self[i] == of {
return i
}
}
return nil
}
}
This is almost certainly just an extension of the fact that protocols (aka. existentials) don't conform to themselves. So the class existential UIViewController & MyDelegate doesn't conform to Equatable, even though UIViewController does.
Therefore because index(of:) is constrained to being called on a Collection with Equatable elements, you cannot call it on a [UIViewController & MyDelegate].
Here's a more minimal example:
protocol P {}
protocol X {}
class Foo : P {}
func foo<T : P>(_ t: T) {}
func bar(_ f: Foo & X) {
// error: Protocol type 'Foo & X' cannot conform to 'P' because only concrete
// types can conform to protocols
foo(f)
}
We cannot pass f as an argument to foo(_:) as Foo & X doesn't conform to P, even though Foo does. However really this should be a clear-cut case of where the existential should always be able to conform to itself, so I went ahead and filed a bug.
Until fixed, a simple solution is just to do an intermediate cast to the concrete type – so in our example, we can do:
foo(f as Foo)
and in your example, you can do:
let index = (self.viewControllers as [UIViewController]).index(of: myController)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With