Skip to content

Instantly share code, notes, and snippets.

@andreashanft
Last active August 12, 2022 17:33
Show Gist options
  • Save andreashanft/e9b99e4dbe5c87f8914e52585689d16e to your computer and use it in GitHub Desktop.
Save andreashanft/e9b99e4dbe5c87f8914e52585689d16e to your computer and use it in GitHub Desktop.
Simple Generic UITableView Data Source
//
// GenericTableManager.swift
// Copyright © 2020 andreashanft.de. All rights reserved.
//
import Foundation
import UIKit
import Reusable
/*
class SomeTableViewCell: UITableViewCell, Reusable {}
final class SomeTableViewModel {
@Published private(set) var items = ["Hello World", "Lorem Ipsum", "Don't Picknick"]
}
class SomeTableViewController: UIViewController {
private lazy var dataSource =
SingleSectionDataSource<String, SomeTableViewCell>(tableView) { (element, cell, indexPath) in
cell.textLabel?.text = element
}
private lazy var tableView = UITableView()
private var subscriptions = Set<AnyCancellable>()
private let viewModel: SomeTableViewModel
private func setupBindings() {
subscriptions.add([
viewModel.$items.assign(to: \.items, on: dataSource)
])
}
}
*/
protocol SectionDefining {
associatedtype E
var headerTitle: String? { get }
var footerTitle: String? { get }
var elements: [E] { get }
init(elements: [E])
}
struct GenericSection<E>: SectionDefining {
var headerTitle: String? { nil }
var footerTitle: String? { nil }
var elements = [E]()
}
typealias ReusableTableViewCell = UITableViewCell & Reusable
// MARK: - SingleSectionDataSource
class SingleSectionDataSource<E, C: ReusableTableViewCell>: GenericDataSource<GenericSection<E>, C> {
public var items: [ElementType] {
set {
let section = SectionType(elements: newValue)
sections = [section]
}
get {
guard let items = sections.first?.elements else {
return []
}
return items
}
}
}
// MARK: - GenericDataSource
class GenericDataSource<S, C>: NSObject, UITableViewDataSource, UITableViewDelegate where S: SectionDefining, C: ReusableTableViewCell {
public typealias CellConfig<Element, Cell> = (Element, Cell, IndexPath) -> Void
public var sections = [S]() {
didSet {
tableView.reloadData()
}
}
typealias ElementType = S.E
typealias SectionType = S
private let tableView: UITableView
private var cellFactory: ((UITableView, IndexPath, ElementType) -> C)
init(_ tableView: UITableView, cellConfig: @escaping CellConfig<ElementType, C>) {
self.tableView = tableView
self.cellFactory = { tableView, indexPath, element in
let cell: C = tableView.dequeueReusableCell(for: indexPath)
cellConfig(element, cell, indexPath)
return cell
}
super.init()
tableView.register(cellType: C.self)
tableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
sections[section].elements.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
sections[section].headerTitle
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
sections[section].footerTitle
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let element = sections[indexPath.section].elements[indexPath.row]
return cellFactory(tableView, indexPath, element)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment