Mis à jour le 30/10/2017
  • 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

La gestion des erreurs

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Un bon développeur de nos jours doit réaliser ses programmes sans qu’aucune erreur ne soit présente. La réalité est qu’un programme qui tourne, ne peut pas garantir qu’absolument aucune erreur ne se produise. Il est alors important de prévoir dans notre code les erreurs susceptibles d’arriver et de réagir quand nous rencontrons ces erreurs.

Comprendre la gestion et le traitement des erreurs

Quelle que soit la taille de votre programme, la qualité de votre code, il y aura toujours un risque d'erreur susceptible de se produire. Par exemple, prenons le cas d'une application iOS qui repose sur l'utilisation d'une connexion internet. Il peut alors arriver plusieurs soucis : perte de connexion internet, l'utilisateur a mis le mode avion pendant que l'application tournait, la connexion internet est lente, etc. Côté code, ce que l'on pourrait faire c'est prévoir comment réagir face à ces soucis. Par exemple, si on utilise l'application et que nous n'avions pas de connexion, alors on informe l'utilisateur qu'il faut une connexion internet active pour que l'application fonctionne.

Il existe deux cas de gestion d'erreur en Swift. Le premier cas consiste à déclencher une erreur lorsqu'on obtient un résultat qu'on ne souhaitait pas avoir. Le deuxième cas consiste à attraper et à manipuler cette erreur une fois lancée par une méthode.

Une erreur qui est lancée au sein de notre code Swift est d'un type particulier. Soit il est de façon générale du type  Error  (protocole  Error  ), soit il est d'un type personnalisé qui suit ce protocole  Error  .

Sachez que dans les développements iOS, watchOS et macOS, la gestion des erreurs est très souvent utilisée et que vous avez grande chance d'y être confronté au cours de vos développements d'applications.

Créer un type d’erreur

Pour l'exemple, imaginons que l'on veuille récupérer des données sur internet. Une méthode qui récupère les données pourrait, par exemple, ne pas obtenir les données parce que la connexion internet n'est pas établi ou encore parce que la connexion est longue ou alors parce que les données ne sont tout simplement pas présentes. Ces erreurs, nous pouvons les représenter sous forme d'énumération qui se conforme au protocole  Error  . Comme je l'ai expliqué ci-dessus.

enum RecupererDonneesError : Error {
    case pasDeConnexion
    case connexionLente
    case donneesInexistantes
}

C’est aussi simple que cela, il faut veiller à la conformité du protocole  Error  . C’est tout.

Lancer une erreur

Une méthode d’un objet ou une fonction tout simplement qui est susceptible de retourner une erreur doit en être averti (que cela puisse arriver). Pour cela, on utilisera le mot-clé  throws  .

func fournirDonnees() throws {
  // ...
}

Si la fonction ou méthode retourne quelque chose, ce mot-clé sera placé avant le type de retour de cette façon :

func fournirDonnees() throws -> [Donnees] {
  // ...
}

Maintenant que la méthode ou la fonction est avertie qu'elle peut rencontrer des erreurs, on peut alors ajouter dans notre code les erreurs à lancer en cas de soucis. Pour cela, nous allons nous servir de la notion de garde (mot-clé  guard  ) et lanceront les erreurs grâce au mot-clé  throw  (sans le s cette fois-ci).

let connexionOk = true
let vitesseDeConnexion = 25
let donneesTrouves = false
 
enum RecupererDonneesError : Error {
    case pasDeConnexion
    case connexionLente
    case donneesInexistantes
}
 
func fournirDonnees() throws {
    guard connexionOk else {
        throw RecupererDonneesError.pasDeConnexion
    }
    
    guard vitesseDeConnexion > 30 else {
        throw RecupererDonneesError.connexionLente
    }
    
    guard donneesTrouves else {
        throw RecupererDonneesError.donneesInexistantes
    }
}

Dans la fonction, chaque instruction de garde vérifie les conditions données. Dès lors qu'une condition est (ou devient) fausse, on saura qu'il faut lancer l'erreur précisée avec le  throw  .

Appeler les méthodes ou fonctions susceptibles de retourner une erreur

Une fois que nous avons précisé à une méthode ou une fonction qu'elle est susceptible de retourner une erreur (grâce à  throws  ), alors nous ne pouvons plus l'appeler comme on le faisait auparavant. Rassurez-vous, rien de bien compliqué dans ce qui va suivre !

En effet, lorsque vous appelez une fonction ou méthode de ce genre, il suffira seulement d'utiliser le mot-clé  try  cette fois-ci devant le nom de la méthode à appeler. Autrement, Xcode vous précisera comme erreur que vous devez utiliser  try  .

try fournirDonnees()

Cette utilisation, doit se faire dans un bloc  do-catch  (et beh, cela en fait de nouveaux mot-clés à apprendre pour cette section). Ce bloc va vous permettre de capturer et de gérer tous les types d'erreurs que l'on peut rencontrer en utilisant cette fonction.

Par exemple, imaginons que nous devons appeler cette fonction au sein d'une autre fonction, voici comment l'on pourrait implémenter le  do-catch  :

func recupererDonnees() -> String {
    do {
        try fournirDonnees()
    } catch RecupererDonneesError.pasDeConnexion {
        return "Pas de connexion internet"
    } catch RecupererDonneesError.connexionLente {
        return "La connexion est lente"
    } catch RecupererDonneesError.donneesInexistantes {
        return "Les données sont inexistantes sur le serveur"
    } catch {
        return "Erreur"
    }
    
    return "Récupération de données réussi !"
}
 
print(recupererDonnees())

Ce bloc  do-catch  est un peu un  if-else if-else  . Dans le  do  , on indique que l'on essaye d'obtenir les données. On rentre forcément dans le  do  . On essaye alors de lancer la fonction  fournirDonnees()  et là plusieurs cas sont possibles.

Il n'y a pas d'erreur survenue. De ce fait, on zappe tous les  catch  et on poursuit dans le code (ici on prend en compte le dernier  return  ).

S'il y a des erreurs, alors c'est là qu'entre en jeu les  catch  . Au sein des  catch  , on précisera quel type d'erreur nous souhaitons gérer. Si on rencontre une erreur précisée dans un  catch  , alors on peut exécuter des instructions propre à ce genre d'erreur (ou dans notre exemple, retourner un message d'erreur personnalisé). Enfin, le  catch  sans précision est obligatoire. D'autres erreurs autres que celles que nous avons pensées (et traitées) peuvent survenir. Dans ce cas, c'est dans ce  catch  là que nous rentrerons. C'est un peu le  default  d'un switch.

On peut aussi se passer du  do-catch  . Pour cela, on peut utiliser le mot-clé  try?  ou  try!  .

try? recupererDonnees()
try! recupererDonnees()

Avec le  try?  , on ne fait rien si une erreur survient. Avec  try!  , le programme se stop si une erreur survient. De ce fait, l’utilisation de ce dernier ne doit se faire seulement si l’on est sûr de ce que l’on fait ! Ce serait dommage de proposer une application qui peut planter à un utilisateur. :p

Pour résumer

  • Un programme est toujours susceptible de rencontrer une erreur au cours de son exécution

  • Nous pouvons créer nos types d’erreurs en se conformant au protocole  Error

  • On prévient une fonction qu’elle peut rencontrer des erreurs grâce au mot-clé  throws

  • On utilise le  guard  pour tester une condition, si la condition est fausse on lance une erreur avec  throw

  • Une méthode ou une fonction qui peut retourner une erreur doit s’appeler par la suite soit avec un  try  au sein d’un  do-catch  , soit avec  try?  ou  try!

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