Last week Venmo introduced a library called Static. It has some similar concepts to Weebly's TableSchemer, but there are some key differences. I wanted to do a brief analysis of the two libraries, and when you should use each of them in your own code.
Disclaimer: I am the original author of TableSchemer, but I'll do my best to be unbiased
Disclaimer: I am the original author of TableSchemer, but I'll do my best to be unbiased
Goals
Both libraries have similar goals. Primarily, to reduce the complexity of working with a UITableView and eliminating the use of index paths. Both libraries provide closure containers for selection of cells, rather than switching on an index path. This benefits the users greatly because its more readable, and protected against change. You can move a row simply by moving the declaration of it. With index path checking, you'd have to update both the data source (for configuration) and delegate (for selection) when you want to move something.
Static seems to have a goal towards simplicity. It's very easy to use, but not very configurable. One of TableSchemer's goals is to be simple, but also very flexible. I'll touch more on that later.
Static seems to have a goal towards simplicity. It's very easy to use, but not very configurable. One of TableSchemer's goals is to be simple, but also very flexible. I'll touch more on that later.
API
Static: let dataSource = TableDataSource(tableView) | TableSchemer: let tableScheme = TableScheme() { builder in |
This is the basic API for both libraries. Static uses arrays, while TableSchemer typically uses a block-based building pattern. This makes TableSchemer more verbose, but it is necessary because of the flexibility required. TableSchemer also lets you specify a custom cell class; Static lets you do this to an extent. You can use a custom one if it conforms to CellType, but if its a Custom cell then the text and detailText won't work properly on the Row type.
In TableSchemer, you also have to set another object as the UITableViewDelegate and forward height and selection messages to the TableScheme. This is by design, allowing users to handle all the other delegate methods. The next update to TableSchemer will support being the delegate, though, for the simpler interfaces. Here's a breakdown so far based on the above example.
Both support
Static has a nice feature of deducing reuse identifiers, and applying them to the table view.
However, Static lives up to its name quite well; you create Static table views, with no manipulation other than replacing the data (however, doing so animates them nicely!). TableSchemer strives to be an interactive static table view. We accomplish this with Schemes and Visibility controls.
In TableSchemer, you also have to set another object as the UITableViewDelegate and forward height and selection messages to the TableScheme. This is by design, allowing users to handle all the other delegate methods. The next update to TableSchemer will support being the delegate, though, for the simpler interfaces. Here's a breakdown so far based on the above example.
Both support
- Grouping of rows into sections
- Specifying the accessory type
- Section headers and footers
Static has a nice feature of deducing reuse identifiers, and applying them to the table view.
However, Static lives up to its name quite well; you create Static table views, with no manipulation other than replacing the data (however, doing so animates them nicely!). TableSchemer strives to be an interactive static table view. We accomplish this with Schemes and Visibility controls.
Schemes
TableSchemer refers to SchemeSets (which maps to a section), and Schemes, which map to zero or more rows. Schemes can also change how many rows they have during runtime. A good example of this is the AccordionScheme; this scheme defaults to having a single row, but when you tap it it expands into an N number of rows. This allows you to make a combo box out of your scheme. Heres an example of an accordion:
// Generic param 1 is the unexpanded cell, and 2 is expanded
let accordionScheme = AccordionScheme<UITableViewCell, UITableViewCell>
let reuseIdentifier = "Cell"
var accordionSelection = 0
accordionScheme.name = "Accordion Sample"
accordionScheme.reuseIdentifier = reuseIdentifier // The reuse identifier for the unexpanded cell
// Use reuse identifiers to support heterogenous cells when expanded
accordionScheme.accordionReuseIdentifiers = [String](count: 3, repeatedValue: reuseIdentifier)
accordionScheme.configurationHandler = { cell in
cell.textLabel?.text = "Selected Index: \(accordionSelection)"
}
accordionScheme.selectionHandler = { cell, scheme in
// Called when expanding the accordion
}
accordionScheme.accordionConfigurationHandler = { cell, index in // index relative to accordion, not tableview
cell.textLabel?.text = "Accordion Expanded Cell \(index + 1)"
if index == accordionSelection {
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
}
accordionScheme.accordionSelectionHandler = { cell, scheme, selectedIndex in
// Called when collapsing the accordion
accordionSelection = selectedIndex
}
TableSchemer comes with a variety of schemes, but the you can create your own as well. There are a few methods that must be overriden by your Scheme subclass; in the future Scheme will be a protocol, and it'll be obvious how to quickly implement your own schemes. In the meantime there is a wiki on TableSchemer's github if you're interested.
Visibility
When you think of a TableScheme it's best to think of it as a table view blueprint. Its what the table can be, but not always what it is. This is expressed through my previous example of an AccordionScheme, but TableSchemer also supports SchemeSet and Scheme hiding.
Here are some examples of working with visibility in TableSchemer:
Here are some examples of working with visibility in TableSchemer:
// Hide a scheme set
tableScheme.hideSchemeSet(mySchemeSet, inTableView: tableView)
// Show a scheme set, with a specific row animation
tableScheme.showSchemeSet(mySchemeSet, inTableView: tableView, withRowAnimation: .Top)
// Hide a scheme, with a specific row animation
tableScheme.hideScheme(myScheme, inTableView: tableView, withRowAnimation: .Bottom)
// Show a scheme
tableScheme.showScheme(myScheme, inTableView: tableView)
// Batch scheme animations with inferred animations (TableSchemer will pick the appropriate animations for each row based on the difference between the scheme before and after the change. This works for schemes that conform to InferrableRowAnimatableScheme)
tableScheme.animateChangesToScheme(arrayScheme, inTableView: self.tableView) {
arrayScheme.objects = [5,4,1,2]
}
// Batch scheme animations with explicit animations
tableScheme.batchSchemeVisibilityChangesInTableView(tableView) { animator in
if schemesHidden {
animator.showScheme(scheme1, withRowAnimation: .Left)
animator.showScheme(scheme2, withRowAnimation: .Right)
} else {
animator.hideScheme(scheme2, withRowAnimation: .Left)
animator.hideScheme(scheme1, withRowAnimation: .Right)
}
}
Conclusion
Static is an awesome little library; it does what it was intended to do simply and beautifully. If you're looking for simple collections of data with built-in cells, Static should be your go-to. TableSchemer was inspired by Settings.app, and had a goal of making table views dynamic while having static content. If you're looking for robust forms, TableSchemer can make that happen for you with ease!