GenericTableView with Examples (Simple - Dynamic - Testable)
- Generic TableView Data Source / Delegate
- Dynamic TableView (Depends on protocols)
- Reusable TableView (We don't need storyboards for tableview, we only need xib files for cells)
- Better TableView Testability (The tableview will be generic and has zero business logic)
- Better Cells Testability (The cells will be depending on the Item -which is testable- for the logic)
- More...
-
Adding the tableview would be like this
let users = [UsersDataSource(user: User(firstname: "Adam", lastname: "Meguid")), UsersDataSource(user: User(firstname: "Ahmed", lastname: "Ali"))] func addTableView() { let usersList = GenericTableView(frame: view.bounds) usersList.updateDataSource(items: users) self.view.addSubview(usersList) }
But first we need to add 3 things
- Model
struct User: GenericModel { var firstname: String var lastname: String }
- Cell
class UserCell: UITableViewCell, GenericCell { @IBOutlet weak var firstname: UILabel! @IBOutlet weak var lastname: UILabel! func configure(model: GenericModel) { if let user = model as? User { firstname.text = user.firstname lastname.text = user.lastname } } }
- Data Source
class UsersDataSource: GenericDataSource { var type: UITableViewCell.Type = UserCell.self var action: (GenericModel) -> () = {_ in } var item: GenericModel init(user: User) { item = user } }
-
Adding actions to cell selection
func addTableView() { . . users.forEach({$0.action = navigateToUserView()}) . } private func navigateToUserView() -> (GenericModel) -> Void { return { (user: GenericModel) in // here navigate to $user view } }
-
Supporting Multi Actions
func addTableView() { . users.forEach { (user) in if let userItem = user.item as? User { if userItem.activated { user.action = navigateToUserView() } else { user.action = blockUserAccess() } } } . . } private func navigateToUserView() -> (GenericModel) -> Void { return { (user: GenericModel) in // here navigate to $user view } } private func blockUserAccess() -> (GenericModel) -> Void { return { (user: GenericModel) in // here block access to $user } } struct User: GenericModel { . . var activated: Bool }
-
Supporting Multi Cells with Multi Actions
let users = [UsersDataSource(user: User(firstname: "Adam", lastname: "Meguid")), PremiumUsersDataSource(user: PremiumUser(firstname: "Ahmed", lastname: "Ali", grade: "Class A"))] struct PremiumUser: GenericModel { var firstname: String var lastname: String var grade: String } class PremiumUserCell: UserCell { @IBOutlet weak private var grade: UILabel! override func configure(model: GenericModel) { if let user = model as? PremiumUser { firstname.text = user.firstname lastname.text = user.lastname grade.text = user.grade } } } class PremiumUsersDataSource: GenericDataSource { var type: UITableViewCell.Type = PremiumUserCell.self var action: (GenericModel) -> () = {_ in } var item: GenericModel init(user: PremiumUserCell) { item = user } }