আমি কীভাবে সুইফট 4 এ এনাম ডিকোডেবল করব?


157
enum PostType: Decodable {

    init(from decoder: Decoder) throws {

        // What do i put here?
    }

    case Image
    enum CodingKeys: String, CodingKey {
        case image
    }
}

এটি সম্পূর্ণ করার জন্য আমি কী রাখি? এছাড়াও, যাক আমি এটিতে পরিবর্তন করেছি case:

case image(value: Int)

আমি কীভাবে এটি ডিকোডেবলের সাথে সামঞ্জস্য করব?

সম্পাদনা এখানে আমার সম্পূর্ণ কোড (যা কাজ করে না)

let jsonData = """
{
    "count": 4
}
""".data(using: .utf8)!

        do {
            let decoder = JSONDecoder()
            let response = try decoder.decode(PostType.self, from: jsonData)

            print(response)
        } catch {
            print(error)
        }
    }
}

enum PostType: Int, Codable {
    case count = 4
}

চূড়ান্ত সম্পাদনা এছাড়াও, এটি এর মতো একটি এনামকে কীভাবে পরিচালনা করবে?

enum PostType: Decodable {
    case count(number: Int)
}

উত্তর:


262

এটি বেশ সহজ, কেবল ব্যবহার Stringবা Intকাঁচা মান যা নিখুঁতভাবে বরাদ্দ করা হয়েছে।

enum PostType: Int, Codable {
    case image, blob
}

imageএনকোড করা হয় 0এবংblob তে আছে1

অথবা

enum PostType: String, Codable {
    case image, blob
}

image"image"এবংblob তে এনকোড করা আছে"blob"


এটি কীভাবে ব্যবহার করবেন এটি এটি একটি সাধারণ উদাহরণ:

enum PostType : Int, Codable {
    case count = 4
}

struct Post : Codable {
    var type : PostType
}

let jsonString = "{\"type\": 4}"

let jsonData = Data(jsonString.utf8)

do {
    let decoded = try JSONDecoder().decode(Post.self, from: jsonData)
    print("decoded:", decoded.type)
} catch {
    print(error)
}

1
আপনার প্রস্তাবিত কোডটি চেষ্টা করেছি, তবে এটি কার্যকর হয় না। আমি দেখানোর জন্য তাদেরকে JSON আমি ডিকোড করার চেষ্টা করছি আমার কোড সম্পাদনা করেছেন
সারকথা দ্রুতগতি

8
একটি এনাম সম্পূর্ণরূপে এন-ডিকোড করা যায় না। এটি অবশ্যই একটি কাঠামো এম্বেড করা আবশ্যক। আমি একটি উদাহরণ যুক্ত করেছি।
ভাদিয়ান

আমি এটিকে সঠিক হিসাবে পতাকাঙ্কিত করব। কিন্তু উপরের প্রশ্নের একটি শেষ অংশ ছিল যার উত্তর দেওয়া হয়নি। আমার এনাম যদি এ রকম দেখত তবে কী হবে? (উপরে সম্পাদিত)
সারকথা দ্রুতগতি

আপনি যদি সম্পর্কিত প্রকারের সাথে এনাম ব্যবহার করেন তবে আপনাকে কাস্টম এনকোডিং এবং ডিকোডিং পদ্ধতিগুলি লিখতে হবে। দয়া করে এনকোডিং এবং
ডকোডিং

1
"একটি এনামকে পুরোপুরি এন-ডিকোড করা যায় না About" সম্পর্কে, এটির সমাধান হয়েছে বলে মনে হয় iOS 13.3। আমি পরীক্ষা করি iOS 13.3এবং iOS 12.4.3তারা আলাদা আচরণ করে। এর অধীনে iOS 13.3, এনাম সম্পূর্ণরূপে এন-ডিকোড করা যায়।
এচোলিউ

111

সম্পর্কিত প্রকারের সাহায্যে এনামগুলি কীভাবে তৈরি করা যায় Codable

এই উত্তরটি @ হাওয়ার্ড লোভ্যাট-এর অনুরূপ তবে একটি PostTypeCodableFormকাঠামো তৈরি করা এড়ানো যায় এবং এর পরিবর্তে অ্যাপল দ্বারা প্রদত্তKeyedEncodingContainer প্রকারটি সম্পত্তি হিসাবে ব্যবহার করে এবং এটি বয়লারপ্লেট হ্রাস করে।EncoderDecoder

enum PostType: Codable {
    case count(number: Int)
    case title(String)
}

extension PostType {

    private enum CodingKeys: String, CodingKey {
        case count
        case title
    }

    enum PostTypeCodingError: Error {
        case decoding(String)
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        if let value = try? values.decode(Int.self, forKey: .count) {
            self = .count(number: value)
            return
        }
        if let value = try? values.decode(String.self, forKey: .title) {
            self = .title(value)
            return
        }
        throw PostTypeCodingError.decoding("Whoops! \(dump(values))")
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .count(let number):
            try container.encode(number, forKey: .count)
        case .title(let value):
            try container.encode(value, forKey: .title)
        }
    }
}

এই কোডটি এক্সকোড 9 বি 3-তে আমার জন্য কাজ করে।

import Foundation // Needed for JSONEncoder/JSONDecoder

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let decoder = JSONDecoder()

let count = PostType.count(number: 42)
let countData = try encoder.encode(count)
let countJSON = String.init(data: countData, encoding: .utf8)!
print(countJSON)
//    {
//      "count" : 42
//    }

let decodedCount = try decoder.decode(PostType.self, from: countData)

let title = PostType.title("Hello, World!")
let titleData = try encoder.encode(title)
let titleJSON = String.init(data: titleData, encoding: .utf8)!
print(titleJSON)
//    {
//        "title": "Hello, World!"
//    }
let decodedTitle = try decoder.decode(PostType.self, from: titleData)

আমি এই উত্তর ভালবাসি! একটি নোট হিসাবে, এই উদাহরণে এছাড়াও প্রতিধ্বনিত হয় একটি পোস্ট objc.io উপর নির্মাণ সম্পর্কে Eithercodable
বেন Leggiero

সেরা উত্তর
পিটার সুওয়ারা

38

সুইফ্ট নিক্ষেপ করা হবে একটি .dataCorruptedযদি অজানা এনাম মানের মুখোমুখি হয় তবে ত্রুটি । যদি আপনার ডেটা কোনও সার্ভার থেকে আসে, তবে এটি আপনাকে যে কোনও সময় একটি অজানা এনাম মান প্রেরণ করতে পারে (বাগ সার্ভার সাইড, নতুন এপিআই সংস্করণে যুক্ত করা এবং আপনি আপনার অ্যাপ্লিকেশনটির পূর্ববর্তী সংস্করণগুলি কেসটি হ্যান্ডেল করতে চান ইত্যাদি), আপনার এনামগুলিকে নিরাপদে ডিকোড করতে আপনি আরও ভাল প্রস্তুত এবং কোড "ডিফেন্সিভ স্টাইল" রাখতে চান।

এটি কীভাবে করবেন, সম্পর্কিত মান সহ বা ছাড়াই এখানে একটি উদাহরণ

    enum MediaType: Decodable {
       case audio
       case multipleChoice
       case other
       // case other(String) -> we could also parametrise the enum like that

       init(from decoder: Decoder) throws {
          let label = try decoder.singleValueContainer().decode(String.self)
          switch label {
             case "AUDIO": self = .audio
             case "MULTIPLE_CHOICES": self = .multipleChoice
             default: self = .other
             // default: self = .other(label)
          }
       }
    }

এবং এটি কীভাবে একটি সংযুক্তি কাঠামোতে ব্যবহার করবেন:

    struct Question {
       [...]
       let type: MediaType

       enum CodingKeys: String, CodingKey {
          [...]
          case type = "type"
       }


   extension Question: Decodable {
      init(from decoder: Decoder) throws {
         let container = try decoder.container(keyedBy: CodingKeys.self)
         [...]
         type = try container.decode(MediaType.self, forKey: .type)
      }
   }

1
ধন্যবাদ, আপনার উত্তরটি বোঝা অনেক সহজ।
ড্যাজচং

1
ধন্যবাদ, এই উত্তরটি আমাকেও সাহায্য করেছে। আপনার এনামটি স্ট্রিংয়ের উত্তরাধিকারী করে এটি উন্নত করা যেতে পারে, তারপরে আপনার স্ট্রিংগুলির
ওপরে

27

@ টোকার উত্তরটি প্রসারিত করতে, আপনি খুব এনামগুলিতে একটি কাঁচা উপস্থাপনযোগ্য মান যুক্ত করতে পারেন এবং একটি ছাড়াই এনাম তৈরির জন্য ডিফল্ট alচ্ছিক নির্মাণকারী ব্যবহার করতে পারেন switch:

enum MediaType: String, Decodable {
  case audio = "AUDIO"
  case multipleChoice = "MULTIPLE_CHOICES"
  case other

  init(from decoder: Decoder) throws {
    let label = try decoder.singleValueContainer().decode(String.self)
    self = MediaType(rawValue: label) ?? .other
  }
}

এটি কাস্টম প্রোটোকল ব্যবহার করে বাড়ানো যেতে পারে যা নির্মাণকারীকে রিফ্যাক্টর করতে দেয়:

protocol EnumDecodable: RawRepresentable, Decodable {
  static var defaultDecoderValue: Self { get }
}

extension EnumDecodable where RawValue: Decodable {
  init(from decoder: Decoder) throws {
    let value = try decoder.singleValueContainer().decode(RawValue.self)
    self = Self(rawValue: value) ?? Self.defaultDecoderValue
  }
}

enum MediaType: String, EnumDecodable {
  static let defaultDecoderValue: MediaType = .other

  case audio = "AUDIO"
  case multipleChoices = "MULTIPLE_CHOICES"
  case other
}

কোনও মানকে ডিফল্ট করার পরিবর্তে যদি কোনও অবৈধ এনাম মান নির্দিষ্ট করা থাকে তবে এটি ত্রুটি ছোঁড়ার জন্য সহজেই বাড়ানো যেতে পারে। এই পরিবর্তনের সংক্ষিপ্তসারটি এখানে পাওয়া যায়: https://gist.github.com/stephanecopin/4283175fabf6f0cdaf87fef2a00c8128
কোডটি সুইফট ৪.১ / এক্সকোড ৯.৩ ব্যবহার করে সংকলিত এবং পরীক্ষিত হয়েছিল।


1
এই উত্তরটি আমি খুঁজতে এসেছি।
নাথান হোসেলটন

7

@ প্রক্সিপেরো প্রতিক্রিয়াটির একটি বৈকল্পিক যা টর্জার হ'ল ডিকোডারটি এইভাবে তৈরি করা হবে:

public init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    guard let key = values.allKeys.first else { throw err("No valid keys in: \(values)") }
    func dec<T: Decodable>() throws -> T { return try values.decode(T.self, forKey: key) }

    switch key {
    case .count: self = try .count(dec())
    case .title: self = try .title(dec())
    }
}

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    switch self {
    case .count(let x): try container.encode(x, forKey: .count)
    case .title(let x): try container.encode(x, forKey: .title)
    }
}

এটি সংকলককে কেসগুলি সম্পূর্ণরূপে যাচাই করার অনুমতি দেয় এবং সেই ক্ষেত্রে এনকোডযুক্ত মান কীটির প্রত্যাশিত মানের সাথে মেলে না এমন ক্ষেত্রে ত্রুটি বার্তাটিও দমন করে না।


আমি সম্মত যে এটি আরও ভাল।
প্রক্সিপে 0

6

প্রকৃতপক্ষে উপরের উত্তরগুলি দুর্দান্ত, তবে ক্রমাগত বিকাশিত ক্লায়েন্ট / সার্ভার প্রকল্পে বহু লোকের কী প্রয়োজন তার জন্য তারা কিছু বিশদ অনুপস্থিত। আমাদের ব্যাকেন্ডটি ক্রমাগতভাবে সময়ের সাথে বিকশিত হওয়ার সময় আমরা একটি অ্যাপ্লিকেশন বিকাশ করি যার অর্থ কিছু এনাম কেসগুলি সেই বিবর্তনকে পরিবর্তন করে দেয়। সুতরাং আমাদের এমন একটি এনাম ডিকোডিং কৌশল দরকার যা অজানা মামলাগুলি ধারণ করে এমন এনামগুলির অ্যারে ডিকোড করতে সক্ষম। অন্যথায় অ্যারে থাকা অবজেক্টটিকে ডিকোডিং করা সহজভাবে ব্যর্থ হয়।

আমি যা করেছি তা বেশ সহজ:

enum Direction: String, Decodable {
    case north, south, east, west
}

struct DirectionList {
   let directions: [Direction]
}

extension DirectionList: Decodable {

    public init(from decoder: Decoder) throws {

        var container = try decoder.unkeyedContainer()

        var directions: [Direction] = []

        while !container.isAtEnd {

            // Here we just decode the string from the JSON which always works as long as the array element is a string
            let rawValue = try container.decode(String.self)

            guard let direction = Direction(rawValue: rawValue) else {
                // Unknown enum value found - ignore, print error to console or log error to analytics service so you'll always know that there are apps out which cannot decode enum cases!
                continue
            }
            // Add all known enum cases to the list of directions
            directions.append(direction)
        }
        self.directions = directions
    }
}

বোনাস: বাস্তবায়ন লুকান> এটিকে একটি সংগ্রহ করুন

বাস্তবায়ন বিশদটি গোপন করা সর্বদা একটি ভাল ধারণা। এর জন্য আপনার আরও কিছুটা কোড দরকার। কৌশলটি হ'ল আপনার অভ্যন্তরীণ অ্যারেটি ব্যক্তিগতভাবে তৈরি DirectionsListকরা Collectionএবং তৈরি করা list:

struct DirectionList {

    typealias ArrayType = [Direction]

    private let directions: ArrayType
}

extension DirectionList: Collection {

    typealias Index = ArrayType.Index
    typealias Element = ArrayType.Element

    // The upper and lower bounds of the collection, used in iterations
    var startIndex: Index { return directions.startIndex }
    var endIndex: Index { return directions.endIndex }

    // Required subscript, based on a dictionary index
    subscript(index: Index) -> Element {
        get { return directions[index] }
    }

    // Method that returns the next index when iterating
    func index(after i: Index) -> Index {
        return directions.index(after: i)
    }
}

জন স্যান্ডেলের এই ব্লগ পোস্টে আপনি কাস্টম সংগ্রহগুলিকে মেনে চলার বিষয়ে আরও পড়তে পারেন: https://medium.com/@johnsundell/creating-custom-collections-in-swift-a344e25d0bb0


5

আপনি যা চান তা করতে পারেন তবে এটি কিছুটা জড়িত :(

import Foundation

enum PostType: Codable {
    case count(number: Int)
    case comment(text: String)

    init(from decoder: Decoder) throws {
        self = try PostTypeCodableForm(from: decoder).enumForm()
    }

    func encode(to encoder: Encoder) throws {
        try PostTypeCodableForm(self).encode(to: encoder)
    }
}

struct PostTypeCodableForm: Codable {
    // All fields must be optional!
    var countNumber: Int?
    var commentText: String?

    init(_ enumForm: PostType) {
        switch enumForm {
        case .count(let number):
            countNumber = number
        case .comment(let text):
            commentText = text
        }
    }

    func enumForm() throws -> PostType {
        if let number = countNumber {
            guard commentText == nil else {
                throw DecodeError.moreThanOneEnumCase
            }
            return .count(number: number)
        }
        if let text = commentText {
            guard countNumber == nil else {
                throw DecodeError.moreThanOneEnumCase
            }
            return .comment(text: text)
        }
        throw DecodeError.noRecognizedContent
    }

    enum DecodeError: Error {
        case noRecognizedContent
        case moreThanOneEnumCase
    }
}

let test = PostType.count(number: 3)
let data = try JSONEncoder().encode(test)
let string = String(data: data, encoding: .utf8)!
print(string) // {"countNumber":3}
let result = try JSONDecoder().decode(PostType.self, from: data)
print(result) // count(3)

আকর্ষণীয় হ্যাক
রোমান ফিলিপোভ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.