Mot-clé de garde de Swift

197

Swift 2 a introduit le guardmot - clé, qui pourrait être utilisé pour s'assurer que diverses données sont configurées prêtes à l'emploi. Un exemple que j'ai vu sur ce site Web montre une fonction submitTapped:

func submitTapped() {
    guard username.text.characters.count > 0 else {
        return
    }

    print("All good")
}

Je me demande si l'utilisation guardest différente de la faire à l'ancienne, en utilisant une ifcondition. Cela donne-t-il des avantages que vous ne pourriez pas obtenir en utilisant un simple chèque?

David Snabel
la source
Voir aussi la garde vs si-let question
Honey
Veuillez vous référer au lien suivant medium.com/@pvpriya7/swift-guard-18e59c50c624
Priyanka V

Réponses:

369

En lisant cet article, j'ai remarqué de grands avantages en utilisant Guard

Ici, vous pouvez comparer l'utilisation de la garde avec un exemple:

C'est la partie sans garde:

func fooBinding(x: Int?) {
    if let x = x where x > 0 {
        // Do stuff with x
        x.description
    }

    // Value requirements not met, do something
}
  1. Ici, vous mettez votre code souhaité dans toutes les conditions

    Vous ne verrez peut-être pas immédiatement un problème avec cela, mais vous pourriez imaginer à quel point cela pourrait devenir confus s'il était imbriqué avec de nombreuses conditions qui devaient toutes être remplies avant d'exécuter vos instructions

La façon de nettoyer cela est de faire d'abord chacun de vos contrôles et de quitter si aucun n'est respecté. Cela permet de comprendre facilement quelles conditions feront quitter cette fonction.

Mais maintenant, nous pouvons utiliser la garde et nous pouvons voir qu'il est possible de résoudre certains problèmes:

func fooGuard(x: Int?) {
    guard let x = x where x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
    x.description
}
  1. Vérifier l'état que vous voulez, pas celui que vous ne voulez pas. Ceci est à nouveau similaire à une assertion. Si la condition n'est pas remplie, l'instruction else de guard est exécutée, ce qui sort de la fonction.
  2. Si la condition réussit, la variable facultative ici est automatiquement dépliée pour vous dans la portée que l'instruction de garde a été appelée - dans ce cas, la fonction fooGuard (_ :).
  3. Vous recherchez les mauvais cas tôt, ce qui rend votre fonction plus lisible et plus facile à maintenir

Ce même modèle s'applique également aux valeurs non facultatives:

func fooNonOptionalGood(x: Int) {
    guard x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

func fooNonOptionalBad(x: Int) {
    if x <= 0 {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

Si vous avez encore des questions, vous pouvez lire l'intégralité de l'article: Déclaration de Swift Guard.

Emballer

Et enfin, en lisant et en testant, j'ai trouvé que si vous utilisez guard pour déballer les options,

ces valeurs non enveloppées restent pour que vous puissiez les utiliser dans le reste de votre bloc de code

.

guard let unwrappedName = userName else {
    return
}

print("Your username is \(unwrappedName)")

Ici, la valeur déballée ne serait disponible qu'à l'intérieur du bloc if

if let unwrappedName = userName {
    print("Your username is \(unwrappedName)")
} else {
    return
}

// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")
Jorge Casariego
la source
3
Hé @Eric tu as fait un excellent post! Merci à vous de le rendre si facile à comprendre!
Jorge Casariego
1
J'utilise guard for pour déballer NSError. Mais quand j'essaye de l'utiliser dans la portée de la garde (pour passer une erreur dans un rappel par exemple), il dit "La variable déclarée en condition de garde n'est pas utilisable dans son corps". Cela a-t-il un sens? Merci
GeRyCh
6
Le déballage de @GeRyCh dans une instruction de garde rend cette variable disponible après l'instruction de garde, pas à l'intérieur. Cela m'a pris un certain temps pour m'y habituer.
DonnaLea
2
Voici un autre excellent article sur l'utilisation de Guard pour déballer proprement les options. Résume bien.
Doches
let x = x where x > 0cela signifie-t-il que vous avez associé une autre condition à votre liaison facultative? Je veux dire que c'est un peu différent deif let constantName = someOptional { statements }
Honey
36

Contrairement à if, guardcrée la variable accessible depuis l'extérieur de son bloc. Il est utile de déballer un grand nombre de Optionals.

takebayashi
la source
24

Il y a vraiment deux gros avantages guard. L'un évite la pyramide du destin, comme d'autres l'ont mentionné - beaucoup de ennuyeuxif let déclarations imbriquées les unes dans les autres se déplaçant de plus en plus vers la droite.

L'autre avantage est souvent que la logique que vous souhaitez implémenter est plus " if not let" que "if let { } else ".

Voici un exemple: supposons que vous souhaitiez implémenter accumulate- un croisement entre mapet reduceoù cela vous donne un tableau de réduction en cours d' exécution . Le voici avec guard:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
        // if there are no elements, I just want to bail out and
        // return an empty array
        guard var running = self.first else { return [] }

        // running will now be an unwrapped non-optional
        var result = [running]

        // dropFirst is safe because the collection
        // must have at least one element at this point
        for x in dropFirst(self) {
            running = combine(running, x)
            result.append(running)
        }
        return result
    }

}


let a = [1,2,3].accumulate(+)  // [1,3,6]
let b = [Int]().accumulate(+)  // []

Comment l'écririez-vous sans garde, mais toujours en utilisant firstcela renvoie une option? Quelque chose comme ça:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {

        if var running = self.first  {
            var result = [running]

            for x in dropFirst(self) {
                running = combine(running, x)
                result.append(running)
            }
            return result
        }
        else {
            return []
        }
    }

}

