2

I am trying to refactor code and create an enum class to hold indexPath of table view cells.

I would like to make the code works this way:

enum TableViewCell: IndexPath {
     case shopImageView = [0,0]
     case selectShopImageButton = [0,1]
}

But the compiler says indexPath is not rawRepresentable:

'TableViewCell' declares raw type 'IndexPath', but does not conform to RawRepresentable and conformance could not be synthesized

Raw value for enum case must be a literal

How can I make indexPath rawRepresentable? The code currently works like this and I would like to improve it.

enum TableViewCell {
    case shopImageView
    case selectShopImageButton
    case shopNameLocal
    case shopNameEN
    case addressLocal
    case addressEN
    case selectAddressButton
    case openingHours
    case datePickers
    case phone
    case email
    case uploadShopFormButton
    
    var indexPath: IndexPath {
        switch self {
        case .shopImageView:         return [0,0]
        case .selectShopImageButton: return [0,1]
        case .shopNameLocal:         return [0,2]
        case .shopNameEN:            return [0,3]
        case .addressLocal:          return [0,4]
        case .addressEN:             return [0,5]
        case .selectAddressButton:   return [0,6]
        case .openingHours:          return [0,7]
        case .datePickers:           return [0,8]
        case .phone:                 return [0,9]
        case .email:                 return [0,10]
        case .uploadShopFormButton:  return [0,11]
        }
    }
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
gd08xxx
  • 338
  • 4
  • 11

3 Answers3

3

Another way of using hard coded TableView IndexPath's is to use an enum with static properties. The benefit of doing this is that if you reference the IndexPath's in various TableView delegate methods, and you need to change the IndexPath references, then you only have to change them once in the enum.

 private enum TableIndex {
    // Section 0: Information
    static let information =        IndexPath(row: 0, section: 0)
    // Section 1: Preferences
    static let notifications =      IndexPath(row: 0, section: 1)
    static let units =              IndexPath(row: 1, section: 1)
    static let hapticFeedback =     IndexPath(row: 2, section: 1)
    // Section 2: Links
    static let leaveReview =        IndexPath(row: 0, section: 2)
    static let support =            IndexPath(row: 1, section: 2)
    static let privacyPolicy =      IndexPath(row: 2, section: 2)
    static let disclaimer =         IndexPath(row: 3, section: 2)
}

and you can then reference them like this:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    switch indexPath {
    case TableIndex.information:
        // do something
    case TableIndex.notifications:
        // do something
    case TableIndex.units:
        // do something

        // etc

    default:
        break
    }
}
rbaldwin
  • 4,581
  • 27
  • 38
1

There are other ways but not necessarily better, anyways you can cut down the code to this.

enum TableViewCell: Int  {
    case shopImageView = 0
    case selectShopImageButton = 1
    case shopNameLocal = 2
    case shopNameEN = 3
    case addressLocal
    case addressEN
    case selectAddressButton
    case openingHours
    case datePickers
    case phone
    case email
    case uploadShopFormButton
    
    var indexPath: IndexPath {
        return [0, self.rawValue]
    }
}

Also keep in mind it is also depending on how are you using them, like where and how are you passing the arguments.

One of the ways .

extension IndexPath {
     init(using type: TableViewCell) {
        self.init(row: type.rawValue, section: 0)
    }
}

let indexPath = IndexPath(using: .shopNameEN)
pkamb
  • 33,281
  • 23
  • 160
  • 191
Mohmmad S
  • 5,001
  • 4
  • 18
  • 50
0
extension IndexPath : RawRepresentable {
    public init?(rawValue: (Int, Int)) {
        self.init(row: rawValue.0, section: rawValue.1)
    }
    public var rawValue: (Int, Int) {
        (self.row, self.section)
    }
}

// We can't use array literal for raw value of an enum, but we can use `String` literal instead.
extension IndexPath: ExpressibleByStringLiteral {
    public init(stringLiteral: StringLiteralType) {
        let a = stringLiteral.split(separator: ",")
        if let row = Int(a[0]), let section = Int(a[1]) {
             self.init(row: row, section: section)
        } else {
            self.init(row: 0, section: 0)
        }
    }
}
// We can use array literals to initialize `IndexPath` but not to define an enum case.
enum TableViewCell: IndexPath {
     case shopImageView = "0,0" // `String` literal
     case selectShopImageButton = "0,1" // `String` literal
}

print(TableViewCell.selectShopImageButton.rawValue)
Paul B
  • 3,989
  • 33
  • 46
  • This doesn't appear to fix the compiler errors in the question. Can IndexPath be used as the raw value of an enum with this extension? – pkamb Oct 30 '21 at 19:06
  • Yes, but raw values also must be expressible by `String` or number. I' ve updated the answer. Try it in playground, @pkamb . – Paul B Oct 30 '21 at 20:08