• 10 heures
  • Facile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 23/04/2024

Comprenez les optionnels

Chose promise, chose due ! Je vais vous raconter la fabuleuse histoire des optionnels ! Et nous allons pouvoir modifier notre programme grâce à eux.

Suivez une histoire de licornes…

Il était une fois un dictionnaire (toutes les bonnes histoires commencent par Il était une fois, non ?). Ce dictionnaire contenait le nombre de licornes se trouvant dans chaque pays, comme ceci :

var nombreDeLicornes = ["France": 165, "USA": 38, "Italie": 102]

Très heureux de sa mission, notre dictionnaire coulait des jours heureux. Dans son travail, les programmes lui demandaient toujours des informations qu’il pouvait donner. Par exemple, on lui disait : “Donne-moi le nombre de licornes en France”. Et avec plaisir, notre ami dictionnaire s’exécutait.

nombreDeLicornes["France"] // Il renvoyait 165

Mais un jour, un programme vicieux le piégea : « Donne-moi le nombre de licornes au Groenland ».

Notre dictionnaire pris de panique par cette demande tenta l’impossible avec cette instruction :

nombreDeLicornes["Groenland"]

Mais bien sûr cette commande ne retourna rien, et le programme planta.

Personne ne se maria ni n’eut beaucoup d’enfants.

THE END

Bon OK, c’était une histoire tragique… Mais dans la vie d’un programmeur, tout n’est pas rose. Autant vous y faire tout de suite !

C’est après avoir été traumatisé pendant 50 ans par cette histoire que les créateurs de Swift ont fini par inventer les optionnels ! Vous pouvez déjà les remercier.

Parce qu’en fait, cette histoire ne se limite pas aux dictionnaires. Par exemple, je suis sûr que vous vous êtes déjà inscrit sur de nombreux sites web qui demandaient à ce que vous remplissiez un paquet d’informations. Parmi celles-ci, il y en avait qui étaient obligatoires, comme votre e-mail ou votre mot de passe. Mais souvent on vous propose d’en remplir d’autres facultatives, comme votre adresse ou votre téléphone. Et si vous êtes comme moi, vous ne les remplissez pas.

Le problème, c’est qu’un programmeur innocent comptait peut-être sur vous pour remplir votre numéro de téléphone pour pouvoir l’afficher sur l’application ou pour vous envoyer un SMS. Et donc quelque part dans son programme, il a une variable  téléphone  qui ne contient aucune valeur !

var telephone = // Rien !

Et si jamais il essaie d’utiliser cette variable sans valeur (ou autrement dit cette variable non initialisée), son programme va planter, comme vous le savez.

D’où les optionnels !

Découvrez les optionnels, concrètement !

MAIS C’EST QUOI ???

OK, j’ai peut-être fait un peu durer le suspens… En fait, un optionnel c’est un type un peu spécial. C’est le seul type qui autorise une variable à être utilisée sans valeur. Vous vous souvenez du cycle de vie d’une variable :

  1. La variable est déclarée. À ce moment, elle a un nom et un type, et on sait si elle est constante ou non. Comme la variable n’a pas de valeur à ce moment-là, elle ne peut pas être utilisée.

  2. La variable est initialisée. Elle a une valeur. La variable peut être utilisée.

On ne peut donc pas utiliser une variable qui n’a pas été initialisée. Mais avec les optionnels, si ! Car ils autorisent une variable à ne pas avoir de valeur.

On va voir tout de suite à quoi ça ressemble. Pour déclarer un optionnel, on utilise le point d’interrogation   ?    à la suite du type. Comme ceci :

var jeSuisOptionnel: Int?     // Ceci est un optionnel
var jeNeSuisPasOptionnel: Int  // Ceci est un entier

Les deux variables ci-dessus ne sont pas initialisées. Elles n’ont pas de valeur. Essayons de les utiliser :

