Gestion des erreurs en langage Swift

190

Je n'ai pas trop lu Swift, mais une chose que j'ai remarquée est qu'il n'y a pas d'exceptions. Alors, comment font-ils la gestion des erreurs dans Swift? Quelqu'un a-t-il trouvé quelque chose concernant la gestion des erreurs?

peko
la source
1
J'ai trouvé des messages d'erreur comme avec Obj-C: o
Arbitur
13
@Arbitur la bonne vieille manière de segfault?
peko
J'ai créé un NSTimer dans Swift et quand j'ai mal orthographié la fonction, il s'est écrasé et m'a donné une erreur disant qu'il ne pouvait pas trouver la méthode :)
Arbitur
3
Vous pouvez ajouter le support try-catch pour Swift en suivant les instructions de cet article: medium.com/@_willfalcon/adding-try-catch-to-swift-71ab27bcb5b8
William Falcon
@peko Comment gérez-vous un segfault dans Swift? Je ne pense pas que ce soit possible pour le moment, ce qui rend malheureusement certaines erreurs irrécupérables
Orlin Georgiev

Réponses:

148

Swift 2 et 3

Les choses ont un peu changé dans Swift 2, car il existe un nouveau mécanisme de gestion des erreurs, qui est un peu plus similaire aux exceptions mais différent en détail.

1. Indication de la possibilité d'erreur

Si la fonction / méthode veut indiquer qu'elle peut générer une erreur, elle doit contenir un throwsmot clé comme celui-ci

func summonDefaultDragon() throws -> Dragon

Remarque: il n'y a pas de spécification du type d'erreur que la fonction peut réellement générer. Cette déclaration indique simplement que la fonction peut lancer une instance de n'importe quel type implémentant ErrorType ou ne la lance pas du tout.

2. Appel d'une fonction susceptible de générer des erreurs

Pour appeler la fonction, vous devez utiliser le mot clé try, comme celui-ci

try summonDefaultDragon()

cette ligne devrait normalement être présente dans un bloc do-catch comme celui-ci

do {
    let dragon = try summonDefaultDragon() 
} catch DragonError.dragonIsMissing {
    // Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
    // Other specific-case error-handlng
} catch {
    // Catch all error-handling
}

Remarque: la clause catch utilise toutes les fonctionnalités puissantes de la correspondance de motifs Swift, vous êtes donc très flexible ici.

Vous pouvez décider de propager l'erreur, si vous appelez une fonction de lancement à partir d'une fonction qui est elle-même marquée par le throwsmot-clé:

func fulfill(quest: Quest) throws {
    let dragon = try summonDefaultDragon()
    quest.ride(dragon)
} 

Vous pouvez également appeler la fonction de lancement en utilisant try?:

let dragonOrNil = try? summonDefaultDragon()

De cette façon, vous obtenez la valeur de retour ou nil, si une erreur s'est produite. En utilisant cette méthode, vous n'obtenez pas l'objet d'erreur.

Ce qui signifie que vous pouvez également combiner try?avec des déclarations utiles telles que:

if let dragon = try? summonDefaultDragon()

ou

guard let dragon = try? summonDefaultDragon() else { ... }

Enfin, vous pouvez décider que vous savez que l'erreur ne se produira pas réellement (par exemple parce que vous avez déjà vérifié les prérequis) et utiliser le try!mot-clé:

let dragon = try! summonDefaultDragon()

Si la fonction génère réellement une erreur, vous obtiendrez une erreur d'exécution dans votre application et l'application se terminera.

3. Lancer une erreur

Pour lancer une erreur, vous utilisez un mot-clé throw comme celui-ci

throw DragonError.dragonIsMissing

Vous pouvez lancer tout ce qui est conforme au ErrorTypeprotocole. Pour commencer, il NSErrorest conforme à ce protocole, mais vous aimeriez probablement utiliser enum-based ErrorTypequi vous permet de regrouper plusieurs erreurs liées, potentiellement avec des données supplémentaires, comme celle-ci

enum DragonError: ErrorType {
    case dragonIsMissing
    case notEnoughMana(requiredMana: Int)
    ...
}

