A pseudo implememntation of a mvvm with different cell types in the same section.
The table view displays a mixed list of User
profiles and their Posts
.
Each section's 1st row is the profile of a user and the next cells in that section are the posts of that user
// MARK: - Models
struct Post {
let title: String
let date: Date
let comment: String
}
struct User {
let firstName: String
let lastName: String
let profilePictureName: String
let posts: [Post]
}
// MARK: - Item List ViewModels
protocol ListItemViewModel {
}
struct PostViewModel: ListItemViewModel {
private let post: Post
var postTitle: String {
return "\(post.date) \(post.title)"
}
var postPreview: String {
return post.comment.substring(to: post.comment.index(post.comment.startIndex, offsetBy: 10)) + "..."
}
}
struct UserViewModel: ListItemViewModel {
private let user: User
var userName: String {
return user.firstName + " " + user.lastName
}
var userProfileImage: UIImage {
return UIImage(named: user.profilePictureName) ?? UIImage(named: "placeholder")!
}
}
// MARK: - List View Model
struct ForumListViewModel {
let users: [User]
var numberOfSections: Int {
return users.count
}
// MARK: rows
func numberOfRowsInSection(section: Int) -> Int {
return users[section].posts.count + 1 // 1st row in each section is the user profile
}
// returns a different id for each view model type
func itemViewModelId(for indexPath: IndexPath) -> String {
// 1rst row in a section is a profile view, followed by all comments
return indexPath.row == 0 ? "profileViewId" : "commentViewId"
}
func itemViewModel(for indexPath: IndexPath) -> ListItemViewModel {
let user = users[indexPath.section]
if indexPath.row == 0 {
return UserViewModel(user: user)
} else {
return PostViewModel(post: user.posts[indexPath.row])
}
}
}
// MARK: ViewController
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = viewModel.itemViewModelId(for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
if let cell = cell as? PostTableViewCell {
guard let itemViewModel = viewModel.itemViewModel(for: indexPath) as? PostViewModel else { fatalError() }
cell.configure(with: cellViewModel)
}
if let cell = cell as? UserTableViewCell {
guard let cellViewModel = viewModel.itemViewModel(for: indexPath) as? UserViewModel else { fatalError() }
cell.configure(with: cellViewModel)
}
return cell
}