var jeSuisOptionnel: Int?      // Ceci est un optionnel
jeSuisOptionnel                // Le Playground affiche nil
var jeNeSuisPasOptionnel: Int  // Ceci est un entier
jeNeSuisPasOptionnel           // Le Playground plante…

Pour la variable classique, le programme plante, car on essaie d’accéder à la valeur d’une variable qui n’en a pas ! C’est interdit par Swift. Mais c’est autorisé pour les optionnels. Et donc pour la variable  jeSuisOptionnel  , le programme renvoie  nil  .

nil ?

nil  en Swift, c’est un mot clé qui veut dire nada / rien / que dalle / zip / pas de valeur. Et c’est bien le cas, notre variable  jeSuisOptionnel  ne contient rien.

Et à quoi ressemble un optionnel qui a une valeur ? Essayons !

var jeSuisOptionnel: Int? = 12
print(jeSuisOptionnel)

Dans la console vous devriez voir :

Optional(12)

Il y a bien écrit  Optional(12)  et pas  12  !

Pourquoi ?

Pour une raison très simple, mais cruciale pour que vous compreniez correctement les optionnels.

La variable  jeSuisOptionnel  N’EST PAS DU TYPE  Int  MAIS DU TYPE  Optional. Je répète : La variable  jeSuisOptionnel  N’EST PAS DU TYPE  Int  MAIS DU TYPE  Optional  . 

Pour être sûr que le message soit bien passé, je vais vous faire un petit cadeau.

Un optionnel, c’est un paquet cadeau

Eh oui, un optionnel c’est comme un paquet cadeau qui englobe une valeur. Si on vous offre un paquet cadeau, vous ne savez pas ce qu’il y a dedans avant de l’ouvrir. Plus précisément, à l’intérieur :

  • Soit il n’y a rien. Quelqu’un vous a fait une mauvaise blague…

  • Soit il y a quelque chose mais vous ne savez pas quoi avant de l’ouvrir. Joyeux anniversaire !

Un optionnel se comporte exactement comme l’explique le schéma suivant.

Quand on ouvre le paquet var x: String?,  il peut soit contenir rien (valeur nil), soit contenir quelque chose (une valeur de type String).

Dans le schéma précédent,   x    est un optionnel et non une string ! Le type  String?  ne doit pas se lire : « Je suis peut-être une string » mais « Je suis un optionnel qui contient peut-être une string ».  

Déballez le cadeau

Les optionnels sont bien pratiques pour gérer des variables qui peuvent ne pas avoir de valeur. Mais alors comment accède-t-on à leur valeur ? Il existe 6 méthodes :

  • 2  méthodes sécurisées ; 

  • 3 méthodes alternatives ;

  • 1 méthode non sécurisée.

Première méthode sécurisée et recommandée : la déclaration optionnelle avec if let

La première  méthode sécurisée est plus subtile, mais aussi plus pratique ! C’est la raison pour laquelle c’est aussi la plus utilisée ! Elle s’utilise comme ceci :

if let variableDéballée = variableOptionnelle {
   print("La variable déballée vaut \(variableDéballée)")
}

Je vais détailler ce qu’il se passe ici :

  1. Le programme regarde si la variable  variableOptionnelle  vaut  nil  .

  2. Si elle ne contient pas nil, le programme crée une variable constante nommée  variableDéballée  .

  3. La valeur de la variable déballée est attribuée à la nouvelle constante  variableDéballée  .

L’avantage de cette méthode, c’est que la variable nouvellement créée n’est pas de type optionnel. C’est une variable classique qu’on peut donc utiliser normalement. On appelle cela une déclaration optionnelle (ou optional binding, en anglais). C’est très courant en Swift.

Un jour vous repenserez à ce jour où vous n’utilisiez pas if let, et vous aurez un sourire nostalgique (éventuellement en pensant à votre prof !).

Deuxième méthode sécurisée et recommandée :  la déclaration optionnelle avec guard let / else

