When I was working on my app I ran into this problem that I don't know if my thinking is wrong or I found a bug in Swift or the compiler.
I was trying to store an array of UIViews all of which are guaranteed to conform to a protocol. But I had to upcast these UIViews in order to put them in the same array. When I tried calling a function on these the Compiler threw an error:
Global function 'callFunc(itemType:)' requires that 'UIView' conform to 'Identifiable'
In the following example, I took out all the unnecessary code in order to isolate the problem. For this reason, the code lost its usefulness in application but the principle still stands.
protocol Identifiable: class {
static var id: String { get }
}
extension Identifiable {
static var id: String {
return String(describing: self)
}
}
typealias IdentifiableView = UIView & Identifiable
func identify<T>(_ view: T.Type) where T: UIView, T: Identifiable {
print(view.id)
}
class MyViewA: IdentifiableView { }
class MyViewB: IdentifiableView { }
let views: [IdentifiableView.Type] = [MyViewA.self, MyViewB.self]
// flag-1: This works
identify(MyViewA.self)
identify(MyViewB.self)
for view in views {
// flag-2: This doesn't work
// identify(view)
// flag-3: This works
print(view.id)
// flag-4: This works
if let view = view as? MyViewA.Type {
identify(view)
} else if let view = view as? MyViewB.Type {
identify(view)
}
}
The problem in this code is at flag-2 and flag-3:
At flag-2 the functions aren't willing on taking the view of type IdentifiableView.Type
even though it fulfils all the requirements. I prove this at flag-3 by getting the id
property of the type. This is what the function would do.
At flag-4 when I downcast to the most specific type it works again.
My theory as to why this is working the way it is because in the eye of the compiler the type loses its protocol conformance when upcasted. However, this is not the case as proven by flag-3.
I even tried changing the function to the following but even like this, the same error occurs:
func identify<T>(_ view: T.Type) where T: IdentifiableView {
print(view.id)
}
I also started a small tread before on Reddit.
So is my thinking wrong? is my theory correct? Why is the error thrown? I would much appreciate some feedback.