Les principales différences entre le nouveau mécanisme d'erreur Swift 2 & 3 et les exceptions de style Java / C # / C ++ sont les suivantes:

  • La syntaxe est un peu différente: do-catch+ try+ defervs traditionnelletry-catch-finally syntaxe .
  • La gestion des exceptions entraîne généralement un temps d'exécution beaucoup plus long dans le chemin d'exception que dans le chemin de réussite. Ce n'est pas le cas des erreurs Swift 2.0, où le chemin de réussite et le chemin d'erreur coûtent à peu près le même prix.
  • Tout code de lancement d'erreur doit être déclaré, tandis que des exceptions peuvent avoir été levées de n'importe où. Toutes les erreurs sont des «exceptions vérifiées» dans la nomenclature Java. Cependant, contrairement à Java, vous ne spécifiez pas les erreurs potentiellement générées.
  • Les exceptions Swift ne sont pas compatibles avec les exceptions ObjC. Votre do-catchbloc n'attrapera aucune NSException, et vice versa, pour cela vous devez utiliser ObjC.
  • Les exceptions Swift sont compatibles avec les NSErrorconventions de la méthode Cocoa consistant à retourner false(pour Boolrenvoyer des fonctions) ou nil(pour AnyObjectrenvoyer des fonctions) et à transmettre NSErrorPointeravec les détails de l'erreur.

En tant que sucre syntaxique supplémentaire pour faciliter la gestion des erreurs, il existe deux autres concepts

  • actions différées (en utilisant un defermot-clé) qui vous permettent d'obtenir le même effet que les blocs finally en Java / C # / etc
  • déclaration de garde (en utilisant un guardmot-clé) qui vous permet d'écrire un peu moins de code if / else que dans le code de vérification / signalisation d'erreur normal.

Swift 1

Erreurs d'exécution:

Comme Leandros le suggère pour gérer les erreurs d'exécution (comme les problèmes de connectivité réseau, l'analyse des données, l'ouverture de fichier, etc.), vous devez utiliser NSErrorcomme vous l'avez fait dans ObjC, car la Fondation, AppKit, UIKit, etc. signalent leurs erreurs de cette manière. C'est donc plus une question de cadre que de langage.

Un autre modèle fréquemment utilisé est celui des blocs de succès / échec de séparateur comme dans AFNetworking:

var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
    success: { (NSURLSessionDataTask) -> Void in
        println("Success")
    },
    failure:{ (NSURLSessionDataTask, NSError) -> Void in
        println("Failure")
    })

Toujours le bloc d'échec a fréquemment reçu l' NSErrorinstance, décrivant l'erreur.

Erreurs de programmeur:

Pour les erreurs de programmeur (comme l'accès hors limites à l'élément de tableau, les arguments non valides passés à un appel de fonction, etc.), vous avez utilisé des exceptions dans ObjC. Swift langue ne semble pas avoir de prise en charge linguistique des exceptions (comme throw, catch, etc mot - clé). Cependant, comme la documentation le suggère, il s'exécute sur le même runtime qu'ObjC, et vous pouvez donc toujours lancer NSExceptionscomme ceci:

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

Vous ne pouvez tout simplement pas les attraper en Swift pur, bien que vous puissiez opter pour intercepter les exceptions dans le code ObjC.

La question est de savoir si vous devez lever des exceptions pour les erreurs du programmeur, ou plutôt utiliser des assertions comme le suggère Apple dans le guide du langage.

