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