[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