skip to Main Content

Consider the following simple JSON:

{
  "title": "Some title",
  "subtitle": "Some subtitle",
  "button_type": "rounded"
}

This is my current approach towards decoding the buttonType field:

// I dont have access to this enums implementation as it comes from a 3rd party lib.
enum ButtonType {
    case squared
    case simple
    case rounded
    case normal
}

struct ResponseModel: Decodable {
    var title: String
    var subtitle: String
    var type: ButtonType
    
    enum CodingKeys: String, CodingKey {
        case title = "title"
        case subtitle = "subtitle"
        case type = "button_type"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        title = try container.decode(String.self, forKey: .title)
        subtitle = try container.decode(String.self, forKey: .subtitle)
        let stringType = try container.decode(String.self, forKey: .type)
        switch stringType {
        case "squared":
            type = .squared
        case "simple":
            type = .simple
        case "rounded":
            type = .rounded
        default:
            type = .normal
        }
    }
}

Is there any prettier way to accomplish decoding the string to the custom enum without that nasty switch statement iterating over a plain string?. I sadly do not have access to the enums implementation as it comes from a third party library. If I did I would just make the enum conform to String & Codable and have Codable work its magic, but I dont.

Thanks!

2

Answers


  1. You can create your own enum

    enum MyButtonType: String {
        case squared
        case simple
        case rounded
        case normal
    
        var toButtonType: ButtonType {
             switch self {
                  case .squared: return .squared
                  case .simple: return .simple
                  case .rounded: return .rounded
                  case .normal: return .normal
             }
        }
    }
    

    Then change

     var type: ButtonType
    

    To

     var type: MyButtonType
    

    And when you need the custom enum just use

    responseModel.type.toButtonType
    

    The String after the : in the enum makes it conform to RawRepresentable<String> you won’t need the custom init anymore.

    Login or Signup to reply.
  2. Just another solution.

    Even if you don’t control the how ButtonType is defined, you can still conform it to Decodable using an extension.

    Step 1: Conform ButtonType to Decodable

    extension ButtonType: Decodable {
      init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let text = try container.decode(String.self)
        switch text {
        case "squared": self = .squared
        case "simple": self = .simple
        case "rounded": self = .rounded
        case "normal": self = .normal
        default: throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Cannot initialize ButtonType from invalid String value (text)"))
        }
      }
    }
    

    Step 2: Use it in your ResponseModel

    struct ResponseModel: Decodable {
        var title: String
        var subtitle: String
        var type: ButtonType
        
        enum CodingKeys: String, CodingKey {
            case title = "title"
            case subtitle = "subtitle"
            case type = "button_type"
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search