---
title: "Enum & custom type from primitive JSON type"
description: "Learn two techniques in Codable"
canonical: https://oozou.com/blog/enum-custom-type-from-primitive-json-type-7
locale: en
date: 2018-12-18
author: "OOZOU Team"
---

# Enum & custom type from primitive JSON type

> Learn two techniques in Codable

Published: 2018-12-18 · Author: OOZOU Team (Engineering) · 4 min read

Today we are going to learn how two cases that you might need to do when dealing with Codable. Parsing Enum and Parsing custom type from a primitive type like string.

# Enum

If your enum has a raw value that can be represented as Int or String (which I think that should cover most cases). We can make enum Codable by assigning a raw value to it.

Let’s say you have JSON object like this:

```
{"name": "iOS developer", "status": "open"}
lang-json
```

You can create Swift struct like this:

```
<pre>struct Job: Codable {
  enum Status: String, Codable {
    case open
    case close
  }
  let name: String
  let status: Status
}
lang-swift
```

If you have enum which isn’t String or Int representable you can still make it conform to Codable as long as that raw value is Codable.

# Custom type from a primitive type
If you have a custom type that can be derived from basic JSON type, for example, you have an object with image sending as base64 encoded string:

```
{
  "name": "XXX",
  "image": "data:image/png;base64,iVBORw0KGgo......."
}
lang-json
```

Since UIImage doesn’t conform to Codable and we can’t conform it with an extension, we have 2 ways to handle this:

1. Create another wrapper class around this image.

```
struct Base64ImageWrapper: Codable {
    let image: UIImage

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()        
        let base64String = try container.decode(String.self)        
        let components = base64String.split(separator: ",")        
        if components.count != 2 {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Wrong data format")
        }

        let dataString = String(components[1])

        if let dataDecoded = Data(base64Encoded: dataString, options: .ignoreUnknownCharacters),
            let image = UIImage(data: dataDecoded) {
            self.image = image
        } else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Can't initialize image from data string")
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        let data = image.jpegData(compressionQuality: 1)
        let prefix = "data:image/jpeg;base64,"
        guard let base64String = data?.base64EncodedString(options: .lineLength64Characters) else {
            throw EncodingError.invalidValue(image, EncodingError.Context(codingPath: [], debugDescription: "Can't encode image to base64 string."))
        }

        try container.encode(prefix + base64String)
    }
}
lang-swift
```

The code above should be familiar to you with one key take away. Instead of:

```
let values = try decoder.container(keyedBy: CodingKeys.self)
lang-swift
```

We use:

```
let container = try decoder.singleValueContainer()
lang-swift
```

since we are dealing with one primitive value, base64 string, in this case. Then we can use this wrapper like this:

```
<pre>struct ModelUsingImageWrapper: Codable {
  let name: String
  let image: Base64ImageWrapper
}
lang-swift
```

2. Add custom encode/decode for UIImage in KeyedEncodingContainer:

```
extension KeyedEncodingContainer {

    mutating func encode(_ value: UIImage,
                         forKey key: KeyedEncodingContainer.Key) throws {

        let imageData = value.jpegData(compressionQuality: 1)
        let prefix = "data:image/jpeg;base64,"        

        guard let base64String = imageData?.base64EncodedString(options: .lineLength64Characters) else {
            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: "Can't encode image to base64 string."))
        }

        try encode(prefix + base64String, forKey: key)
    }

}

extension KeyedDecodingContainer {

    public func decode(_ type: UIImage.Type, forKey key: KeyedDecodingContainer.Key) throws -> UIImage {
        let base64String = try decode(String.self, forKey: key)

        // data:image/svg+xml;base64,PD.....
        let components = base64String.split(separator: ",")

        if components.count != 2 {
            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Unsupported format"))
        }

        let dataString = String(components[1])
        if let dataDecoded = Data(base64Encoded: dataString, options: .ignoreUnknownCharacters), let image = UIImage(data: dataDecoded) {
            return image
        } else {
            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Unsupported format"))
        }
    }

}
lang-swift
```

And use this with explicit encode and decode.

```
struct ModelUsingKeyedEncodingContainer: Codable {
  let name: String
  let image: UIImage<pre>  enum CodingKeys: String, CodingKey {
    case name
    case image
  }
  public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decode(String.self, forKey: .name)
    image = try container.decode(UIImage.self, forKey: .image)
  }
  public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(name, forKey: .name)
    try container.encode(image, forKey: .image)
  }
}
lang-swift
```

These 2 approaches should suffice for most of your use cases.

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
