Notre très cher et estimé Swift, aussi « restrictif » soit-il, nous permet d‘éviter de commettre des erreurs lors de la création de code. Mais il faut tout de même savoir comment gérer ces erreurs, comment les renvoyer ou comment les prévenir.

Certaines opérations ne sont pas garanties de toujours se terminer ou encore de produire un résultat utile. On utilise, par exemple, les optionnels pour représenter l’absence d’une valeur. Mais lorsqu’une opération échoue, il est souvent utile de comprendre la cause de l’échec, afin que le code puisse réagir en conséquence.

Moi quand je pense, en partant, à l’erreur que j’aurais pu gérer autrement.
Cas de figure qui fonctionne aussi sous la douche ou au moment de dormir… 😅

Les types d’erreurs en Swift

Il existe différents types d’erreurs en Swift :

  • Erreurs de syntaxe : elles se se produisent lorsque le compilateur ne peut pas comprendre le code. Elle sont soulevées par Xcode à la compilation.
  • Erreurs de logique : quand il n’y a pas d’erreur de syntaxe mais que le code ne produit pas ce qu’on attend de lui.
  • Erreurs d’exécution : elles se produisent lors de l’exécution du code. Ces erreurs peuvent être causées par des facteurs tels que des entrées utilisateur incorrectes, des problèmes de mémoire ou des erreurs système.

Comprendre les différents types d’erreurs en Swift est important pour pouvoir les identifier et les traiter correctement. Il est important de pouvoir différencier les différentes catégories d’erreurs, car cela peut aider à déterminer le type de réponse appropriée à fournir à l’utilisateur. En effet, Swift fournit les outils pour envoyer (throw), retenir (catch), propager et manipuler les erreurs récupérables au moment de l’exécution.

La syntaxe de la gestion d’erreurs en Swift

Dans Swift, les erreurs sont représentées par des valeurs de types conformes au protocole Error. Ce protocole vide indique qu’un type peut être utilisé pour la gestion des erreurs.

Lorsqu’une erreur est déclenchée, un morceau de code environnant doit être chargé de la gérer. Par exemple, en corrigeant le problème, en essayant une autre approche ou en informant l’utilisateur de l’échec. Il existe 4 façons de gérer les erreurs en Swift :

  • Propager l’erreur
  • Gérer l’erreur avec do/catch
  • Gérer l’erreur avec un optionnel : try?
  • Affirmer que l’erreur ne se produira pas : try!

C’est ce que nous allons voir dans ce chapitre.

• Throws

Renvoyer une erreur permet d’indiquer que quelque chose d’inattendu s’est produit. L’exécution normale ne peut donc pas se produire.

On peut anticiper l’erreur en indiquant le mot clé throw directement dans la déclaration de la fonction, après les paramètres :

func canThrowErrors() throws -> String

• Do/Catch

Dans d’autres langages, on utilise plutôt le try/catch, pas en Swift. On utilise cette syntaxe quand une fonction peut potentiellement générer une erreur.

do {
    try someFunctionThatThrowsError()
    // le code suivant ne sera exécuté que si la fonction n'a pas généré d'erreur
} catch {
    // le code suivant sera exécuté en cas d'erreur
}

Dans cet exemple, nous appelons la fonction someFunctionThatThrowsError()à l’intérieur du bloc do. Si aucune erreur n’est générée, le code à l’intérieur du bloc do sera exécuté normalement.

Si une erreur est générée, elle sera capturée par le bloc catch. Dans ce bloc, l’erreur est stockée dans une variable qui peut être utilisée pour identifier le type d’erreur et fournir une réponse appropriée à l’utilisateur.

Voici concrètement ce que ça donne :

enum DivisionError: Error {
    case divideByZero
}

func divide(_ number: Int, by divisor: Int) throws -> Int {
    guard divisor != 0 else {
        throw DivisionError.divideByZero
    }
    return number / divisor
}

do {
    let result = try divide(10, by: 0)
    print(result)
} catch DivisionError.divideByZero {
    print("Impossible de diviser par zéro !")
} catch {
    print("Erreur inconnue : \(error)")
}

La division par 0 étant impossible, il est utile de créer une erreur dans l’énumération DivisionError. Ensuite, le cas divideByZero est appliqué si le divisor est égal à 0.

• Convertir les erreurs avec des valeurs optionnelles

On peut utiliser le try? pour gérer une erreur en la convertissant en valeur optionnelle. Si une erreur se produit, la valeur de l’expression est nulle :

func openFile(named fileName: String) throws {
    // Ouvre le fichier et renvoie une erreur si le fichier n'existe pas
}

if let file = try? openFile(named: "monFichier.txt") {
    // Le fichier a été ouvert avec succès
} else {
    // Une erreur s'est produite
}

• Forcer le code

On peut utiliser le try! pour appeler de force une fonction qui pourrait générer une erreur. Ainsi, on n’est plus obligé d’utiliser de do/catch. Mais, par conséquent, si l’erreur se produit, l’application va crasher :

func openFile(named fileName: String) throws {
    // Ouvre le fichier et renvoie une erreur si le fichier n'existe pas
}

let file = try! openFile(named: "monFichier.txt")

Erreurs personnalisées

Les énumérations Swift sont particulièrement bien adaptées à la création d’un groupe de conditions d’erreur. Les valeurs associées permettant de communiquer des informations supplémentaires sur la nature d’une erreur.

Par exemple, pour la gestion d’erreur d’un site de réservation de restaurant :

enum ReservationError: Error {
    case invalidDate
    case invalidTime
    case unavailableTable
    case reservationNotFound
    case maximumReservationReached
    case bookingFailed
} 
throw ReservationError.invalidDate

Les bonnes pratiques

  1. Utilise la gestion d’erreur pour les erreurs qui peuvent être récupérées. La gestion d’erreur est conçue pour les erreurs récupérables qui peuvent être gérées. Les erreurs non récupérables, comme une condition de précondition non satisfaite, devraient être traitées à l’aide de la clause fatalError ou assert.
  2. Utilise des types d’erreur spécifiques. Utilise des types d’erreur spécifiques pour chaque fonction ou méthode. Cela permettra de mieux comprendre l’erreur en cas d’échec et de faciliter la maintenance du code.
  3. Personnalise les erreurs : Créez des types d’erreur personnalisés pour fournir des informations plus précises sur les erreurs.
  4. Utilise defer. Pour garantir que certaines tâches soient toujours effectuées, quelle que soit l’issue de la fonction ou de la méthode, on utilise le defer. Il garantit que l’action soit effectuée à la fin du bloc de code.
  5. Évite d’utiliser try!. Puisque cette syntaxe force l’exécution d’une fonction même si elle peut générer une erreur, cela peut causer des crash inattendus et des erreurs difficiles à résoudre.
  6. Utilise plutôt la syntaxe try? pour les fonctions qui renvoient une valeur optionnelle. Elle permet de gérer les erreurs de manière concise lorsqu’une fonction renvoie une valeur optionnelle.
  7. Enregistre les erreurs dans les logs. Afin de faciliter le débogage et la maintenance.
  8. Teste les erreurs. Pour vérifier que le code fonctionne correctement dans toutes les conditions possibles. Les tests automatisés sont particulièrement utiles pour détecter les erreurs potentielles.


Ça y est, tu es un(e) pro de la gestion d’erreur ! Pour en savoir un peu plus, je t’invite à lire la documentation officielle Apple à ce sujet.

Dis nous en commentaire si cet article t’a été utile ✌️

Si tu as aimé cet article, partage le 🫶