Swift nous donne une alternative à la première méthode sécurisée appelée  guard let   avec  `else`  , qui déballe également les optionnels s'ils contiennent une valeur. Mais cette méthode fonctionne légèrement différemment :  guard let   est conçu pour quitter la fonction, la boucle ou la condition actuelle si la vérification échoue. Toutes les valeurs que vous déballerez en l'utilisant persisteront donc même en dehors du bloc de code (aussi appelé “scope”) servant au déballage de l’optionnel.

Cette méthode est aussi très populaire ! :)

Kézako ? Mais c’est quoi la différence entre les deux méthodes, du coup ?

Pour résumer :

  • Avec  guard let  / else  , vous créez une nouvelle variable qui existera toujours en dehors de l'instruction else  .

  • Avec  if let  , vous ne créez pas de nouvelle variable. Le déballage se fait uniquement si l’optionnel a une valeur différente de nil  . La variable nouvellement créée n'existe qu'à l'intérieur du bloc de code mais plus après ! ;)

Pour vous démontrer la différence entre ces 2 méthodes sécurisées, voici un exemple. Prenons une fonction qui renvoie la réponse sur la grande question sur la vie sous la forme d'un entier facultatif :

func reponseUniverselle() -> Int? {
    42
}

Et voici une autre fonction qui va nous permettre de l’afficher :

func afficherReponseUniverselle() -> Int? {
    if let nom = reponseUniverselle() {
        print(nom)
        return nom
    }
    return nil
}

Au-dessus, on utilise la méthode sécurisée avec  if let   . Cela signifie que le résultat de la fonction ne sera affiché que s'il a renvoyé un entier ( Int  ) plutôt que nil  .

Si nous avions écrit cela en utilisant la méthode sécurisée avec  guard let  / else  , cela ressemblerait à ceci :

func afficherReponseUniverselle2() -> Int? {
    guard let nom = reponseUniverselle() else {
        return nil
     }
   print(nom)
   return nom
}

Il est courant de voir  guard let   , car il est utile pour vérifier que les conditions sont correctes dès le départ. Cela rend notre code plus facile à lire que si nous essayions de vérifier une condition, puis d'exécuter du code, puis de vérifier une autre condition et d'exécuter un code différent.

Donc, utilisez  if let   si vous voulez juste déballer quelques optionnels, mais préférez  guard let   si vous vérifiez spécifiquement que les conditions sont correctes avant de continuer. 

Première méthode alternative : la vérification avant utilisation

Non recommandée, cette méthode est dangereuse, car il faut être certain que la variable ait une valeur pour utiliser le point d’exclamation. 

Pour pallier ce risque, il existe deux moyens pour s’assurer que la variable a une valeur. Le premier est assez intuitif :

if jeSuisUnOptionnel != nil {
   print("La variable contient une valeur")
}

Ici, on vérifie que l’optionnel ne vaut pas nil. Si c’est le cas, on peut donc le déballer avec le point d'exclamation :

if jeSuisUnOptionnel != nil {
   print("La variable vaut \(jeSuisUnOptionnel!)")
}

Deuxième méthode alternative : opérateur ternaire

Non recommandée, cette méthode alternative consiste à utiliser un opérateur ternaire.

Kézako ?! Terna… quoi ???

Encore un nom compliqué mais rassurez-vous ! Vous allez voir que le concept est très simple ! ;)

Au fait, il ne s’agit ni plus ni moins que d’un raccourci avec une instruction if / else.

Par exemple, si l’on écrit :

if question {
    réponse1
} else {
    réponse2
}

Avec un opérateur ternaire, on va pouvoir simplifier la syntaxe comme cela :

question ? réponse 1 : réponse 2

On peut appliquer cette formule pour notre exemple :

var jeSuisOptionnel: Int? = 12

let valeurDéballée = jeSuisOptionnel != nil ? jeSuisOptionnel! : 0

