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 ??
4 Réponses :
Votre classe souhaite également mettre en œuvre un protocole codable
Si j'ajoute Codable
à Wish
j'obtiens la même erreur dans Wish
que dans la Wishlist
de Wishlist
UIImage
n'est pas conforme à Codable
. Vous pouvez le convertir en base64 d' abord, puis stocker que dans UserDefaults
.
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? :)
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.
c'est une réponse 10/10. Merci! Excellente explication également, appréciez vraiment votre aide :)
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" } }
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: leType 'Wish' does not conform to protocol 'Decodable'
et'super' members cannot be referenced in a root class
UIImage
etUIColor
ne sont pas conformes àCodable
.UIImage
surData
etUIColor
sur une valeur de chaîne hexadécimale ou un tableau deDouble
. 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 maclass
? Et je changerai l'optinal
!Voici un exemple pour
UIColor
et en voici un pourUIImage
Par curiosité, pourquoi
Wish
est-il uneclass
et pourquoiWishlist
est-il unestruct
? Et même si vous vouliez faire deWish
uneclass
, 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
à laWishlist
deWishlist
?Chaque type qui va être encodé / décodé doit être conforme à Codable
@vadian bien, ça a du sens