MDJ
la source
20
«problèmes de connectivité réseau» et «ouverture de fichiers» à l'aide des API Cocoa (NSFileHandle) peuvent générer des exceptions qui doivent être interceptées. Sans exceptions dans Swift, vous devez implémenter cette partie de votre programme dans Objective-C ou effectuer tout votre travail en utilisant les API BSD C (qui sont toutes deux de mauvaises solutions de contournement). Consultez la documentation de NSFileHandle.writeData pour en savoir plus ... developer.apple.com/library/ios/documentation/Cocoa/Reference/… :
Matt Gallagher
5
Encore une fois, aucune gestion des exceptions signifie une construction d'objet en deux étapes avec tous ses problèmes inhérents. Voir stroustrup.com/except.pdf .
Phil
2
le fatalError(...)est le même aussi bien.
holex
8
Autant j'aime Swift, je pense que c'est un choix catastrophique, et ayant eu un avant-goût de certaines des conséquences, ils jouent avec le feu avec cette omission ...
Rob
2
Oui, les exceptions vérifiées sont maintenant utilisées avec parcimonie, car il a été constaté qu'en forçant le programmeur à intercepter des exceptions, il a peu d'espoir de récupérer du code pollue de manière à briser le principe de responsabilité unique. Une classe au niveau du domaine ne souhaite pas avoir à gérer les exceptions de la couche infrastructure. Alors maintenant, les exceptions non vérifiées ont tendance à être favorisées, et la partie intéressée peut les attraper, le cas échéant. . Vérifié = définitivement récupérable. Décoché = non / potentiellement récupérable.
Jasper Blues
69

Mise à jour du 9 juin 2015 - Très important

Swift 2.0 est livré avec try, throwet des catchmots - clés et le plus excitant est la suivante :

Swift traduit automatiquement les méthodes Objective-C qui produisent des erreurs en méthodes qui génèrent une erreur selon la fonctionnalité native de gestion des erreurs de Swift.

Remarque: les méthodes qui consomment des erreurs, telles que les méthodes déléguées ou les méthodes qui prennent un gestionnaire d'achèvement avec un argument d'objet NSError, ne deviennent pas des méthodes qui se lancent lorsqu'elles sont importées par Swift.

Extrait de: Apple Inc. «Utilisation de Swift avec Cocoa et Objective-C (version préliminaire de Swift 2)». iBooks.

Exemple: (du livre)

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
    NSLog(@"Error: %@", error.domain);
}

L'équivalent en swift sera:

let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
    try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
    print ("Error: \(error.domain)")
}

Lancer une erreur:

*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]

Sera automatiquement propagé à l'appelant:

throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)

D'après les livres Apple, The Swift Programming Language, il semble que les erreurs devraient être gérées à l'aide d'énumération.

Voici un exemple du livre.

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure...  \(error)"
}

De: Apple Inc. «The Swift Programming Language». iBooks. https://itun.es/br/jEUH0.l

Mettre à jour

Tiré des livres d'actualités Apple, "Utilisation de Swift avec Cocoa et Objective-C". Les exceptions d'exécution ne se produisent pas en utilisant des langages Swift, c'est pourquoi vous n'avez pas try-catch. À la place, vous utilisez le chaînage facultatif .

Voici un extrait du livre:

Par exemple, dans la liste de codes ci-dessous, les première et deuxième lignes ne sont pas exécutées car la propriété length et la méthode characterAtIndex: n'existent pas sur un objet NSDate. La constante myLength est supposée être un Int facultatif et est définie sur nil. Vous pouvez également utiliser une instruction if – let pour dérouler conditionnellement le résultat d'une méthode à laquelle l'objet peut ne pas répondre, comme indiqué à la ligne trois

let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
    println("Found \(fifthCharacter) at index 5")
}

Extrait de: Apple Inc. «Utilisation de Swift avec Cocoa et Objective-C». iBooks. https://itun.es/br/1u3-0.l


Et les livres vous encouragent également à utiliser le modèle d'erreur cacao d'Objective-C (NSError Object)

Le rapport d'erreurs dans Swift suit le même modèle que dans Objective-C, avec l'avantage supplémentaire d'offrir des valeurs de retour facultatives. Dans le cas le plus simple, vous renvoyez une valeur booléenne à partir de la fonction pour indiquer si elle a réussi ou non. Lorsque vous devez signaler la raison de l'erreur, vous pouvez ajouter à la fonction un paramètre NSError out de type NSErrorPointer. Ce type est à peu près équivalent à NSError ** d'Objective-C, avec une sécurité de mémoire supplémentaire et un typage facultatif. Vous pouvez utiliser le préfixe & opérateur pour transmettre une référence à un type NSError facultatif en tant qu'objet NSErrorPointer, comme indiqué dans la liste de codes ci-dessous.