C’est beaucoup mieux parce qu’au final on a réussi à réduire nos 5 lignes de code en une seule ! Si jeSuisOptionnel  a une valeur, alors on déballe l’optionnel, sinon notre valeur par défaut  0   est utilisée.

Troisième méthode alternative : opérateur nil-coalescing

Comme vous l’aurez compris, les optionnels sont bien pratiques pour gérer des variables qui peuvent ne pas avoir de valeur. Cependant, il arrive parfois que l’on ai envie d’écrire une valeur facultative et une valeur par défaut en cas d’absence de valeur, mais qui serait non nil ! Pour cela, il existe l’opérateur nil-coalescing.

La syntaxe est beaucoup plus simple à utiliser que l’opérateur ternaire. Reprenons notre exemple :

var jeSuisOptionnel: Int? = 12
let valeurDéballée = jeSuisOptionnel ?? 0

jeSuisOptionnel  est de type optionnel, et il peut peut-être contenir un Int  .  Avec l’opérateur nil-coalescing ??  , on va pouvoir être sûr que valeurDéballée  va contenir une valeur autre que nil, en utilisant la valeur par défaut, celle à droite. Ici, la valeur par défaut est donc  0   . Ce qui signifie que la variable valeurDéballée  deviendra un Int  quoi qu’il arrive. Et ça, c’est plutôt pratique !

Par exemple, vous n’avez pas besoin de créer des variables séparées pour utiliser un opérateur nil-coalescing :

print(“Combien reste-t-il d’argent à Joe ? Il lui reste  \(jeSuisOptionnel ?? 0) euros”)

La méthode non-sécurisée et dangereuse : déballage forcé

Pour déballer un optionnel, on peut utiliser le point d’exclamation  !  à la fin de la variable comme ceci :

var jeSuisOptionnel: Int? = 12
jeSuisOptionnel! // La variable est déballée avec le point d’exclamation

Si on essaye d’écrire print(jeSuisOptionnel!)  , la console affichera directement 12  et non Optional(12)  . L’optionnel a été déballé. C’est comme si on utilisait directement une variable non optionnelle.

Le problème, c’est que du coup si l’optionnel n’a pas de valeur, le programme va planter.

var jeSuisVide: Int?
jeSuisVide! // Le programme plante car la valeur est nil

Appréhendez quelques usages des optionnels

Les optionnels au début, ça peut être un peu déstabilisant. Voilà donc quelques exemples d’utilisation des optionnels pour que vous sentiez leur intérêt.

La conversion de  String  en  Int

On a vu que l’on pouvait transformer un type en un autre en utilisant l’initialisation du type. Par exemple, comme ceci :

Int(3.4) // Renvoie 3

On a converti 3.4 de type double, en 3 qui est de type Int, grâce à l’instruction Int. Avec cette méthode on peut aussi convertir un type  String  en type  Int  . Comme ceci :

var a = Int("123")

Et si l’on écrit  print(a)  , la console affiche  Optional(123)  . Eh oui !   a  est un optionnel ! Pourquoi ? Car pour transformer une  String  en  Int  , il faut que cette string contienne uniquement des chiffres. Par exemple si j’écris :

var a = Int("Des pommes")

L’initialisation ne va pas fonctionner, car on ne peut pas convertir la chaîne  "Des pommes" en entier, et   a    va valoir  nil  . Donc cette instruction permettant de créer un  Int  à partir d’une  String  renvoie :

  • Soit un entier si la string utilisée est convertible.

  • Soit nil si la string n’est pas convertible en Int. 

Et c’est bien la définition d’un optionnel !

Les propriétés first et last

Pour rappel, pour accéder aux données d’un tableau, on utilise son index. Par exemple :

var poupeesRusses = ["petite", "moyenne", "grande"]
poupeesRusses[1] // Moyenne

