Skip to content

Instantly share code, notes, and snippets.

@JanC
Last active January 29, 2018 16:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JanC/63dafd23d019ab55f8caf14116494120 to your computer and use it in GitHub Desktop.
Save JanC/63dafd23d019ab55f8caf14116494120 to your computer and use it in GitHub Desktop.
mvvm

MVVM

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
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment