Custom Struct: Type does not conform to protocol 'Decodable'

|▌冷眼眸甩不掉的悲伤 提交于 2020-08-10 23:16:35

问题


I would like to be able to save a Custom-struct to UserDefaults but for that I need it to be Codable.. I tried it like this:

struct Wishlist: Codable {
var name: String
var image: UIImage
var wishData: [Wish]
var color: UIColor
var textColor: UIColor
var index: Int
}

But that gives me this error:

Type 'Wishlist' does not conform to protocol 'Decodable'

Here is my Class Wish , maybe that's where the problem is:

class Wish: NSObject {
public var wishName : String?
public var checkedStatus : Bool?
public var wishLink : String?
public var wishPrice : String?
public var wishNote : String?
public var wishImage : UIImage?

init(withWishName name: String, link: String, price: String, note: String, image: UIImage, checked: Bool) {
    super.init()
    wishName = name
    checkedStatus = checked
    wishLink = link
    wishPrice = price
    wishNote = note
    wishImage = image
}
}

What am I doing wrong here??


回答1:


You will need to make Wish adopt Codable.

But because UIImage and UIColor are not Codable, you’ll have to manually implement them as outlined in Encoding and Decoding Custom Types:

struct Wishlist: Codable {
    var name: String
    var image: UIImage
    var wishes: [Wish]
    var color: UIColor
    var textColor: UIColor
    var index: Int

    enum CodingKeys: String, CodingKey {
        case name, image, wishData, color, textColor, index
    }

    init(name: String, image: UIImage, wishes: [Wish], color: UIColor, textColor: UIColor, index: Int) {
        self.name = name
        self.image = image
        self.wishes = wishes
        self.color = color
        self.textColor = textColor
        self.index = index
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)

        name = try values.decode(String.self, forKey: .name)
        wishes = try values.decode([Wish].self, forKey: .wishData)
        color = try values.decode(Color.self, forKey: .color).uiColor
        textColor = try values.decode(Color.self, forKey: .textColor).uiColor
        index = try values.decode(Int.self, forKey: .index)

        let data = try values.decode(Data.self, forKey: .image)
        guard let image = UIImage(data: data) else {
            throw DecodingError.dataCorruptedError(forKey: .image, in: values, debugDescription: "Invalid image data")
        }
        self.image = image
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(name, forKey: .name)
        try container.encode(wishes, forKey: .wishData)
        try container.encode(Color(uiColor: color), forKey: .color)
        try container.encode(Color(uiColor: textColor), forKey: .textColor)
        try container.encode(index, forKey: .index)
        try container.encode(image.pngData(), forKey: .image)
    }

}

struct Wish: Codable {
    public var name: String
    public var checkedStatus: Bool
    public var link: String
    public var price: String
    public var note: String
    public var image: UIImage

    init(name: String, link: String, price: String, note: String, image: UIImage, checkedStatus: Bool) {
        self.name = name
        self.checkedStatus = checkedStatus
        self.link = link
        self.price = price
        self.note = note
        self.image = image
    }

    enum CodingKeys: String, CodingKey {
        case name, checkedStatus, link, price, note, image
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)

        name = try values.decode(String.self, forKey: .name)
        checkedStatus = try values.decode(Bool.self, forKey: .checkedStatus)
        link = try values.decode(String.self, forKey: .link)
        price = try values.decode(String.self, forKey: .price)
        note = try values.decode(String.self, forKey: .note)

        let data = try values.decode(Data.self, forKey: .image)
        guard let image = UIImage(data: data) else {
            throw DecodingError.dataCorruptedError(forKey: .image, in: values, debugDescription: "Invalid image data")
        }
        self.image = image
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(name, forKey: .name)
        try container.encode(checkedStatus, forKey: .checkedStatus)
        try container.encode(link, forKey: .link)
        try container.encode(price, forKey: .price)
        try container.encode(note, forKey: .note)
        try container.encode(image.pngData(), forKey: .image)
    }
}

Where I’d use this as a convenient way to encode UIColor objects:

struct Color: Codable {
    let red: CGFloat
    let green: CGFloat
    let blue: CGFloat
    let alpha: CGFloat

    init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        self.red = red
        self.green = green
        self.blue = blue
        self.alpha = alpha
    }

    init(uiColor: UIColor) {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0

        uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

        self.red = red
        self.green = green
        self.blue = blue
        self.alpha = alpha
    }

    var uiColor: UIColor { UIColor(red: red, green: green, blue: blue, alpha: alpha) }
}

Note, I did a couple of unrelated changes:

  • I made both of these struct. I wouldn’t introduce reference types (much less NSObject subclasses) unless necessary.

  • I simplified some of the property names. E.g. in Wish, we wouldn’t generally use wish prefix in property names. I also wouldn’t use “data” in a property name unless it was, in fact, a Data.

  • I updated init methods to use standard naming conventions.




回答2:


Also your class Wish have to implement Codable protocol




回答3:


UIImage does not conform to Codable. You could convert it to Base64 first and then store that in UserDefaults.




回答4:


I got the same error before, but then I realized that the CodingKey doesn't match with the struct. or there is Any Data type, just change the data type to your custom object.

for example:

public struct DtClip: Codable {

    // MARK: Properties
    public var video: String?
    public var preview: String?
    public var clip: String?
    public var trailer: String?

    enum CodingKeys: String, CodingKey {
        case video = "video"
        case preview = "preview"
        case clip = "clip"
    }
}

from the example we know that trailer isn't in the codingKeys yet. You should add it to CodingKeys too.

public struct DtClip: Codable {

    // MARK: Properties
    public var video: String?
    public var preview: String?
    public var clip: String?
    public var trailer: Any?

    enum CodingKeys: String, CodingKey {
        case video = "video"
        case preview = "preview"
        case clip = "clip"
    }
}

from the second example, Any should be changed to another data type like String, Int, or Trailer (custom data type).



来源:https://stackoverflow.com/questions/61548074/custom-struct-type-does-not-conform-to-protocol-decodable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!