简体   繁体   中英

How to resolve generic property in protocol with associatedtype?

Idea is to share the common code between UITableView and UICollectionView and their viewModels. So I have a baseCollectionsViewModel which is a generic class and looks like

class BaseCollectionsViewModel<T: BasicTrackDataExtractorProtocol> {
    var baseTrackResponse: T?
    ...
}

Now I would like to have a BaseCollectionsView which holds BaseCollectionsViewModel . Because its a UITableView and UICollectionView I cant have a common base class among them so I decided to go in the direction of protocol to work around multiple inheritance

protocol BaseCollectionsViewProtocol {
    associatedtype dataExtractorType: BasicTrackDataExtractorProtocol
    var viewModel: BaseCollectionsViewModel<dataExtractorType>! { get }
    //all other common properties and logic here
}

Finally when I try to use this protocol in my final class

final class TrackingTableViewModel: BaseCollectionsViewModel<TrackOrderV3> {
     
}

final class TrackingTableView: UITableView,BaseCollectionsViewProtocol {
    var viewModel: TrackingTableViewModel!
    typealias dataExtractorType = TrackOrderV3
    //other code
}

Compiler complains that TrackingTableView does not confirm to BaseCollectionsViewProtocol and keeps adding

final class TrackingTableView: UITableView,BaseCollectionsViewProtocol {
    var viewModel: BaseCollectionsViewModel<TrackOrderV3>!
    typealias dataExtractorType = TrackOrderV3

Is this because BaseCollectionsViewModel<TrackOrderV3> and TrackingTableViewModel are invariant? Is there anyway I can resolve this problem with TypeErasure ?

If its not a protocol I could solve it as

class TestTableView<T: BasicTrackDataExtractorProtocol, U: BaseCollectionsViewModel<T>> {
    var viewModel: U!
}

class TestTrackingTableView: TestTableView<TrackOrderV3, TrackingTableViewModel> {
    func testViewModel() {
        let _ = self.viewModel.numberOfRows
    }
}

But the issue is now I cant say class TestTrackingTableView: TestTableView<TrackOrderV3, TrackingTableViewModel>, UITableView because multiple inheritance is not allowed.

PS: Sorry for the highly confusing title, couldn't come up with a better title, will change if someone can suggest a better one:)

What I would do is define a protocol for your view model:

protocol CollectionsViewModel {
    associatedtype DataType: BasicTrackDataExtractorProtocol
    var baseTrackResponse: DataType? { get }
}

Then implement a generic view model class that houses all of your common logic:

class BaseCollectionsViewModel<T: BasicTrackDataExtractorProtocol>: CollectionsViewModel {
    typealias DataType = T
    var baseTrackResponse: T?
}

Rather than setting the data model as a type constraint on the view protocol set the view model as a type constraint. This will give us greater flexibility down the road.

protocol BaseCollectionsViewProtocol {
    associatedtype ViewModelType: CollectionsViewModel
    var viewModel: ViewModelType! { get }
}

Now we can implement our final classes:

final class TrackingTableViewModel: BaseCollectionsViewModel<TrackOrderV3> {

}

final class TrackingTableView: UITableView, BaseCollectionsViewProtocol {
    var viewModel: TrackingTableViewModel!
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM