[swift-evolution] [Proposal] Make optional protocol methods first class citizens
plx
plxswift at icloud.com
Sat Apr 2 08:00:13 CDT 2016
> On Apr 1, 2016, at 7:37 PM, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:
>
>> Protocol requirements with default (no-op) implementations already satisfy that design goal, no?
>
> Kind of. If I may steelman* optional members for a moment…
I’d actually go a bit further: for at least *some* of the uses of optional protocol methods, default implementations are necessary but not sufficient; to really “Swift-ify” without *losing* functionality you also would need to do a much-more-involved redesign of the protocol methods.
As just one example, I *think* the following API sketch is *adequate* for a Swift-ified` `tableView:heightForRowAtIndexPath:`:
/// Mocked-up, "Swifty" table-view-delegate protocol:
protocol TableViewDelegate : class {
/// Non-optional method, returning a "policy" enum that e.g. provides enough
/// information to the table view to let it choose the appropriate layout strategy...
func rowHeightPolicyForTableView(tableView: TableView) -> TableViewRowHeightPolicy
}
/// “How to calculate height” information for an entire table view.
enum TableViewRowHeightPolicy {
/// all rows have identical height, table-view can operate in the "fast"
/// mode, like setting `tableView.rowHeight = $someHeight` w/o delegate.
case Uniform(CGFloat)
/// table view should use auto-layout to measure each cell automatically;
/// like `tableView.rowHeight = UITableViewAutomaticDimension` w/o delegate.
case Automatic
/// get per-row height info from `RowHeightCalculator`; equivalent to your
/// having a delegate that implements `tableView:heightForRowAtIndexPath:`.
case Manual(RowHeightCalculator)
}
/// A dedicated “row-height-calculator” protocol. On the one hand, this could
/// also be "just a closure", but promoting it into a protocol seems more
/// future-proof (e.g. if additional calculation-options become supported later).
///
/// Unfortunately, making this an object means having to think harder than
/// before about ownership issues; this seems mildly thorny.
protocol RowHeightCalculator : class {
/// Returns the height-information for the given row; to *fully* recapture
/// existing `UITableView` functionality we need to return more than `CGFloat`.
func heightForRow(in tableView: UITableView, at indexPath: NSIndexPath) -> RowHeight
}
/// Height-calculation result for a single row.
enum RowHeight {
/// Tells the table view to use auto-layout to measure this row's cell;
/// equivalent to *returning* `UITableViewAutomaticDimension`.
case Automatic
/// Tells the table view to use the specified height for this row.
case Height(CGFloat)
}
…and at least AIUI that’s a complete representation of the various row-height-related behaviors it’s possible arrive-at, today, via various combinations of “did you implement `tableView:heightForRowAtIndexPath:` or not?” and/or “are you setting/returning `UITableViewAutomaticDimension`?.
Note also that this is just the behavior for row-heights; to fully-cover today’s user-exposed functionality, you’d need to handle header-heights and footer-heights (which have similar methods/semantics), and probably also the estimated-height methods for rows, headers, and footers.
Pausing here for a moment, I think the above illustrates why the topic keeps coming up: although it’s certainly *possible* to get to the same place w/out optional protocol methods, it sometimes takes a bit of a design rethink to get there. The API surface also seems to get quite a bit larger; I think it’s fair to classify this as simply “making already-present complexity actually-visible”, but it’d still be bit of a shift.
For the actual `tableView:heightForRowAtIndexPath:` family I’m not sure what I think; I personally like a more-explicit approach as per the above, but once you start to include the header-and-footer heights and also the estimated height APIs, it gets to be a bit much; more so when you think about how this API looks with similar approaches for other currently-optional method “suites" (like highlight/selection management “suite", the menu/action “suite”, etc.).
To conclude, in many cases "optional methods => methods w/ reasonable default implementation” is all you need, but in *some* cases you would also need to design your API very differently to have equivalent functionality.
I assume patterns and idioms for such designs will evolve over time, but at present there’s not a lot of Swift-ified datasource/delegate-style APIs out there to use for inspiration.
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
More information about the swift-evolution
mailing list