L'imbrication supplémentaire est ennuyeuse, mais aussi, ce n'est pas aussi logique d'avoir le ifet le elsesi éloignés. Il est beaucoup plus lisible d'avoir la sortie anticipée pour le cas vide, puis de continuer avec le reste de la fonction comme si ce n'était pas une possibilité.

Vitesse de vitesse
la source
19

Lorsqu'une condition est remplie, guardelle expose les variables déclarées dans le guardbloc au reste du bloc de code, les amenant dans sa portée. Ce qui, comme indiqué précédemment, sera certainement utile avec les if letdéclarations imbriquées .

Notez que le gardien requiert un retour ou un lancer dans sa déclaration else.

Analyser JSON avec Guard

Voici un exemple de la façon dont on pourrait analyser un objet JSON en utilisant guard plutôt que if-let. Ceci est un extrait d'une entrée de blog qui comprend un fichier de terrain de jeu que vous pouvez trouver ici:

Comment utiliser Guard dans Swift 2 pour analyser JSON

func parseJSONWithGuard(data : [String : AnyObject]) throws -> Developer {

    guard let firstname = data["First"] as? String  else {
        return Developer() // we could return a nil Developer()
    }

    guard let lastname = data["Last"] as? String else {
        throw ParseError.BadName // or we could throw a custom exception and handle the error
    }

    guard let website = data["WebSite"] as? String else {
        throw ParseError.BadName
    }

    guard let iosDev = data["iosDeveloper"] as? Bool else {
        throw ParseError.BadName
    }



    return Developer(first: firstname, last: lastname, site: website, ios: iosDev)

}

aire de jeux de téléchargement: aire de jeux de garde

Plus d'informations:

Voici un extrait du Guide du langage de programmation Swift:

Si la condition de l'instruction de garde est remplie, l'exécution du code se poursuit après l'accolade de fermeture de l'instruction de garde. Toutes les variables ou constantes auxquelles des valeurs ont été affectées à l'aide d'une liaison facultative dans le cadre de la condition sont disponibles pour le reste du bloc de code dans lequel l'instruction guard apparaît.

Si cette condition n'est pas remplie, le code à l'intérieur de la branche else est exécuté. Cette branche doit transférer le contrôle pour quitter le bloc de code dans lequel cette instruction guard apparaît. Elle peut le faire avec une instruction de transfert de contrôle telle que return, break ou continue, ou elle peut appeler une fonction ou une méthode qui ne retourne pas, telle comme fatalError ().

Dan Beaulieu
la source
7

Un avantage est l'élimination d'un grand nombre d' if letinstructions imbriquées . Voir la vidéo "Quoi de neuf dans Swift" de la WWDC vers 15h30, la section intitulée "Pyramide de Doom".

zaph
la source
6

Quand utiliser des gardes

Si vous avez un contrôleur de vue avec quelques éléments UITextField ou un autre type d'entrée utilisateur, vous remarquerez immédiatement que vous devez déballer le textField.text facultatif pour accéder au texte à l'intérieur (le cas échéant!). isEmpty ne vous fera aucun bien ici, sans aucune entrée, le champ de texte retournera simplement nil.