var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
    encoding: NSUTF8StringEncoding,
    error: &writeError)
if !written {
    if let error = writeError {
        println("write failure: \(error.localizedDescription)")
    }
}

Extrait de: Apple Inc. «Utilisation de Swift avec Cocoa et Objective-C». iBooks. https://itun.es/br/1u3-0.l

Guilherme Torres Castro
la source
Pour la dernière instruction, cela devrait être: do {try myString.writeToFile (path, atomically: true, encoding: NSUTF8StringEncoding)} catch let error as NSError {print (error)}
Jacky
1
@Jacky Oui, c'est vrai pour swift 2.0 bien que cette réponse ait été écrite avant la sortie de swift 2.0, j'ai mis à jour la réponse pour montrer la nouvelle façon de gérer les erreurs dans swift 2.0. Je pensais laisser cette manière pour référence, mais je vais envisager de mettre à jour la réponse entière pour n'utiliser que swift 2.0
Guilherme Torres Castro
12

Il n'y a pas d'exceptions dans Swift, similaire à l'approche d'Objective-C.

En développement, vous pouvez utiliser assert pour détecter les erreurs qui pourraient apparaître et qui doivent être corrigées avant de passer en production.

L' NSErrorapproche classique n'est pas modifiée, vous envoyez unNSErrorPointer , qui se remplit.

Bref exemple:

var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
    println("An error occurred \(error)")
} else {
    println("Contents: \(contents)")
}
Leandros
la source
6
Cela soulève deux questions: que se passe-t-il lorsque le code ObjC que nous appelons à partir de Swift lève réellement une exception, et si NSError est notre objet d'erreur universel comme dans ObjC?
MDJ
1
Est-ce juste une réalité de la vie avec Swift que les initialiseurs échouent ou ne peuvent pas échouer?
Phil
11
La gestion des exceptions semble plutôt sale
Tash Pemhiwa
27
Ouais, qui a besoin d'exceptions quand tu peux simplement tomber en panne? Ou mettre un NSError ** comme argument dans toutes les fonctions que vous déclarez? de sorte que tout f();g();devient f(&err);if(err) return;g(&err);if(err) return;pour le premier mois, puis il devient justef(nil);g(nil);hopeToGetHereAlive();
hariseldon78
2
Cette réponse est à la fois obsolète (Swift prend désormais en charge les exceptions) et fausse (Objective-C prend en charge les exceptions.
Rog
11

Le 'Swift Way' recommandé est:

func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
    return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}

var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
    println("write failure 1: \(writeError!.localizedDescription)")
    // assert(false) // Terminate program
}

Cependant, je préfère try / catch car je trouve cela plus facile à suivre car il déplace la gestion des erreurs vers un bloc séparé à la fin, cet arrangement est parfois appelé "Golden Path". Heureusement, vous pouvez le faire avec des fermetures:

