I am working on an app that uses TableView for showing feeds to users using ViewModel and my ViewModel contains a variable that contains data of all cells and ViewModel also contains other data as well, what I am doing is passing the whole ViewModel reference and indexPath to cell, here you can see:
func configureCell(feedsViewModelObj feedsViewModel: FeedsViewModel, cellIndexPath: IndexPath, presentingVC: UIViewController){
//Assigning on global variables
self.feedsViewModel = feedsViewModel
self.cellIndexPath = cellIndexPath
self.presentingVC = presentingVC
let postData = feedsViewModel.feedsData!.data[cellIndexPath.row]
//Populate
nameLabel.text = postData.userDetails.name
userImageView.sd_setImage(with: URL(string: postData.userDetails.photo), placeholderImage: UIImage(named: "profile-image-placeholder"))
updateTimeAgo()
postTextLabel.text = postData.description
upvoteBtn.setTitle(postData.totalBull.toString(), for: .normal)
upvoteBtn.setSelected(selected: postData.isClickedBull, isAnimated: false)
downvoteBtn.setSelected(selected: postData.isClickedBear, isAnimated: false)
downvoteBtn.setTitle(postData.totalBear.toString(), for: .normal)
commentbtn.setTitle(postData.totalComments.toString(), for: .normal)
optionsBtn.isHidden = !(postData.canEdit && postData.canDelete)
populateMedia(mediaData: postData.files)
}
so, is it the right or good way to pass full ViewModel reference and index to cell, and then each cell access its data from the data array? thanks.
2
Answers
*Passing whole ViewModel reference and indexPath to cell is not necessary. Call back after receiving data:
ViewController -> ViewModel -> TableViewDatasource -> TableViewCell.*
ViewController
View Model
TableView Datasource
TableView Cell
The accepted solution is good, but not great.
This method’s logic in particular needs to be improved:
to something like this:
You are currently referencing
postData
and_feedsData
(part of the Model) from Table View Cell – which is technically incorrect in the context of MVVM paradigm since View would have direct dependencies of Model…Note that
PostCellViewModel
is the ViewModel struct (or class) you have to implement and it should look like this:Depending on the project/team/coding standards, you may want to also use a protocol:
And then implement it:
Also note that
sd_setImage
uses a library/pod/dependency, which on its turn uses functionality of the Networking/Service Layer. So probably it’s better not to make Cell/View dependent on it.For cells’ images in particular – you can add those calls inside
cellForRow(at:)
, even if the method is implemented inside a dedicated UITableViewDatasource subclass and not inside the UIViewController directly.For the UITableViewDatasource subclass, which is technically some controller/mediator type (since it depends on View and Model or ViewModel) – it’s ok to interact with dependencies from other layers (Networking in case of image downloads). Views/Cells should care less if the image is to be downloaded or to to be fetched from local cache.
In general, if the images are too big and you want to implement a scalable architecture – you may want to create a custom ImageLoader class to take care of loading images only when needed, as well as canceling remote image requests if the cell disappears while the image download is in progress.
Have a look here for such a solution:
https://www.donnywals.com/efficiently-loading-images-in-table-views-and-collection-views/
Also see how Apple recommends to implement a solution for a similar use-case:
https://developer.apple.com/documentation/uikit/views_and_controls/table_views/asynchronously_loading_images_into_table_and_collection_views