Logo de Swift en orange sur fond gris qui sert de hero de présentation de la formation sur les fondamentaux de Swift

Optionnel

Sur ce cours, nous allons découvrir les optionnels. Cette notion est simple à comprendre et surtout importante à mettre en place dans tes programmes en Swift. Pour faire simple, les optionnels vont te permettre de gérer très facilement les cas potentiels où tu as une absence de valeur. Par exemple :

struct HarryPotter {
   var name: String
   var publicationYear: Int
}

let books = [
    HarryPotter(name: "Harry Potter à l'école des sorciers", publicationYear: 1997),
    HarryPotter(name: "Harry Potter et la Chambre des secrets", publicationYear: 1998),
    HarryPotter(name: "Harry Potter et le Prisonnier d'Azkaban", publicationYear: 1999),
    HarryPotter(name: "Harry Potter et la Coupe de feu", publicationYear: 2000),
    HarryPotter(name: "Harry Potter et l'Ordre du Phénix", publicationYear: 2003),
    HarryPotter(name: "Harry Potter et le Prince de sang-mêlé", publicationYear: 2005),
    HarryPotter(name: "Harry Potter et les Reliques de la Mort", publicationYear: 2007),
    HarryPotter(name: "Harry potter le retour à Poudlard", publicationYear: ???)
]
J’ai ici une struct HarryPotter que j’utilise pour stocker les livres de la série Harry Potter dans un tableau. Maintenant, imaginons que J.K. Rowling annonce un nouveau livre intitulé « Harry Potter : Le Retour à Poudlard » et que je souhaite l’intégrer dans mon tableau. Je vais rencontrer un problème : je ne connais pas encore la date de sortie du livre.

struct Book {
   var name: String
   var publicationYear: Int?
}

Book(name: "Harry potter le retour à Poudlard", publicationYear: nil)
Pour qu’une propriété soit optionnelle, il suffit de placer un ? à la fin de son type. Ensuite, quand j’instancie un livre Harry Potter, je peux mettre une date si je la connais, ou, si je ne connais pas la date de sortie du livre, mettre le mot-clé nil . Ce mot-clé représente une valeur nulle.

If Let déballage

Unwrapping


struct HarryPotter {
   var name: String
   var publicationYear: Int?
}

let books = [
    HarryPotter(name: "Harry Potter à l'école des sorciers", publicationYear: 1997),
    HarryPotter(name: "Harry Potter et la Chambre des secrets", publicationYear: 1998),
    HarryPotter(name: "Harry Potter et le Prisonnier d'Azkaban", publicationYear: 1999),
    HarryPotter(name: "Harry Potter et la Coupe de feu", publicationYear: 2000),
    HarryPotter(name: "Harry Potter et l'Ordre du Phénix", publicationYear: 2003),
    HarryPotter(name: "Harry Potter et le Prince de sang-mêlé", publicationYear: 2005),
    HarryPotter(name: "Harry Potter et les Reliques de la Mort", publicationYear: 2007),
    HarryPotter(name: "Harry potter le retour à Poudlard", publicationYear: ???)
]

for book in books {
    print(book.publicationYear)
}

On va rencontrer un problème avec notre valeur nil.

Si je boucle sur mon tableau pour afficher les valeurs, il arrivera un moment où la boucle rencontrera une valeur inexistante (nil) et ne saura pas quoi afficher dans la console.

Pour résoudre ce problème, on peut utiliser un if let, qui permet de déballer une valeur optionnelle et de l’utiliser dans un bloc de code spécifique. Si une valeur est présente, le premier cas sera exécuté. Sinon, on passera au second cas pour gérer l’absence de valeur.


for book in books {
    if let year = book.publicationYear {
        print("Le livre \(book.name) a été publié en \(year).")
    } else {
        print("L'année de publication du livre \(book.name) est inconnue.")
    }
}

Guard let


struct HarryPotter {
    var name: String
    var publicationYear: Int?   
}

func displayBook(book: HarryPotter?) {
    guard let potterBook = book else {
        print("Le livre est manquant.")
        return
    }
    guard let year = potterBook.publicationYear else {
        print("L'année de publication du livre \(potterBook.name) est inconnue.")
        return
    }
    print("Le livre \(potterBook.name) a été publié en \(year).")
}