Vous en avez donc quelques-unes que vous dépliez et que vous passez éventuellement à une fonction qui les publie sur un point de terminaison de serveur. Nous ne voulons pas que le code du serveur doive traiter avec des valeurs nulles ou envoyer par erreur des valeurs invalides au serveur, nous allons donc déballer ces valeurs d'entrée avec garde en premier.

func submit() {
    guard let name = nameField.text else {
        show("No name to submit")
        return
    }

    guard let address = addressField.text else {
        show("No address to submit")
        return
    }

    guard let phone = phoneField.text else {
        show("No phone to submit")
        return
    }

    sendToServer(name, address: address, phone: phone)
}

func sendToServer(name: String, address: String, phone: String) {
  ...
}

Vous remarquerez que notre fonction de communication avec le serveur prend comme paramètres des valeurs de chaîne non facultatives, d'où le déballage préalable de la garde. Le déballage n'est pas intuitif car nous sommes habitués à déballer avec if let qui déballe les valeurs à utiliser dans un bloc. Ici, l'instruction guard a un bloc associé mais c'est en fait un bloc else - c'est-à-dire la chose que vous faites si le débouclage échoue - les valeurs sont déroulées directement dans le même contexte que l'instruction elle-même.

// séparation des préoccupations

Sans garde

Sans utiliser de garde, nous nous retrouverions avec un gros tas de code qui ressemble à une pyramide de malheur . Cela ne convient pas bien pour l'ajout de nouveaux champs à notre formulaire ou pour un code très lisible. L'indentation peut être difficile à suivre, en particulier avec tant d'autres instructions à chaque fork.

func nonguardSubmit() {
    if let name = nameField.text {
        if let address = addressField.text {
            if let phone = phoneField.text {
                sendToServer(name, address: address, phone: phone)
            } else {
                show("no phone to submit")
            }
        } else {
            show("no address to submit")
        }
    } else {
        show("no name to submit")
    }
}

Oui, nous pourrions même combiner tout cela si les instructions let en une seule instruction séparées par des virgules, mais nous perdrions la possibilité de déterminer quelle instruction a échoué et de présenter un message à l'utilisateur.

https://thatthinginswift.com/guard-statement-swift/

Mon chéri
la source
5

En utilisant la garde, notre intention est claire. nous ne voulons pas exécuter le reste du code si cette condition particulière n'est pas satisfaite. ici, nous pouvons également étendre la chaîne, veuillez consulter le code ci-dessous:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here
Narendra G
la source
5

Déclaration de garde va faire. c'est différent

1) il me permet de réduire l'imbrication si l'énoncé
2) c'est d'augmenter ma portée que ma variable accessible

if

func doTatal(num1 : Int?, num2: Int?) {
  // nested if statement
    if let fistNum = num1 where num1 > 0 {
        if let lastNum = num2 where num2 < 50 {

          let total = fistNum + lastNum
        }
    }
 // don't allow me to access out of the scope 
 //total = fistNum + lastNum 
}

Déclaration de la garde

func doTatal(num1 : Int?, num2: Int?) {
   //reduce  nested if statement and check positive way not negative way 
    guard let fistNum = num1 where num1 > 0 else{
      return
    }
    guard  let lastNum = num2 where num2 < 50 else {
     return
    }
    // increase my scope which my variable accessible
    let total = fistNum + lastNum

}
Nazmul Hasan
la source
3

De la documentation Apple:

Déclaration de la garde

Une instruction de garde est utilisée pour transférer le contrôle du programme hors d'une portée si une ou plusieurs conditions ne sont pas remplies.

Synatx:

guard condition else {
    statements
}

Avantage:

1. En utilisant la guarddéclaration, nous pouvons nous débarrasser des conditionnels profondément imbriqués dont le seul but est de valider un ensemble d'exigences.

2. Il a été spécialement conçu pour quitter rapidement une méthode ou une fonction.

si vous utilisez si laissé ci-dessous est le code à quoi il ressemble.

  let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

        if error == nil {
            if let  statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 {
                if let data = data {

                    //Process Data Here.
                    print("Data: \(data)")

                } else {
                    print("No data was returned by the request!")
                }
            } else {
                print("Your request returned a status code other than 2XX!")
            }
        } else {
            print("Error Info: \(error.debugDescription)")
        }
    }
    task.resume()

En utilisant guard, vous pouvez transférer le contrôle hors de la portée si une ou plusieurs conditions ne sont pas remplies.

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

            /* GUARD: was there an error? */
            guard (error == nil) else {
                print("There was an error with your request: \(error)")
                return
            }

            /* GUARD: Did we get a successful 2XX response? */
            guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
                print("Your request returned a status code other than 2XX!")
                return
            }

            /* GUARD: was there any data returned? */
            guard let data = data else {
                print("No data was returned by the request!")
                return
            }

            //Process Data Here.
            print("Data: \(data)")
}
task.resume()