Pour accéder à la première valeur de ce tableau, on ferait :  poupeesRusses[0]  , et pour la dernière valeur on peut faire  poupeesRusses[2]  . Ça, vous le savez. Mais sachez que Swift propose des propriétés bien pratiques pour ces deux cas : les propriétés  first  et  last  :

var petite = poupeesRusses.first
var grande = poupeesRusses.last

Cela permet d’avoir un programme qui se lit plus clairement. Et c’est plus pratique.

Seulement, si le tableau est vide, les propriétés  first  et  last  ne peuvent rien renvoyer. Donc elles renvoient nil. Du coup, les propriétés  first  et  last  peuvent renvoyer :

  • soit un élément du tableau ;

  • soit nil si le tableau est vide. 

C’est donc un optionnel. Vous pouvez le tester en écrivant  print(grande)  qui affiche  Optional("grande")  .

Les dictionnaires

Pour accéder à une valeur dans un dictionnaire, on utilise sa clé comme ceci :

var barn = ["milk": 0, "wheat": 0, "wool": 0]
barn["milk"] // Renvoie 0

Ça, vous le savez. Mais je vous ai menti. Et l’heure de vérité est arrivée ! En utilisant la clé, le dictionnaire ne renvoie pas directement la valeur mais un optionnel. Essayez d’écrire par exemple :

print(barn["milk"])

La console affiche  Optional(0)  . Pourquoi ne pas renvoyer la valeur directement, me direz-vous ? Car si l’on se trompe de clé, le dictionnaire ne peut rien renvoyer (cf. la fameuse histoire de licornes…). Par exemple, si j’écris :

print(barn["MILK"])

Le dictionnaire ne contient aucune valeur associée à la clé  MILK  . Et donc il va renvoyer  nil  . Autrement dit, lorsque je cherche à accéder à un élément dans un dictionnaire, le dictionnaire renvoie :

  • Une valeur si la clé existe.

  • Rien si la clé n’existe pas.

Donc il est normal que le dictionnaire renvoie un optionnel ! Donc pour effectivement accéder à sa valeur, il faut le déballer.

À vous de jouer

Le contenu de l’exercice se trouve dans le dossier Github P3C3.

  1. Ouvrez un nouveau Playground Xcode.

  2. Copiez le contenu du fichier “main.swift” dans votre Playground.

  3. Suivez les instructions.

Ici, j’utilise la première méthode sécurisée avec le  if let   pour déballer les valeurs de mon dictionnaire. On notera dans la correction que je déballe volontairement sur une ligne les optionnels avec une virgule (,) afin de faciliter la lecture du code pour gagner en clarté ! ;).

En résumé

  • Un optionnel est un type qui permet à une variable d’être utilisée sans valeur.

  • Dans le cas où l’optionnel ne contient pas de valeur, on dit que sa valeur est  nil  .

  • Il faut se représenter un optionnel comme un paquet cadeau qui contient :

    • soit une valeur du type indiqué ; 

    • soit rien.

  • Un optionnel doit donc être déballé, selon de différentes méthodes :

    • Une première méthode sécurisée  et recommandée qui permet d’accéder à la variable par la création d’une nouvelle variable.

    • Une deuxième méthode sécurisée  et recommandée qui permet d’accéder à la variable par la création d’une nouvelle variable, et qui sera réutilisable en dehors de la scope.

    • D'autres méthodes existent, mais ne sont pas recommandées.

  • Lorsqu’on accède aux valeurs d’un dictionnaire avec une clé, le dictionnaire renvoie un optionnel et non la valeur directement.

Maintenant que vous savez tout sur tout avec les optionnels, que direz-vous si je vous dis que dans le chapitre suivant je vais vous apprendre à créer vos premières fonctions ?  ;)

Mais avant cela, je vous invite à réaliser un petit quiz afin de vérifier vos nouvelles connaissances ! :p

Exemple de certificat de réussite
Exemple de certificat de réussite