Certaines opérations ne sont pas garanties pour toujours terminer l'exécution ou produire une sortie utile. Les optionnels sont utilisés pour représenter l'absence de valeur, mais lorsqu'une opération échoue, il est souvent utile de comprendre ce qui a causé l'échec, afin que votre code puisse répondre en conséquence.

Les énumérations Swift sont particulièrement bien adaptées à la modélisation d'un groupe de conditions d'erreurs liées, avec des valeurs associées permettant de communiquer des informations supplémentaires sur la nature d'une erreur.


enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}

Lancer une erreur vous permet d'indiquer que quelque chose d'inattendu s'est produit et que le déroulement normal de l'exécution ne peut pas continuer. Vous utilisez une instruction throw pour générer une erreur. Par exemple, le code suivant génère une erreur pour indiquer que cinq pièces supplémentaires sont nécessaires au distributeur automatique :


throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

Traitement des erreurs

Lorsqu'une erreur est générée, un morceau de code environnant doit être responsable de la gestion de l'erreur, par exemple, en corrigeant le problème, en essayant une autre approche ou en informant l'utilisateur de l'échec.

Il existe quatre façons de gérer les erreurs dans Swift. Vous pouvez propager l'erreur d'une fonction au code qui appelle cette fonction, gérer l'erreur à l'aide d'une instruction do-catch, gérer l'erreur comme une valeur facultative ou affirmer que l'erreur ne se produira pas.

Lorsqu'une fonction génère une erreur, elle modifie le déroulement de votre programme, il est donc important que vous puissiez identifier rapidement les endroits de votre code qui peuvent générer des erreurs. Pour identifier ces emplacements dans votre code, écrivez le mot-clé try avant un morceau de code qui appelle une fonction, une méthode ou un initialiseur qui peut générer une erreur.

Fonction de lancement

Pour indiquer qu'une fonction, une méthode ou un initialiseur peut générer une erreur, vous écrivez le mot-clé throws dans la déclaration de la fonction après ses paramètres. Une fonction marquée d'un throws est appelée une fonction de lancement. Si la fonction spécifie un type de retour, vous écrivez le throws avant la flèche de retour (->).


func canThrowErrors() throws -> String

Une fonction de lancement propage les erreurs qui sont lancées à l'intérieur de celle-ci vers la portée à partir de laquelle elle est appelée.

Seules les fonctions de lancement peuvent propager des erreurs. Toutes les erreurs lancées dans une fonction non lancée doivent être gérées à l'intérieur de la fonction.

Gérer une erreur

Prenons par exemple le cas d'une gestion de compte bancaire qui peut soulever plusieurs erreurs quand on essaye de faire passer une transaction :


enum AccountError: Error {
    case TransactionExceedsFunds
    case NonPositiveTransactionNotAllowed(Int)
}

TransactionExceedsFunds : le montant de la transaction est supérieur au solde du compte.

NonPositiveTransactionNotAllowed : le montant de la transaction doit être positif

Quand une erreur est envoyée il faut être capable de la gérer. Pour cela il y a 4 façons de faire en Swift :

  • Laisser se propager l'erreur en dehors de la fonction utilisant le mot clé throws pour que l'appelant la gère lui même
  • Attraper l'erreur nous même en utilisant une instruction do-catch
  • Gérer l'erreur comme une optionnelle avec le mot clé try?
  • Affirmer que l'erreur ne se produira jamais (à nos risques et périls) en utilisant le mot clé try!

struct BankAccount {
    
    var fund: Int

    mutating func withdraw(_ amount: Int) throws {
        guard amount < fund else {
          throw AccountError.TransactionExceedsFunds
        }

        guard amount > 0 else {
          throw AccountError.NonPositiveTransactionNotAllowed(amount)
        }

        fund -= amount
    } 
}

La méthode withdraw est marquée avec le mot clé throws, ce qui permet de déléguer la gestion des erreur à la méthode appelante. Pour cela il faut utiliser les instructions do-try-catch.


var bankAccount = BankAccount(fund: 300)

do {
    try bankAccount.withdraw(400)
}

catch AccountError.TransactionExceedsFunds {
    print("Pas assez de fond")
}

catch AccountError.NonPositiveTransactionNotAllowed(let amount) {
    print("Transaction invalide : \(amount)")
}

Nous faisons appel à la méthode withdraw. Cette méthode peut renvoyer des erreurs, donc pour l'appeler nous utilisons le mot clé try. Cela veut dire que nous allons essayer d'exécuter cette méthode et que si la moindre erreur survient l'exécution du block s'arrête là et la structure do-catch prend le relai. Si aucune erreur n'est relevée, l'exécution du bloc continue normalement. Les catch sont appelés uniquement si une erreur survient et uniquement le catch correspondant au type de l'erreur donnée est exécuté.

On peut aussi utiliser le mot clé try? ou try!


var bankAccount2 = BankAccount(fund: 500)

try? bankAccount2.withdraw(600)

try! bankAccount2.withdraw(200)

Avec le try? on décide juste de ne rien faire si une erreur survient alors qu'avec le try! si une erreur survient le programme va être stoppé. Donc il faut faire très attention quant à son utilisation.