Well, today after I update Xcode to version 10.0, I faced following error in my code.
// error: Cannot convert value of type '[UITableViewCell.Type]' to expected argument type
// '[_.Type]'
table.registerCells(cells: [MainMenuTableViewCell.self,
RescueServicesTableViewCell.self])
Here is the registerCells
function:
func registerCells<T> (cells : [T.Type]) where T: UITableViewCell {
for cell in cells {
let nib = UINib(nibName: String(describing: cell), bundle: nil)
register(nib, forCellReuseIdentifier: String(describing: cell))
}
}
At the beginning I thought it might be swift re-versioning issue so I convert from swift 3 to swift 4 and after 2 hours spending time to fix syntaxes the error was still there, until I did the magic trick.
let cellItems = [MainMenuTableViewCell.self,
RescueServicesTableViewCell.self]
table.registerCells(cells:cellItems)
This solution works and error gone. Now my question is why I'm getting this error is this Xcode problem or I did something wrong?
This is an interesting bug (SR-8825) where the compiler appears to be unable to perform type joining (the process of inferring a common supertype for a collection of types) within a member access on an implicitly unwrapped optional (IUO) declaration (presumably in your case table
is an IUO @IBOutlet
).
A minimal example would be:
class C {}
class D : C {}
class E : C {}
struct X {
func foo<T>(_: [T.Type]) where T : C {}
}
var x: X!
// error: Cannot convert value of type '[C.Type]' to expected argument type '[_.Type]'
x.foo([D.self, E.self])
Making x
either non-optional, or a strong optional (i.e X?
) while performing either optional chaining (i.e x?.foo
) or force unwrapping (i.e x!.foo
) to perform the member access allows the code to compile.
There are a few workarounds you can use, first of which is to explicitly specify the array type, saving the compiler from having to infer the type join:
x.foo([D.self, E.self] as [C.Type])
In your case, this translates to:
table.registerCells(cells:
[MainMenuTableViewCell.self, RescueServicesTableViewCell.self] as [UITableViewCell.Type]
)
Second workaround is to use a non-optional base. In your case, you can force unwrap the IUO into a local variable before performing the member access:
// this is just explicitly doing what the compiler would have otherwise done implicitly.
let table = self.table!
table.registerCells(cells: [MainMenuTableViewCell.self, RescueServicesTableViewCell.self])
Third workaround is, as you've already discovered, to separate out the array into its own expression – which allows the compiler to do the type joining on its own:
let cellItems = [MainMenuTableViewCell.self, RescueServicesTableViewCell.self]
table.registerCells(cells: cellItems)
Though the solution I would go with in your case is to make registerCells(cells:)
non-generic, as it doesn't appear that you're using the generic placeholder T
for anything useful:
extension UITableView {
func registerCells(_ cells: [UITableViewCell.Type]) {
for cell in cells {
let nib = UINib(nibName: String(describing: cell), bundle: nil)
register(nib, forCellReuseIdentifier: String(describing: cell))
}
}
}
Which you can now just call like so:
table.registerCells([MainMenuTableViewCell.self, RescueServicesTableViewCell.self])