let livreOptionnel: HarryPotter? = HarryPotter(name: "Harry Potter et l'Ordre du Phénix", publicationYear: 2003)
displayBook(book: livreOptionnel)
displayBook(book: nil)

guard let permet de vérifier que des valeurs optionnelles existent avant de continuer l’exécution du code. Si une valeur est nil, on sort immédiatement de la fonction.

Dans notre exemple, on passe en paramètre de la fonction le type HarryPotter? en optionnel. guard let vérifie s’il y a une valeur, et si c’est le cas, il passe à la suite de son exécution. Si ce n’est pas le cas, il exécute le bloc else.

guard let est très utile pour enchaîner les vérifications de cas d’absence ou non de valeur. Pour être clair, displayBook vérifie si un livre existe. Si le livre n’existe pas, la fonction s’arrête là. Si la valeur existe, il passe à la vérification suivante et vérifie si l’année de publication existe. Si ce n’est pas le cas, il s’arrête là et sort de la fonction, et si c’est le cas, il passe à la suite et affiche l’année de publication. On peut donc enchaîner les vérifications facilement.

Déballage forcé !

Forced unwrapping


struct HarryPotter {
   var name: String
   var publicationYear: Int?
}

let books = [
    HarryPotter(name: "Harry Potter à l'école des sorciers", publicationYear: 1997),
    HarryPotter(name: "Harry Potter et la Chambre des secrets", publicationYear: 1998),
    HarryPotter(name: "Harry Potter et le Prisonnier d'Azkaban", publicationYear: 1999),
    HarryPotter(name: "Harry Potter et la Coupe de feu", publicationYear: 2000),
    HarryPotter(name: "Harry Potter et l'Ordre du Phénix", publicationYear: 2003),
    HarryPotter(name: "Harry Potter et le Prince de sang-mêlé", publicationYear: 2005),
    HarryPotter(name: "Harry Potter et les Reliques de la Mort", publicationYear: 2007),
    HarryPotter(name: "Harry potter le retour à Poudlard", publicationYear: nil)
]

let firstHarryPotterPublicationYear = books[0].publicationYear!
print("Le premier livre Harry Potter a été publié en \(firstHarryPotterPublicationYear).")

// La valeur est bien présente sur l'index 0, elle sera bien déballé.

let newPotterPublicationYear = books[7].publicationYear!
print("Le nouveau livre Harry Potter a été publié en \(newPotterPublicationYear).")

// La valeur est nil sur l'index 7, le programme va crash.
Le déballage forcé consiste à utiliser le point d’exclamation ! pour accéder directement à la valeur d’une optionnelle, en supposant qu’elle n’est pas nil. Si l’optionnelle est nil, cela provoque une erreur d’exécution (crash). 🚨 C’est donc une mauvaise pratique d’utiliser le déballage forcé, car tu dois être sûr et certain d’avoir une valeur, au risque de faire planter ton projet. Privilégie toujours le if let ou le guard let.

 Opérateur de coalescence nulle ??

Nil-Coalescing Operator


struct HarryPotter {
   var name: String
   var publicationYear: Int?
}

let book = HarryPotter(name: "Harry potter le retour à Poudlard", publicationYear: nil)

let publicationYearMessage = "L'année de publication est \(book.publicationYear ?? 0)
L’opérateur de coalescence nulle ?? en Swift permet de fournir une valeur par défaut si une optionnelle est nil. Il simplifie le code en évitant des vérifications répétées pour les optionnelles. Dans notre exemple, on utilise l’opérateur de coalescence nulle ?? dans l’interpolation. Si la date est connue, elle sera affichée, sinon on affichera un 0.

 Chainage optionnel

Optional Chaining


struct Author {
    var name: String
    var birthYear: Int?
}

struct HarryPotter {
    var name: String
    var publicationYear: Int?
    var author: Author?
}

