0
votes

Structure personnalisée: le type n'est pas conforme au protocole 'Decodable'

Je voudrais pouvoir enregistrer un Custom-struct dans UserDefaults mais pour cela j'ai besoin qu'il soit Codable .. Je l'ai essayé comme ceci:

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

Mais cela me donne cette error :

Le type «Liste de souhaits» n'est pas conforme au protocole «Décodable»

Voici mon Class Wish , c'est peut-être là que se situe le problème:

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

Qu'est-ce que je fais de mal ici ??


10 commentaires

au lieu de «Struct Wishlist: Codable». make it Struct Wishlist: Décodable. Si cela ne résout pas le problème, faites de votre souhait de classe: codable ou décodable, pas NSObject


ne résout pas le problème. Si je crée Wish Codable, j'obtiens cette erreur: le Type 'Wish' does not conform to protocol 'Decodable' et 'super' members cannot be referenced in a root class


UIImage et UIColor ne sont pas conformes à Codable . UIImage sur Data et UIColor sur une valeur de chaîne hexadécimale ou un tableau de Double . Et ne déclarez jamais les propriétés comme facultatives qui sont initialisées avec des valeurs non facultatives.


@vadian merci pour la réponse, pourriez-vous peut-être élaborer là-dessus? À quoi cela ressemblerait-il dans ma struct et ma class ? Et je changerai l' optinal !


Voici un exemple pour UIColor et en voici un pour UIImage


Par curiosité, pourquoi Wish est-il une class et pourquoi Wishlist est-il une struct ? Et même si vous vouliez faire de Wish une class , pourquoi sous- class NSObject ?


@Rob tbh il n'y a aucune raison. Je pourrais aussi bien créer les deux structs . Je ne sais pas vraiment quand utiliser quoi exactement, mais ils fonctionnent tous les deux très bien, donc je n'ai pas vraiment dérangé. Que me suggéreriez-vous de faire?


@vadian et en supposant que je l'ai fait, qu'aurais-je à ajouter alors? Juste Codable à la Wishlist de Wishlist ?


Chaque type qui va être encodé / décodé doit être conforme à Codable


@vadian bien, ça a du sens


4 Réponses :


0
votes

Votre classe souhaite également mettre en œuvre un protocole codable


1 commentaires

Si j'ajoute Codable à Wish j'obtiens la même erreur dans Wish que dans la Wishlist de Wishlist



0
votes

UIImage n'est pas conforme à Codable . Vous pouvez le convertir en base64 d' abord, puis stocker que dans UserDefaults .


2 commentaires

Ou, récupérez simplement les Data (que vous auriez besoin d'encoder en base64, de toute façon) et stockez-les directement et contournez l'encodage manuel en base64.


@Rob, il y a plusieurs suggestions ici, mais vous semblez avoir une assez bonne compréhension de ce que je dois faire ici. Pourriez-vous peut-être élaborer? :)



1
votes

Vous devrez faire adopter par Wish Codable .

Mais comme UIImage et UIColor ne sont pas Codable , vous devrez les implémenter manuellement comme indiqué dans Encodage et décodage des types personnalisés :

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) }
}

Là où j'utiliserais ceci comme moyen pratique d'encoder des objets UIColor :

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)
    }
}

Remarque, j'ai fait quelques changements non liés:

  • J'ai fait ces deux struct . Je n'introduirais pas de types de référence (encore moins de sous-classes NSObject ) à moins que cela ne soit nécessaire.

  • J'ai simplifié certains des noms de propriétés. Par exemple, dans Wish , nous n'utiliserions généralement pas le préfixe wish dans les noms de propriétés. Je n'utiliserais pas non plus «données» dans un nom de propriété à moins qu'il ne s'agisse, en fait, de Data .

  • J'ai mis à jour les méthodes init pour utiliser les conventions de dénomination standard.


1 commentaires

c'est une réponse 10/10. Merci! Excellente explication également, appréciez vraiment votre aide :)



1
votes

dans votre cas, vous devez ajouter CodingKeys énumération CodingKeys et ne pas utiliser le type de données UIColor ni UIImage . J'ai eu la même erreur avant, mais j'ai ensuite réalisé que CodingKey ne correspond pas à la structure ni qu'il n'y a de type de données non-codable . changez simplement le type de données en votre objet codable personnalisé.

mauvais exemple:

public struct DtClip: Codable {

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

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

public struct Trailer: Codable {

    // MARK: Properties
    public var name: String?
    public var id: Int?

    enum CodingKeys: String, CodingKey {
       case name, url
    }
}

à partir de l'exemple, nous savons que la trailer - trailer n'est pas encore dans les codingKeys . Vous devez ajouter tous les accessoires aux CodingKeys . Et Any type de données doit être remplacé par un type de données codable tel que String , Int ou Trailer (type de données codable personnalisé). ci-dessous est l'exemple correct:

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"
    }
}


0 commentaires