Référence:

1. Swift 2: Sortez tôt avec garde 2. Udacity 3. Déclaration de garde

Ashok R
la source
Mais vous pouvez faire ce dernier avec des instructions if? if condition { return }bourru?
Oliver Dixon
2

Comme une instruction if, guard exécute des instructions basées sur la valeur booléenne d'une expression. Contrairement à une instruction if, les instructions guard ne s'exécutent que si les conditions ne sont pas remplies. Vous pouvez penser à la garde plus comme une assertion, mais plutôt que de planter, vous pouvez quitter gracieusement.

voir: http://ericcerney.com/swift-guard-statement/

Zgpeace
la source
1

Cela rend vraiment le flux d'une séquence avec plusieurs recherches et options beaucoup plus concis et clair et réduit beaucoup d'imbrication. Voir le post d'Erica Sadun sur le remplacement des Ifs . .... Pourrait se laisser emporter, un exemple ci-dessous:

    let filteredLinks = locationsLinkedToList.filter({$0.actionVerb == movementCommand})
    guard let foundLink = filteredLinks.first else {return ("<Person> cannot go in that direction.", nil, nil)}
    guard filteredLinks.count == 1 else {return ("<Person> cannot decide which route to take.", nil, nil)}
    guard let nextLocation = foundLink.toLocation else {return ("<Person> cannot go in that direction.", nil, nil)}

Voyez si ça colle.

DavidS
la source
1

Autrement dit, il fournit un moyen de valider les champs avant l'exécution. Il s'agit d'un bon style de programmation car il améliore la lisibilité. Dans d'autres langues, cela peut ressembler à ceci:

func doSomething() {
    if something == nil {
        // return, break, throw error, etc.
    }
    ...
}

Mais parce que Swift vous fournit des options, nous ne pouvons pas vérifier si elle est nulle et affecter sa valeur à une variable. En revanche, if letvérifie qu'il n'est pas nul et attribue une variable pour contenir la valeur réelle. C'est là guardqu'entre en jeu. Il vous donne un moyen plus concis de quitter tôt en utilisant les options.

fusil
la source
1

Source: Garde dans Swift

Voyons l'exemple pour bien le comprendre

Exemple 1:

func validate() {         
    guard 3>2 else {             
    print ("False")             
    return         
    }         
    print ("True") //True     
} 
validate()

Dans l'exemple ci-dessus, nous voyons que 3 est supérieur à 2 et l'instruction à l'intérieur de la clause guard else est ignorée et True est imprimé.

Exemple 2:

func validate() {         
    guard 1>2 else {             
    print ("False")            //False 
    return         
    }         
    print ("True")      
} 
validate()

Dans l'exemple ci-dessus, nous voyons que 1 est inférieur à 2 et l'instruction à l'intérieur de la clause guard else est exécutée et False est imprimé suivi d'un retour.

Example 3: gaurd let, unwrapping optionals through guard let

func getName(args myName: String?) {
     guard let name = myName, !name.isEmpty else {
     print ("Condition is false")          // Condition is false            return         
     }         
     print("Condition is met\(name)")     
} 
getName(args: "")

Dans l'exemple ci-dessus, nous utilisons guard let pour déballer les options. Dans la fonction getName, nous avons défini une variable de type chaîne myName qui est facultative. Nous utilisons ensuite guard let pour vérifier si la variable myName est nulle ou non, sinon assigner à name et vérifier à nouveau, name n'est pas vide. Si les deux conditions sont remplies, c'est-à-dire vraies, le bloc else sera ignoré et affichera «Les conditions sont remplies avec le nom».

Fondamentalement, nous vérifions ici deux choses séparées par une virgule, d'abord déballer et facultatif et vérifier si cela satisfait ou non à la condition.

Ici, nous ne transmettons rien à la fonction, c'est-à-dire une chaîne vide et donc la condition est fausse est imprimée.

func getName(args myName: String?) {
     guard let name = myName, !name.isEmpty else {
     print ("Condition is false")          
     return         
     }        
     print("Condition is met \(name)") // Condition is met Hello    
} getName(args: "Hello")

Ici, nous passons "Bonjour" à la fonction et vous pouvez voir que la sortie est imprimée "La condition est remplie Bonjour".

Aditya
la source