Les types opaques en Swift, aussi appelés types de retour opaques, sont apparus avec Swift 5.1. Ils simplifient l’utilisation des protocoles et des génériques, notamment avec les protocoles à types associés. Cet article explique ce que sont les types opaques et comment les utiliser.
Problèmes avec les protocoles et les génériques
Lorsqu’une fonction retourne un protocole avec un type associé, il est difficile de déterminer le type de données concret de la valeur retournée. Exemple :
protocol P {
associatedtype Value
var value: Value { get }
init(value: Value)
}
struct S1 : P {
var value: Int
}
struct S2: P {
var value: String
}
// Erreur: Impossible de déduire le type de retour
// func foo() -> P { return S1() }
L’utilisation de génériques a aussi ses limites car il faut spécifier le type de données pour le paramètre ou la valeur de retour de la fonction :
func foo<T: P>(value: T.Value) -> T { return T(value: value) }
let s1: S1 = foo(value: 10)
let s2: S2 = foo(value: "ahihi")
Solution avec les types opaques
Les types opaques, déclarés avec le mot-clé some
, permettent à une fonction de retourner un type de données « masqué » qui doit seulement se conformer au protocole, sans être spécifié.
func foo() -> some P { return S1(value: 11) }
Dans cet exemple, some P
représente un type de données concret qui respecte le protocole P
, que le compilateur déduira automatiquement.
Cependant, un type opaque ne peut retourner qu’un seul type de données dans une même fonction :
// Erreur: Impossible de retourner plusieurs types de données différents
// func bar(_ x: Int) -> some P {
// if x > 10 { return S1(value: 12) }
// else { return S2(value: "ahihi") }
// }
Types opaques et protocoles à types associés
Les types opaques sont particulièrement utiles avec les protocoles à types associés. Ils permettent d’utiliser ces protocoles comme type de retour sans connaître le type de données concret, rendant le code plus flexible et moins dépendant des versions de librairies.
func giveMeACollection() -> some Collection { return [1, 2, 3] }
let collection = giveMeACollection()
print(collection.count) // 3
Types opaques et type concret
Lorsqu’une fonction avec un type opaque retourne un type concret, le compilateur garantit que les appels à cette fonction retournent toujours le même type.
func fooo() -> some Equatable { return 5 }
let f1 = fooo()
let f2 = fooo()
print(f1 == f2) // true
Cependant, cela ne s’applique pas aux protocoles à types associés. Chaque fonction avec un type opaque et un protocole à type associé retournera un type de données distinct, même s’ils respectent le même protocole.
Types opaques et espaces réservés génériques
Combiner les types opaques avec des espaces réservés génériques permet de créer des fonctions à la fois polyvalentes avec plusieurs types de données et sans avoir à retourner un type concret.
Pourquoi utiliser les types opaques ?
Les types opaques offrent une flexibilité accrue lors de l’utilisation de protocoles, en particulier les protocoles à types associés. Ils évitent de spécifier un type de données concret, réduisent la dépendance aux versions de librairies et améliorent la réutilisabilité du code.
Types opaques dans SwiftUI
Les types opaques sont largement utilisés dans SwiftUI. La propriété body
du protocole View
a le type some View
, permettant de retourner n’importe quel type de données conforme au protocole View
, simplifiant ainsi la construction de l’interface utilisateur. L’utilisation de some View
évite les types de données complexes lorsque la vue a plusieurs niveaux d’imbrication.