So, each cell has a view model, the same goes for the controllers.
Now I can subclass those:
final class ListCell: BaseListCell<ElementViewModel>
struct SomeViewModel: ListViewModel {}
final class ListViewController: BaseListViewController<ListCell<ElementViewModel>, ElementViewModel, SomeViewModel>
As you can see, there's a repetition of ElementViewModel, which is used to fulfil the requirement of BaseListCell. Is there a way to make Swift somehow infer the view model type of ListCell when subclassing BaseListViewController? I'm aiming for the following syntax:
struct SomeViewModel: ListViewModel {}
final class ListViewController: BaseListViewController<ListCell, SomeViewModel>
I'm in no way proficient in the language, so if there's something fundamentally incorrect that I'm doing, I'm open for suggestions.
It seems like you're asking for higher-kinded types. You want to pass the generic type constructor ListCell as the argument for Cell to BaseListViewController, and in the body of BaseListViewController, have Cell be an "abstracted" generic type which can be applied to CellViewModel, like Cell<CellViewModel>.
Swift doesn't support this so you can't get exactly what you want, but you might want to consider using a protocol instead of a BaseListCell class. Perhaps something like:
protocol CellProtocol {
associatedtype ViewModel : ListCellViewModel
func configure(with model: ViewModel)
}
class BaseListViewController<Cell: UITableViewCell & CellProtocol,
ViewModel: ListViewModel>: UITableViewController {
...
// you can talk about Cell.ViewModel here, which conforms to ListCellViewModel.
}
Now you can write BaseListViewController<ListCell<ElementViewModel>, SomeViewModel>, omitting the second generic parameter.
Edit: You can even put the UITableViewCell superclass requirement on the protocol itself, like
This isn't going to work since it's required to specify a concrete type as a generic argument. There usually will be a stub (mock) view model implementation and a production one. Can you think of an approach I could take to solve this? Besides type erasure and making the view model protocol a class. The latter would require some code to be written (because we'd need to initialise every property, or making it an optional) for every subclass. To be honest, these 2 ways make the whole idea of a generic list controller not worth it.
Edit: whatever I've just asked was weird, sorry. So far come up with this, seems to work.
protocol FeedViewModel: ListViewModel {
...
}
final class StubFeedViewModel: FeedViewModel {
...
}
final class FeedViewController<ViewModel: FeedViewModel>: BaseListViewController<FeedCell, ViewModel> {
static func instantiate(with viewModel: ViewModel) -> FeedViewController<ViewModel> {
let viewController = FeedViewController<ViewModel>()
viewController.viewModel = viewModel
return viewController
}
}
let viewController = FeedViewController.instantiate(with: StubFeedViewModel())