let books = [
    HarryPotter(name: "Harry Potter à l'école des sorciers", publicationYear: 1997, author: Author(name: "J.K. Rowling", birthYear: 1965)),
    HarryPotter(name: "Harry Potter et la Chambre des secrets", publicationYear: 1998, author: Author(name: "J.K. Rowling", birthYear: 1965)),
    HarryPotter(name: "Harry Potter et le Prisonnier d'Azkaban", publicationYear: 1999, author: Author(name: "J.K. Rowling", birthYear: 1965)),
    HarryPotter(name: "Harry Potter et la Coupe de feu", publicationYear: 2000, author: Author(name: "J.K. Rowling", birthYear: 1965)),
    HarryPotter(name: "Harry Potter et l'Ordre du Phénix", publicationYear: 2003, author: nil),
    HarryPotter(name: "Harry Potter et le Prince de sang-mêlé", publicationYear: 2005, author: Author(name: "J.K. Rowling", birthYear: nil)),
    HarryPotter(name: "Harry Potter et les Reliques de la Mort", publicationYear: 2007, author: Author(name: "J.K. Rowling", birthYear: 1965)),
    HarryPotter(name: "Harry Potter le retour à Poudlard", publicationYear: nil, author: nil)
]

for book in books {
    if let authorName = book.author?.name {
        print("Le livre \(book.name) a été écrit par \(authorName).")
    } else {
        print("L'auteur du livre \(book.name) est inconnu.")
    }
}
Le chaînage optionnel permet d’accéder aux propriétés, méthodes d’une optionnelle qui pourrait être nil. Si l’optionnelle contient une valeur, l’appel réussit et renvoie la valeur. Si l’optionnelle est nil, l’appel renvoie nil et l’expression entière échoue. Ça évite les erreurs d’exécution et simplifie le code lorsqu’on travaille avec des valeurs imbriquées optionnelles.

Dans notre exemple, on utilise book.author?.name pour accéder au nom de l’auteur de manière sécurisée. Si book.author est nil, l’expression renvoie nil sans provoquer de crash.

Exercices Pratiques

Exercice Optionnel

Télécharger le playground

Accède aux consignes directement en téléchargeant le playground.

⚠️ Les consignes que tu trouveras dans le playground ne sont pas verrouillées. Fais attention à ne pas les effacer par inadvertance ! 

Objectif

Les optionnels t’amènent à te poser la question : Est-ce que cette variable peut ne pas avoir de valeur ? Si la réponse est oui, c’est un moyen de mieux gérer cette incertitude dans ton code.

Quiz

Après un peu de pratique, il est temps de te tester avec un petit quiz pour vérifier si tout est clair pour toi. Prends ton temps, lis bien les questions, et bonne chance.

Note

Les notions s’enchaînent ! Si tu prends un peu de recul, tu remarqueras qu’à chaque nouvelle notion, on retravaille forcément un peu sur les notions précédentes. Ça te permet de pratiquer encore et encore, et donc de mieux les assimiler. Si tu doutes (et c’est tout à fait normal), prends le temps de regarder tout ce que tu as appris depuis le début. Je suis sûr que les premières notions abordées dans ce cours te semblent déjà plus évidentes à ce stade. C’est normal, car on les répète et les consolide avec chaque nouvelle notion. Ce sera pareil pour celles que tu apprends maintenant, elles deviendront plus claires avec le temps et la pratique.

Exercices Pratiques

Exercice Optionnel

Télécharger le playground

Accède aux consignes directement en téléchargeant le playground.

⚠️ Les consignes que tu trouveras dans le playground ne sont pas verrouillées. Fais attention à ne pas les effacer par inadvertance ! 

Objectif

Les optionnels t’amènent à te poser la question : Est-ce que cette variable peut ne pas avoir de valeur ? Si la réponse est oui, c’est un moyen de mieux gérer cette incertitude dans ton code.

Quiz

Après un peu de pratique, il est temps de te tester avec un petit quiz pour vérifier si tout est clair pour toi. Prends ton temps, lis bien les questions, et bonne chance.

Note

Les notions s’enchaînent ! Si tu prends un peu de recul, tu remarqueras qu’à chaque nouvelle notion, on retravaille forcément un peu sur les notions précédentes. Ça te permet de pratiquer encore et encore, et donc de mieux les assimiler. Si tu doutes (et c’est tout à fait normal), prends le temps de regarder tout ce que tu as appris depuis le début. Je suis sûr que les premières notions abordées dans ce cours te semblent déjà plus évidentes à ce stade. C’est normal, car on les répète et les consolide avec chaque nouvelle notion. Ce sera pareil pour celles que tu apprends maintenant, elles deviendront plus claires avec le temps et la pratique.