Palabra clave de guardia de Swift

197

Swift 2 introdujo la guardpalabra clave, que podría usarse para garantizar que varios datos estén configurados listos para usar. Un ejemplo que vi en este sitio web demuestra una función submitTapped:

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

    print("All good")
}

Me pregunto si usar guardes diferente a hacerlo a la antigua usanza de una ifcondición. ¿Ofrece beneficios que no podría obtener con un simple cheque?

David Snabel
fuente
También vea la pregunta de guardia contra if-let
Honey
Consulte el siguiente enlace medium.com/@pvpriya7/swift-guard-18e59c50c624
Priyanka V

Respuestas:

369

Al leer este artículo , noté grandes beneficios al usar Guard

Aquí puede comparar el uso de guardia con un ejemplo:

Esta es la parte sin guardia:

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

    // Value requirements not met, do something
}
  1. Aquí está poniendo su código deseado dentro de todas las condiciones

    Es posible que no vea inmediatamente un problema con esto, pero podría imaginar cuán confuso podría llegar a ser si estuviera anidado con numerosas condiciones que todas debían cumplirse antes de ejecutar sus declaraciones

La forma de limpiar esto es hacer primero cada uno de sus controles y salir si no se cumple alguno. Esto permite comprender fácilmente qué condiciones harán que esta función salga.

Pero ahora podemos usar guardia y podemos ver que es posible resolver algunos problemas:

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. Verificando la condición que desea, no la que no desea. Esto nuevamente es similar a una afirmación. Si no se cumple la condición, se ejecuta la instrucción else de guard, que se sale de la función.
  2. Si la condición pasa, la variable opcional aquí se desenvuelve automáticamente para usted dentro del alcance que se llamó a la declaración de protección, en este caso, la función fooGuard (_ :).
  3. Está verificando si hay casos graves temprano, haciendo que su función sea más legible y más fácil de mantener

Este mismo patrón también es válido para valores no opcionales:

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 todavía tiene alguna pregunta, puede leer el artículo completo: Declaración de Swift Guard.

Terminando

Y finalmente, leyendo y probando, descubrí que si usas guardia para desenvolver cualquier opción,

esos valores sin envolver permanecen para que los use en el resto de su bloque de código

.

guard let unwrappedName = userName else {
    return
}

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

Aquí el valor sin envolver estaría disponible solo dentro del bloque 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
fuente
3
¡Hola @ Eric, hiciste una excelente publicación! ¡Gracias por hacer que sea tan fácil de entender!
Jorge Casariego
1
Estoy usando guardia para desenvolver NSError. Pero cuando trato de usarlo dentro del alcance de protección (por ejemplo, para pasar un error en alguna devolución de llamada) dice "La variable declarada en condición de protección no se puede utilizar en su cuerpo". ¿Tiene algún sentido? Gracias
GeRyCh
66
El desenvolvimiento de @GeRyCh en una declaración de protección hace que esa variable esté disponible para después de la declaración de protección, no dentro de ella. Me tomó un tiempo acostumbrarme a esto.
DonnaLea
2
Aquí hay otro excelente artículo sobre el uso de guardia para desenvolver limpiamente las opciones. Lo resume muy bien.
Doches
let x = x where x > 0¿Significa que ha acoplado otra condición en su enlace opcional? Quiero decir, es un poco diferente deif let constantName = someOptional { statements }
Miel
36

A diferencia if, guardcrea la variable a la que se puede acceder desde fuera de su bloque. Es útil desenvolver muchos Optionals.

Takebayashi
fuente
24

Realmente hay dos grandes beneficios para guard. Una es evitar la pirámide de la fatalidad, como han mencionado otros, muchas if letdeclaraciones molestas anidadas una dentro de la otra que se mueven cada vez más hacia la derecha.

El otro beneficio es a menudo que la lógica que desea implementar es más " if not let” que " if let { } else".

Aquí hay un ejemplo: suponga que desea implementar accumulate: un cruce entre mapy reducedonde le devuelve una serie de reducciones en ejecución . Aquí está con 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(+)  // []

¿Cómo lo escribirías sin protección, pero aún usando firsteso devuelve un opcional? Algo como esto:

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 []
        }
    }

}

El anidamiento adicional es molesto, pero tampoco es tan lógico tener el ify elsetan lejos. Es mucho más legible tener la salida anticipada para el caso vacío, y luego continuar con el resto de la función como si eso no fuera una posibilidad.

Velocidad de velocidad del aire
fuente
19

Cuando se cumple una condición usando guard, expone las variables declaradas dentro del guardbloque al resto del bloque de código, llevándolas a su alcance. Lo cual, como se dijo anteriormente, sin duda será útil con if letdeclaraciones anidadas .

Tenga en cuenta que el guardia requiere una devolución o un lanzamiento en su declaración else.

Analizando JSON con Guard

A continuación se muestra un ejemplo de cómo se puede analizar un objeto JSON usando guard en lugar de if-let. Este es un extracto de una entrada de blog que incluye un archivo de juegos que puedes encontrar aquí:

Cómo usar Guard en Swift 2 para analizar 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)

}

descargar juegos infantiles: juegos infantiles

Más información:

Aquí hay un extracto de la Guía del lenguaje de programación Swift:

Si se cumple la condición de la declaración de protección, la ejecución del código continúa después del cierre de la declaración de protección. Las variables o constantes a las que se les asignaron valores mediante un enlace opcional como parte de la condición están disponibles para el resto del bloque de código en el que aparece la declaración de protección.

Si no se cumple esa condición, se ejecuta el código dentro de la rama else. Esa rama debe transferir el control para salir del bloque de código en el que aparece esa declaración de protección. Puede hacer esto con una declaración de transferencia de control como return, break o continue, o puede llamar a una función o método que no devuelve, como como fatalError ().

