---
title: "Enum ونوع مخصص من نوع JSON بدائي"
description: "تعلّموا تقنيتين في Codable"
canonical: https://oozou.com/ar/blog/enum-custom-type-from-primitive-json-type-7
locale: ar
date: 2018-12-18
author: "OOZOU Team"
---

# Enum ونوع مخصص من نوع JSON بدائي

> تعلّموا تقنيتين في Codable

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

سنتعلم اليوم حالتين قد تحتاجون إلى التعامل معهما عند العمل مع Codable: تحليل (parsing) الـ Enum، وتحليل نوع مخصص من نوع بدائي مثل string.

# Enum

إذا كان للـ enum الخاص بكم raw value يمكن تمثيلها كـ Int أو String (وأظن أن هذا يغطي معظم الحالات)، فيمكننا جعل الـ enum متوافقًا مع Codable بإسناد raw value إليه.

لنفترض أن لديكم كائن JSON كهذا:

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

يمكنكم إنشاء struct في Swift كهذا:

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

وإذا كان لديكم enum لا يمكن تمثيله كـ String أو Int، فلا يزال بإمكانكم جعله متوافقًا مع Codable ما دامت الـ raw value نفسها متوافقة مع Codable.

# نوع مخصص من نوع بدائي
إذا كان لديكم نوع مخصص يمكن اشتقاقه من نوع JSON أساسي، فمثلًا لديكم كائن تُرسل فيه صورة على شكل string مرمّز بـ base64:

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

بما أن UIImage غير متوافق مع Codable ولا يمكننا جعله متوافقًا عبر extension، فلدينا طريقتان للتعامل مع ذلك:

1. إنشاء wrapper class آخر حول هذه الصورة.

```
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
```

يُفترض أن يكون الكود أعلاه مألوفًا لكم، مع فكرة رئيسية واحدة. فبدلًا من:

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

نستخدم:

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

لأننا نتعامل في هذه الحالة مع قيمة بدائية واحدة، وهي string بترميز base64. ثم يمكننا استخدام هذا الـ wrapper بهذه الطريقة:

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

2. إضافة encode/decode مخصص لـ UIImage في 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
```

ثم استخدامه مع encode و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
```

يُفترض أن تكفي هاتان الطريقتان لمعظم حالات الاستخدام لديكم.

---

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

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