• 12 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 24/05/2022

Levez le voile sur try

Vous savez comment créer des erreurs, voyons maintenant comment les gérer !

Découvrez le mot-clé try

Nous allons enfin parler de ce fameux mot-clé  try  que nous avons croisé plusieurs fois dans ce cours.

Copiez le code suivant dans votre Playground :

func payFruits() {
   let order = Order()
   order.items = [
      Item(price: 2.40, description: "Melon"),
      Item(price: 4, description: "Fraises"),
      Item(price: 1.20, description: "Pomme")
   ]
   let cb = PaymentMethod(isValid: true, maxAmount: 100)
   let price = order.pay(with: cb)
    print("Votre commande d'un montant de \(price)€ a bien été prise en compte.")
}

Vous devriez avoir une erreur du type :

Call can throw, but it is not marked with 'try' and the error 
is not handled.

Ce message signifie que l'appel de la fonction  pay  peut lancer une erreur, mais que cette erreur n'est pas gérée.

En Swift, lorsqu'on fait appel à une fonction qui peut lancer une erreur, une fonction qui a donc la mention throws dans sa déclaration, on est obligé de prendre des précautions.

Et ces précautions se traduisent concrètement par l'ajout du mot-clé  try  avant l'appel de la fonction.

let price = try order.pay(with: cb)

try signifie essayer , en anglais. Donc l'idée, c'est de dire : "Essaie d'exécuter cette fonction. Je sais que tu ne vas peut-être pas y arriver, car cette fonction peut renvoyer une erreur".

OK je veux bien, mais en attendant, j'ai toujours une erreur dans la console :

Errors thrown from here are not handled.

C'est normal ! Il ne suffit pas d'écrire le mot-clé  try  , il faut aussi gérer l'erreur, et il y a deux façons de le faire.

Gérez l'erreur avec do-catch

Pour gérer proprement les erreurs, il faut entourer l'appel de la fonction par l'instruction do-catch comme ceci :

do {
   let price = try order.pay(with: cb)
    print("Votre commande d'un montant de \(price)€ a bien été prise en compte.")
} catch {
}

Dans la partie  do  , j'essaie d'exécuter ma fonction. Et si jamais elle renvoie une erreur, je vais gérer ça dans la partie  catch  .

Voyons d'ailleurs comment on gère les erreurs avec  catch  . Tout d'abord, nous allons récupérer une instance de l'erreur en contrôlant en même temps son type :

do {
   let price = try order.pay(with: cb)
    print("Votre commande d'un montant de \(price)€ a bien été prise en compte.")
} catch let error as Order.OrderError {
}

J'ai un peu l'impression que le prof est à la ramasse, j'ai encore un problème dans la console !

Errors thrown from here are not handled because the enclosing
 catch is not exhaustive.

D'une part, je trouve ça pas très agréable, et d'autre part, c'est encore normal  ! Le catch doit être exhaustif. Or, il se peut que dans votre méthode  pay  , vous lanciez d'autres types d'erreurs que des  OrderError  . Il faut rajouter un bloc catch générique pour gérer les autres erreurs potentielles.

do {
   let price = try order.pay(with: cb)
    print("Votre commande d'un montant de \(price)€ a bien été prise en compte.")
} catch let error as Order.OrderError {
} catch {
   print("Oups ! Quelque chose a dû mal se passer...")
}

On va maintenant gérer l'erreur dans le premier  catch  .  OrderError  est une énumération, donc nous allons gérer l'erreur avec un  switch  .

do {
   let price = try order.pay(with: cb)
    print("Votre commande d'un montant de \(price)€ a bien été prise en compte.")
} catch let error as Order.OrderError {
   switch error {
   case .orderAlreadyPayed:
      print("Vous avez déjà réglé cette commande.")
   case .orderIsEmpty:
      print("Votre panier est vide.")
   case .invalidPaymentMethod:
      print("Votre méthode de paiement est invalide.")
   case .insufficientFundings:
         print("Votre méthode de paiement a été refusée. Contrôlez vos plafonds.")
   }
} catch {
   print("Oups ! Quelque chose a dû mal se passer...")
}

Et voilà une très belle gestion d'erreur ! Votre utilisateur sait maintenant très clairement pourquoi sa commande n'a pas été passée, ce qui lui évite beaucoup de frustrations !

Certes, mais du coup ça fait beaucoup de code à rédiger !

Il ne faut pas être fainéant quand il s'agit de la gestion des erreurs ! Néanmoins je suis d'accord que parfois, on n'a pas besoin de donner autant de détails.

Dans ces cas-là, il existe une méthode plus rapide.

try! et try?

Comme pour le déballage des optionnels ou le contrôle des types, on peut accoler les ponctuations ! et ? au mot-clé try . Voyons d'abord le point d'exclamation :

let price = try! order.pay(with: cb)
print("Votre commande d'un montant de \(price)€ a bien été prise en compte.")

C'est pratique, mais risqué, car s'il y a malgré tout une erreur, votre code va planter. Donc c'est à n'utiliser que si vous êtes absolument sûr de vous, ou si vous souhaitez que votre code plante si jamais il y a une erreur.

Une autre façon, plus sûre, est d'utiliser le point d'interrogation :

let price = try? order.pay(with: cb)

Lorsqu'on utilise  try?  , le code ne plante plus en cas d'erreur. Pour y arriver, il utilise les optionnels. En effet, avec le code ci-dessus, la fonction pay ne renvoie plus un  Double  , mais un optionnel.

La constante  price  est donc de type optionnel. Du coup, je peux la déballer comme je le fais avec n'importe quel optionnel :

if let price = try? order.pay(with: cb) {
    print("Votre commande d'un montant de \(price)€ a bien été prise en compte.")
} else {
   print("Oups ! Quelque chose a dû mal se passer...")
}

C'est certes moins précis qu'avec  do-catch  , mais c'est un intermédiaire qui permet de faire un minimum de gestion d'erreur à moindre frais.

Identifiez qui utiliser et quand

Dès que vous avez le choix, se pose la question de que faire à quel moment. À vous de définir ou de trouver vos propres règles, mais voici ce que je vous propose :

Si votre logique ou votre expérience utilisateur dépend de la nature de l'erreur, utilisez try avec do-catch et un switch  . Dans les autres cas, utilisez try?  .

Ne réservez l'utilisation de  try!  qu'à de rares exceptions.

En résumé

  • L'appel d'une fonction marquée avec  throws  doit être précédé de  try  .

  • try  ne peut pas être utilisé seul. Il a besoin :

    • soit d'être entouré d'une instruction  do-catch  qui permet de gérer les erreurs précisément ;

    • soit d'être suivi du  ?  qui renvoie le retour de la fonction sous forme d'optionnel ;

    • soit d'être suivi du  !  qui exécute la fonction quoi qu'il arrive, au risque de faire planter le code s'il rencontre une erreur.

Félicitations ! Ce cours n'était pas facile ! Vous verrez avec la pratique que la gestion des réseaux n'est pas la partie la plus évidente du travail de développeur iOS. Pourtant, vous venez de faire un grand pas vers la maîtrise de ce sujet !

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