The accepted solution is good, but not great.
This method's logic in particular needs to be improved:
func initialiseOutlet(_feedsData: feedsData) {
nameLabel.text = _feedsData.userDetails.name
userImageView.sd_setImage(with: URL(string: _feedsData.userDetails.photo), placeholderImage: UIImage(named: "profile-image-placeholder"))
updateTimeAgo()
postTextLabel.text = _feedsData.description
upvoteBtn.setTitle(_feedsData.totalBull.toString(), for: .normal)
upvoteBtn.setSelected(selected: _feedsData.isClickedBull, isAnimated: false)
downvoteBtn.setSelected(selected: _feedsData.isClickedBear, isAnimated: false)
downvoteBtn.setTitle(_feedsData.totalBear.toString(), for: .normal)
commentbtn.setTitle(_feedsData.totalComments.toString(), for: .normal)
optionsBtn.isHidden = !(_feedsData.canEdit && postData.canDelete)
}
to something like this:
func configure(with viewModel: PostCellViewModel) {
nameLabel.text = viewModel.username
userImageView.sd_setImage(with: viewModel.userPhotoURL, placeholderImage: UIImage(named: "profile-image-placeholder"))
updateTimeAgo()
postTextLabel.text = viewModel.description
upvoteBtn.setTitle(viewModel.totalBull, for: .normal)
upvoteBtn.setSelected(selected: viewModel.isClickedBull, isAnimated: false)
downvoteBtn.setSelected(selected: viewModel.isClickedBear, isAnimated: false)
downvoteBtn.setTitle(viewModel.totalBear, for: .normal)
commentbtn.setTitle(viewModel.totalComments, for: .normal)
optionsBtn.isHidden = viewModel.isHidden
}
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:
struct PostCellViewModel {
private(set) var nameLabel: String
private(set) var userImageURL: URL?
// ...
private(set) var postDescription: String
private(set) var isHidden: Bool
init(model: FeedItem) {
nameLabel = model.userDetails.name
userImageURL = URL(string: model.userDetails.photo)
// ...
postDescription = model.description
isHidden = !(model.canEdit && model.post.canDelete)
}
}
Depending on the project/team/coding standards, you may want to also use a protocol:
protocol PostCellViewModelType {
var nameLabel: String { get }
var userImageURL: URL? { get }
// ...
var postDescription: String { get }
var isHidden: Bool { get }
init(model: FeedItem)
}
And then implement it:
struct PostCellViewModel: PostCellViewModelType {
private(set) var nameLabel: String
private(set) var userImageURL: URL?
// ...
private(set) var postDescription: String
private(set) var isHidden: Bool
init(model: FeedItem) {
// ...
}
}
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