Pure-Swift library for statically typed table components.
- Xcode 10+
- Swift 5.0+
- iOS 12.0+
To use the SwiftyTable
library in a SwiftPM project, add the following line to the dependencies in your Package.swift
file:
.package(url: "https://github.com/jrsaruo/SwiftyTable", from: "2.0.3"),
and add SwiftyTable
as a dependency for your target:
.target(name: "<target>", dependencies: [
.product(name: "SwiftyTable", package: "SwiftyTable"),
// other dependencies
]),
CocoaPods is a dependency manager for Cocoa projects.
To integrate SwiftyTable
into your Xcode project using CocoaPods, add the following line to your Podfile
and run pod install
.
pod 'SwiftyTable', git: 'https://github.com/jrsaruo/SwiftyTable.git'
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
To integrate SwiftyTable
into your Xcode project using Carthage, specify it in your Cartfile
:
github "jrsaruo/SwiftyTable"
Run carthage update
to build the framework and drag the built SwiftyTable.framework
into your Xcode project.
No need to define reuse identifiers for UITableViewCell
, UITableHeaderFooterView
, UICollectionViewCell
and UICollectionReusableView
.
class CustomCell: UITableViewCell {
var customProperty: String?
}
let tableView = UITableView()
tableView.register(CustomCell.self) // NO NEED to define reuse identifier!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(of: CustomCell.self, for: indexPath)
cell.customProperty = "NO NEED to use 'as! CustomCell'"
return cell
}
Following implementation is hard to read and will easily cause bugs.
switch indexPath.section {
case 0:
switch indexPath.row {
case 0:
cell.textLabel?.text = "Username"
case 1:
cell.textLabel?.text = "Email"
case 2:
cell.textLabel?.text = "phoneNumber"
default:
break
}
case 1:
cell.accessoryType = .disclosureIndicator
default:
break
}
↓ If you use SwiftyTable, the code becomes much better!
switch Section(indexPath.section) {
case .profile:
cell.textLabel?.text = ProfileRow(indexPath.row).title
case .information:
cell.accessoryType = .disclosureIndicator
}
enum Section: Int, TableSection, CaseIterable {
case profile
case information
}
enum ProfileRow: Int, TableRow, CaseIterable {
case username
case email
case phoneNumber
var title: String { ... }
}
enum InformationRow: Int, TableRow, CaseIterable {
case help
case aboutApp
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return Section.count // automatically returns 2.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch Section(section) {
case .profile:
return ProfileRow.count // automatically returns 3.
case .information:
return InformationRow.count // automatically returns 2.
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(of: UITableViewCell.self, for: indexPath)
switch Section(indexPath.section) { // default case is unnecessary.
case .profile:
cell.textLabel?.text = ProfileRow(indexPath.row).title
case .information:
cell.accessoryType = .disclosureIndicator
}
return cell
}
}
You can easily change sections or rows like this. Even if the change causes some problems, the compiler will tell you about it.
enum InformationRow: Int, TableRow, CaseIterable {
case contact // just add cases to increase rows
case aboutApp // just swap cases to change the order of rows
case help
}
Please create Section/Row enumeration that has integer raw values and conforms to TableSection
/TableRow
and CaseIterable
.
Raw values must correspond to its section/row numbers.
enum SomeSection: Int, TableSection, CaseIterable {
case zero
case one
case two
}
SomeSection.count // automatically returns 3.
SomeSection(0) // .zero
SomeSection(1) // .one
SomeSection(2) // .two
SomeSection(3) // assertionFailure
enum SomeRow: Int, TableRow, CaseIterable {
case zero, one, two, three
}
If the type does not conform to CaseIterable
, you need to implement count
property. However, hard coding like this can causes bugs, so you should use CaseIterable
.
enum SomeSection: Int, TableSection {
case zero
case one
case two
static var count: Int { return 3 }
}
Initialize Section or Row with specified section/row number.
let section = SomeSection(indexPath.section)
let row = SomeRow(indexPath.row)
Initialize IndexPath with specified Section/Row.
// these indexPaths are the same.
let indexPath1 = IndexPath(row: 1, section: 0)
let indexPath2 = IndexPath(row: SomeRow.one, section: 0)
let indexPath3 = IndexPath(row: 1, section: SomeSection.zero)
let indexPath4 = IndexPath(row: SomeRow.one, section: SomeSection.zero)
SwiftyTable is under MIT license. See the LICENSE file for more info.