I´ve got a base ViewController and a base ViewModel. The base ViewModel is used by the base ViewController. Also, I´ve got 2 subclasses of ViewControllers and 2 subclasses of ViewModels that need to be used together.
Example:
class BaseViewModel {
func somethingBasic() {}
}
class ConcreteViewModel1: BaseViewModel {
func somethingConcrete1() {}
}
class ConcreteViewModel2: BaseViewModel {
func somethingConcrete2() {}
}
class BaseViewController {
let viewModel: BaseViewModel
init(with viewModel: BaseViewModel) {
self.viewModel = viewModel
}
}
class ConcreteViewController1: BaseViewController {
init(with viewModel: ConcreteViewModel1) {
super.init(with: viewModel)
}
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete1() //this does not work
}
}
class ConcreteViewController2: BaseViewController {
init(with viewModel: ConcreteViewModel2) {
super.init(with: viewModel)
}
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete2() //this does not work
}
}
The question is: what is the preferred solution to make viewmodel.somethingConcrete1() and viewmodel.somethingConcrete2() work?
Try using Generics for this.
Create init in BaseViewController accepting a generic parameter T constrained to type BaseViewModel, i.e.
class BaseViewController<T: BaseViewModel> {
let viewModel: T
init(with viewModel: T) {
self.viewModel = viewModel
}
}
Now inherit ConcreteViewController1 and ConcreteViewController2 from BaseViewController giving the specific type for generic parameter T, i.e.
class ConcreteViewController1: BaseViewController<ConcreteViewModel1> {
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete1()
}
}
class ConcreteViewController2: BaseViewController<ConcreteViewModel2> {
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete2()
}
}
I discussed this with a few other colleagues, and we came around with this solution, based on Composition instead of inheritance:
class BaseViewModel {
func somethingBasic() {}
}
class ConcreteViewModel1 {
private let baseViewModel = BaseViewModel()
func somethingConcrete1() {}
func somethingBasic() {
baseViewModel.somethingBasic()
}
}
class ConcreteViewModel2 {
private let baseViewModel = BaseViewModel()
func somethingConcrete2() {}
func somethingBasic() {
baseViewModel.somethingBasic()
}
}
class BaseViewController {}
class ConcreteViewController1 {
private let base = BaseViewController()
private let viewModel: ConcreteViewModel1
init(with viewModel: ConcreteViewModel1) {
self.viewModel = viewModel
}
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete1()
}
}
class ConcreteViewController2: BaseViewController {
private let base = BaseViewController()
private let viewModel: ConcreteViewModel2
init(with viewModel: ConcreteViewModel2) {
self.viewModel = viewModel
}
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete2()
}
}
With that solution, you get the type safety, you avoid Generics and you don´t need to cast anywhere.
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