cat swift/equatable-hashable-comparable-codable.md
Equatable, Hashable, Comparable e Codable
Le conformità standard rendono i tipi Swift più utili, confrontabili, ordinabili, serializzabili e integrati nell'ecosistema.
Molto Swift idiomatico nasce da conformità standard. Quattro sono particolarmente importanti:
Equatable;Hashable;Comparable;Codable.
Non sono decorazioni. Sono contratti che dicono cosa un tipo sa fare.
Equatable: confrontare valori
Equatable permette di usare ==.
struct Prodotto: Equatable {
let id: String
let nome: String
}
let a = Prodotto(id: "p-1", nome: "Libro")
let b = Prodotto(id: "p-1", nome: "Libro")
print(a == b) // true
Swift può generare automaticamente la conformità se tutte le proprietà sono Equatable.
Identità di dominio vs uguaglianza strutturale
Attenzione: due prodotti con stesso id ma nome diverso sono uguali?
struct Prodotto: Equatable {
let id: String
let nome: String
static func == (lhs: Prodotto, rhs: Prodotto) -> Bool {
lhs.id == rhs.id
}
}
Questa scelta dipende dal dominio.
Uguaglianza strutturale: tutte le proprietà uguali.
Uguaglianza per identità di dominio: stesso id.
Sii esplicito quando la scelta non è ovvia.
Hashable: usare Set e Dictionary
Hashable permette di usare un tipo in Set o come chiave di Dictionary.
struct CodiceCoupon: Hashable {
let valore: String
}
let codici: Set<CodiceCoupon> = [
CodiceCoupon(valore: "SWIFT10"),
CodiceCoupon(valore: "LIBRI20")
]
Anche Hashable può essere sintetizzato automaticamente se tutte le proprietà sono Hashable.
Hashable richiede coerenza con Equatable
Se due valori sono uguali secondo ==, devono avere lo stesso hash.
Di solito lascia sintetizzare a Swift. Se implementi manualmente, fallo con attenzione.
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
Se == confronta solo id, anche l’hash deve usare solo id.
Comparable: ordinare
Comparable permette di usare <, sorted() e confronti ordinati.
struct Prezzo: Comparable {
let centesimi: Int
static func < (lhs: Prezzo, rhs: Prezzo) -> Bool {
lhs.centesimi < rhs.centesimi
}
}
Ora:
let prezziOrdinati = prezzi.sorted()
Usa Comparable solo quando esiste un ordine naturale.
Per prodotti, l’ordine naturale potrebbe non essere ovvio: nome? prezzo? data? rilevanza? In quel caso preferisci sorted(by:).
Codable: serializzare
Codable combina Encodable e Decodable.
struct Prodotto: Codable {
let id: String
let nome: String
let prezzoInCent: Int
}
Swift può generare encoder e decoder se le proprietà sono Codable.
CodingKeys
Quando il JSON usa nomi diversi:
struct Prodotto: Codable {
let id: String
let nome: String
let prezzoInCent: Int
enum CodingKeys: String, CodingKey {
case id
case nome = "name"
case prezzoInCent = "price_cents"
}
}
Codable diventa molto pratico nei moduli su networking e persistenza.
Codable non è sempre dominio
Attenzione: il modello JSON non coincide sempre con il modello di dominio.
struct ProdottoDTO: Decodable {
let id: String
let name: String
let priceCents: Int
}
Poi converti:
extension ProdottoDTO {
func toDomain() -> Prodotto? {
guard let prezzo = Prezzo(priceCents) else {
return nil
}
return Prodotto(id: id, nome: name, prezzo: prezzo)
}
}
Separare DTO e dominio è spesso più pulito quando la API è instabile o sporca.
Conformità in extension
Puoi organizzare così:
struct Coupon {
let codice: CodiceCoupon
let sconto: Sconto
}
extension Coupon: Equatable {}
extension Coupon: Hashable {}
extension Coupon: Codable {}
Va bene se migliora la leggibilità. Non serve spezzare tutto per forza.
Checklist
Usa Equatable quando:
- devi confrontare valori;
- vuoi test più leggibili;
- vuoi verificare risultati attesi.
Usa Hashable quando:
- il tipo entra in
Set; - il tipo è chiave di
Dictionary; - ti serve deduplicazione.
Usa Comparable quando:
- esiste un ordine naturale.
Usa Codable quando:
- devi leggere o scrivere dati;
- il modello esterno è abbastanza vicino al dominio.
Esercizio
Nel dominio coupon:
- rendi
PrezzoComparable; - rendi
CodiceCouponHashable; - rendi
CouponEquatable; - crea
ProdottoDTO: Decodable; - scrivi
toDomain()che converte DTO inProdotto?.
Poi spiega dove hai scelto sintesi automatica e dove implementazione manuale.