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
WishCodable, j'obtiens cette erreur: leType 'Wish' does not conform to protocol 'Decodable'et'super' members cannot be referenced in a root classUIImageetUIColorne sont pas conformes àCodable.UIImagesurDataetUIColorsur 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
structet maclass? Et je changerai l'optinal!Voici un exemple pour
UIColoret en voici un pourUIImagePar curiosité, pourquoi
Wishest-il uneclasset pourquoiWishlistest-il unestruct? Et même si vous vouliez faire deWishuneclass, pourquoi sous-classNSObject?@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à laWishlistdeWishlist?Chaque type qui va être encodé / décodé doit être conforme à Codable
@vadian bien, ça a du sens