Steve Wilford
Steve Wilford

Reputation: 9012

Is it possible to implicitly cast an enum to a string? (Swift)

Is it possible to implicitly cast an enum to a string in Swift?

As a concrete example, consider the following enum representing a UITableViewCell identifier:

enum TableViewCellIdentifier : String {
    case Basic = "Cell"
}

Then we may want to deque a cell with that identifier...

let cell = tableView.dequeueReusableCellWithIdentifier(TableViewCellIdentifier.Basic.rawValue, forIndexPath: indexPath)

The fact that everywhere we use this pattern we need to use .rawValue is particularly annoying.

Is there any protocol I can make the enum conform to in order to gain this functionality? I've tried looking at StringLiteralConvertible but that is for constructing a value rather than extracting it.

Upvotes: 0

Views: 350

Answers (2)

Abizern
Abizern

Reputation: 150705

I would do this slightly differently to the other answer.

First, I would declare a protocol and a protocol extension

protocol TableViewRowReturnable {
    typealias RowIdentifier: RawRepresentable
}

extension TableViewRowReturnable where Self: UITableViewDataSource, RowIdentifier.RawValue == String {
    func dequeueReuesableCellWithIdentfier(identifier: RowIdentifier, fromTableView tableView: UITableView, forIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        return tableView.dequeueReusableCellWithIdentifier(identifier.rawValue, forIndexPath: indexPath)
    }
}

I'm providing a custom implementation for the case of the implementing type being a UITableViewDatasource because that's the type that returns actually deals with the row.

Now make the DataSource conform to the protocol by providing the enum containing the cell identifiers:

extension ViewController: UITableViewDataSource, TableViewRowReturnable {

    enum RowIdentifier: String {
        case RedCell = "RedCell"
        case BlueCell = "BlueCell"
    }


    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell: UITableViewCell

        switch (indexPath.row % 2) {
        case 0:
            cell = dequeueReuesableCellWithIdentfier(.RedCell, fromTableView: tableView, forIndexPath: indexPath)
        default:
            cell = dequeueReuesableCellWithIdentfier(.BlueCell, fromTableView: tableView, forIndexPath: indexPath)
        }

        cell.textLabel?.text = "Your value here"

        return cell
    }
}

This keeps your enum with the cell identifiers nicely contained in the function that uses it. Also, the default implementation is only available it types that are declared as implementing the protocol, rather than adding it to every instance of UITableView

Upvotes: 1

vadian
vadian

Reputation: 285240

In the WWDC 2015 session "Swift in Practice" the presenter suggested to use an extension for exactly this purpose

enum TableViewCellIdentifier : String {
  case Basic = "Cell"
}

extension UITableView {
   func dequeueReusableCellWithTableViewCellIdentifier(identifier: TableViewCellIdentifier, forIndexPath indexPath: NSIndexPath) -> UITableViewCell {
     return self.dequeueReusableCellWithIdentifier(identifier.rawValue, forIndexPath:indexPath)
   }
}

Then you can call the function

let cell = tableView.dequeueReusableCellWithTableViewCellIdentifier(.Basic, forIndexPath: indexPath)

Upvotes: 1

Related Questions