TryBool {
    write("~/Error2")(error: $0) // The code to try
}.catch {
    println("write failure 2: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Il est également facile d'ajouter une fonction de nouvelle tentative:

TryBool {
    write("~/Error3")(error: $0) // The code to try
}.retry {
    println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
    return write("~/Error3r")  // The code to retry
}.catch {
    println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

La liste de TryBool est:

class TryBool {
    typealias Tryee = NSErrorPointer -> Bool
    typealias Catchee = NSError? -> ()
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return self.retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) {
        var error: NSError?
        for numRetries in 0...retries { // First try is retry 0
            error = nil
            let result = tryee(&error)
            if result {
                return
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        catchee(error)
    }
}

Vous pouvez écrire une classe similaire pour tester une valeur renvoyée facultative au lieu d'une valeur booléenne:

class TryOptional<T> {
    typealias Tryee = NSErrorPointer -> T?
    typealias Catchee = NSError? -> T
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) -> T {
        var error: NSError?
        for numRetries in 0...retries {
            error = nil
            let result = tryee(&error)
            if let r = result {
                return r
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        return catchee(error)
    }
}

La version TryOptional applique un type de retour non optionnel qui facilite la programmation ultérieure, par exemple 'Swift Way:

struct FailableInitializer {
    init?(_ id: Int, error: NSErrorPointer) {
        // Always fails in example
        if error != nil {
            error.memory = NSError(domain: "", code: id, userInfo: [:])
        }
        return nil
    }
    private init() {
        // Empty in example
    }
    static let fallback = FailableInitializer()
}

func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
    return FailableInitializer(id, error: error)
}

var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
    println("failableInitializer failure code: \(failError!.code)")
    failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap

Utilisation de TryOptional:

let failure2 = TryOptional {
    failableInitializer(2)(error: $0)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

let failure3 = TryOptional {
    failableInitializer(3)(error: $0)
}.retry {
    println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
    return failableInitializer(31)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

Notez le déballage automatique.

Howard Lovatt
la source
7

Edit: Bien que cette réponse fonctionne, ce n'est guère plus qu'Objective-C translittéré en Swift. Il a été rendu obsolète par les modifications apportées à Swift 2.0. La réponse de Guilherme Torres Castro ci-dessus est une très bonne introduction à la manière préférée de gérer les erreurs dans Swift. VOS

Il a fallu un peu de temps pour le comprendre, mais je pense que je l'ai compris. Cela semble moche cependant. Rien de plus qu'une peau fine sur la version Objective-C.

Appel d'une fonction avec un paramètre NSError ...

var fooError : NSError ? = nil

let someObject = foo(aParam, error:&fooError)

// Check something was returned and look for an error if it wasn't.
if !someObject {
   if let error = fooError {
      // Handle error
      NSLog("This happened: \(error.localizedDescription)")
   }
} else {
   // Handle success
}`

Ecriture de la fonction qui prend un paramètre d'erreur ...

func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {

   // Do stuff...

   if somethingBadHasHappened {
      if error {
         error.memory = NSError(domain: domain, code: code, userInfo: [:])
      }
      return nil
   }

   // Do more stuff...
}
Vince O'Sullivan
la source
5

Emballage de base autour de l'objectif C qui vous donne la fonction try catch. https://github.com/williamFalcon/SwiftTryCatch

Utilisez comme:

SwiftTryCatch.try({ () -> Void in
        //try something
     }, catch: { (error) -> Void in
        //handle error
     }, finally: { () -> Void in
        //close resources
})
William Falcon
la source
Bonne idée. Mais qui décide de l'utiliser doit garder à l'esprit que les objets alloués dans le bloc try ne sont pas désalloués lorsqu'une exception est levée. Cela peut causer des problèmes d'objets zombies et chaque utilisation de RAII est compromise (déverrouillage automatique, auto-sql-commit, auto-sql-rollback ...). Peut-être que c ++ pourrait nous aider avec une forme de "runAtExit"?
hariseldon78
Mise à jour: je viens de découvrir qu'il y a un drapeau dans clang pour activer la libération d'objets lors du lancement d'exceptions: -fobjc-arc-exceptions. Je dois essayer si cela fonctionne toujours avec la version enveloppée (je pense que cela devrait)
hariseldon78
Si vous utilisez cette option, sachez que la taille du code augmente car le compilateur doit générer du code semi-sécurisé pour les exceptions. Aussi: se fier à une telle fonctionnalité de compilateur n'est peut-être pas la meilleure idée. Les exceptions ne concernent que les erreurs de programmeur, donc utiliser cette option du compilateur pour économiser un peu de mémoire pendant le développement ne vaut pas la peine. Si vous avez des exceptions dans votre code de production, vous devez d'abord vous occuper de la cause de ces exceptions.
Christian Kienle
1
Il peut y avoir des situations hors de votre contrôle. Par exemple, analyser json dans le mauvais format.
William Falcon
3

Ceci est une réponse de mise à jour pour swift 2.0. Je suis impatient d'avoir un modèle de gestion des erreurs riche en fonctionnalités, comme en java. Enfin, ils ont annoncé la bonne nouvelle. ici

Modèle de gestion des erreurs: le nouveau modèle de gestion des erreurs de Swift 2.0 se sentira instantanément naturel, avec des mots-clés try, throw et catch familiers . Mieux encore, il a été conçu pour fonctionner parfaitement avec les SDK Apple et NSError. En fait, NSError est conforme à ErrorType de Swift. Vous voudrez certainement regarder la session de la WWDC sur les nouveautés de Swift pour en savoir plus.

par exemple :

func loadData() throws { }
func test() {
do {
    try loadData()
} catch {
    print(error)
}}
Paraneetharan Saravanaperumal
la source
3

Comme Guilherme Torres Castro a dit, à Swift 2.0, try, catch,do peut être utilisé dans la programmation.

Par exemple, dans la méthode de récupération de données CoreData, au lieu de le placer en &errortant que paramètre dans le managedContext.executeFetchRequest(fetchRequest, error: &error), il ne nous reste plus qu'à utiliser use managedContext.executeFetchRequest(fetchRequest), puis à gérer l'erreur avec try, catch( Apple Document Link )

do {
   let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
   if let results = fetchedResults{
      people = results
   }
} catch {
   print("Could not fetch")
}

Si vous avez déjà téléchargé la version bêta de xcode7. Essayez de rechercher des erreurs de lancement dans la documentation et la référence d'API et choisissez le premier résultat affiché, cela donne une idée de base de ce qui peut être fait pour cette nouvelle syntaxe. Cependant, la documentation complète n'est pas encore publiée pour de nombreuses API.

Des techniques plus sophistiquées de gestion des erreurs peuvent être trouvées dans

Quoi de neuf dans Swift (2015 Session 106 28m30s)

Zingoer
la source
1

Belle et simple bibliothèque pour gérer l'exception: TryCatchFinally-Swift

Comme quelques autres, il englobe les fonctionnalités d'exception de l'objectif C.

Utilisez-le comme ceci:

try {
    println("  try")
}.catch { e in
    println("  catch")
}.finally {
    println("  finally")
}
Morten Holmgaard
la source
J'ai ajouté un échantillon :)
Morten Holmgaard
Cela vaut probablement la peine de mentionner l'opinion des auteurs: "Attention: c'est un hack pour le plaisir et le mal. Résistez à la tentation de l'utiliser."
jbat100
1

À partir de Swift 2, comme d'autres l'ont déjà mentionné, la gestion des erreurs est mieux accomplie en utilisant les énumérations do / try / catch et ErrorType. Cela fonctionne assez bien pour les méthodes synchrones, mais un peu d'intelligence est nécessaire pour la gestion des erreurs asynchrones.

Cet article a une excellente approche de ce problème:

https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/

Résumer:

// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData

// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
    {
    completionHandler()
    }

puis, l'appel à la méthode ci-dessus serait le suivant:

self.loadData("someString",
    completionHandler:     
        { result: LoadDataResult in
        do
            {
            let data = try result()
            // success - go ahead and work with the data
            }
        catch
            {
            // failure - look at the error code and handle accordingly
            }
        })

Cela semble un peu plus propre que d'avoir un rappel errorHandler séparé passé à la fonction asynchrone, ce qui était la façon dont cela serait géré avant Swift 2.

Gene Loparco
la source
0

Ce que j'ai vu, c'est qu'en raison de la nature de l'appareil, vous ne voulez pas lancer un tas de messages de gestion d'erreurs cryptiques à l'utilisateur. C'est pourquoi la plupart des fonctions renvoient des valeurs facultatives, puis vous codez simplement pour ignorer l'option. Si une fonction revient à zéro, ce qui signifie qu'elle a échoué, vous pouvez afficher un message ou autre.

cheborneck
la source
1
Renvoyer un nul ne renvoie aucune information sur la nature de l'erreur. Si un objet d'erreur est renvoyé lorsqu'une erreur se produit, alors, en fonction de l'erreur, le programmeur peut choisir de l'ignorer, de le gérer, de le laisser bouillonner ou de "faire apparaître un message ou autre". La connaissance est le pouvoir.
Vince O'Sullivan