Dan Beaulieu
fuente
7

Un beneficio es la eliminación de muchas if letdeclaraciones anidadas . Vea el video de WWDC "What's New in Swift" alrededor de las 15:30, la sección titulada "Pyramid of Doom".

zaph
fuente
6

Cuando usar guardias

Si tiene un controlador de vista con algunos elementos UITextField o algún otro tipo de entrada de usuario, notará inmediatamente que debe desenvolver el textField.text opcional para acceder al texto (¡si lo hay!). isEmpty no le hará ningún bien aquí, sin ninguna entrada, el campo de texto simplemente devolverá nil.

Entonces, tiene algunos de estos que desenvuelve y finalmente pasa a una función que los publica en un punto final del servidor. No queremos que el código del servidor tenga que tratar con valores nulos o enviar por error valores no válidos al servidor, por lo que primero desenvolveremos esos valores de entrada con guardia.

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) {
  ...
}

Notará que nuestra función de comunicación del servidor toma valores de cadena no opcionales como parámetros, por lo tanto, la protección se desenvuelve de antemano. El desenvolvimiento es un poco intuitivo porque estamos acostumbrados a desenvolver si se deja que desenvuelva los valores para usar dentro de un bloque. Aquí, la declaración de protección tiene un bloque asociado, pero en realidad es un bloque else, es decir, lo que se hace si falla el desenvolvimiento: los valores se desenvuelven directamente en el mismo contexto que la declaración en sí.

// separación de intereses

Sin guardia

Sin usar guardia, terminaríamos con una gran pila de código que se asemeja a una pirámide de fatalidad . Esto no se escala bien para agregar nuevos campos a nuestro formulario o crear un código muy legible. La sangría puede ser difícil de seguir, particularmente con tantas otras declaraciones en cada bifurcación.

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")
    }
}

Sí, incluso podríamos combinar todo esto si dejamos declaraciones en una sola declaración separada por comas, pero perderíamos la capacidad de descubrir qué declaración falló y presentar un mensaje al usuario.

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

Miel
fuente
5

Con el uso de guardia nuestra intención es clara. no queremos ejecutar el resto del código si esa condición particular no se cumple. aquí también podemos extender la cadena, consulte el siguiente código:

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

Declaración de guardia va a hacer. es un par de diferentes

1) me permite reducir la declaración anidada if
2) es aumentar mi alcance que mi variable accesible

si la declaración

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 
}

Declaración de guardia

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
fuente
3

De la documentación de Apple:

Declaración de guardia

Se utiliza una declaración de guardia para transferir el control del programa fuera de un alcance si no se cumplen una o más condiciones.

Synatx:

guard condition else {
    statements
}

Ventaja:

1. Al usar la guarddeclaración podemos deshacernos de condicionales profundamente anidados cuyo único propósito es validar un conjunto de requisitos.

2. Fue diseñado específicamente para salir temprano de un método o función.

si usa si se deja a continuación es el código cómo se ve

  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()

Con la protección, puede transferir el control fuera de alcance si no se cumplen una o más condiciones.

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()

Referencia:

1. Swift 2: Salga temprano con guardia 2. Udacity 3. Declaración de guardia

Ashok R
fuente
¿Pero puede hacer esto último con declaraciones if? if condition { return }¿hosco?
Oliver Dixon
2

Al igual que una declaración if, guard ejecuta declaraciones basadas en un valor booleano de una expresión. A diferencia de una declaración if, las declaraciones de guardia solo se ejecutan si no se cumplen las condiciones. Puede pensar en la guardia más como una Afirmación, pero en lugar de estrellarse, puede salir con gracia.

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

Zgpeace
fuente
1

Realmente hace que el flujo de una secuencia con varias búsquedas y opciones sea mucho más conciso y claro, y reduce mucho si se anida. Vea la publicación de Erica Sadun sobre el reemplazo de Ifs . .... Podría dejarse llevar, un ejemplo a continuación:

    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)}

A ver si eso se pega.

DavidS
fuente
1

En pocas palabras, proporciona una forma de validar los campos antes de la ejecución. Este es un buen estilo de programación, ya que mejora la legibilidad. En otros idiomas, puede verse así:

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

Pero debido a que Swift le ofrece opciones, no podemos verificar si es nulo y asignar su valor a una variable. Por el contrario, if letverifica que no sea nulo y asigna una variable para mantener el valor real. Aquí es donde guardentra en juego. Le brinda una forma más concisa de salir temprano utilizando opciones.

gunby
fuente
1

Fuente: Guardia en Swift

Veamos el ejemplo para entenderlo claramente

Ejemplo 1:

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

En el ejemplo anterior, vemos que 3 es mayor que 2 y se omite la declaración dentro de la cláusula guard else y se imprime True.

Ejemplo 2

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

En el ejemplo anterior, vemos que 1 es menor que 2 y la declaración dentro de la cláusula guard else se ejecuta y False se imprime seguido de return.

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: "")

En el ejemplo anterior, estamos usando guard let para desenvolver los opcionales. En la función getName hemos definido una variable de tipo string myName que es opcional. Luego usamos guard let para verificar si la variable myName es nula o no, si no se asigna a name y verificamos nuevamente, name no está vacío. Si ambas condiciones califican, es decir, verdadero, el bloque else se omitirá e imprimirá "Las condiciones se cumplen con el nombre".

Básicamente, estamos verificando dos cosas separadas por comas, primero desenvolviendo y opcionales, y verificando si eso satisface la condición o no.

Aquí no estamos pasando nada a la función, es decir, una cadena vacía y, por lo tanto, la condición es falsa se imprime.

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")

Aquí estamos pasando "Hola" a la función y puede ver que la salida se imprime "Condición se cumple Hola".

Aditya
fuente