Palabra clave de Swift’s guard

Swift 2 introdujo la palabra clave guard , que podría utilizarse para garantizar que varios datos estén configurados listos para funcionar. Un ejemplo que vi en este sitio web muestra una función submitTapped:

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

Me pregunto si usar guard es diferente de hacerlo a la antigua usanza, usando una condición if . ¿Le da beneficios, que no podría obtener con un simple cheque?

Leyendo este artículo noté grandes beneficios al utilizar Guard

Aquí puedes 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 el código deseado en todas las condiciones

    Es posible que no veas inmediatamente un problema con esto, pero podrías imaginar lo confuso que podría ser si estuviera nested con numerosas condiciones que todos debían cumplirse antes de ejecutar tus declaraciones.

La forma de limpiar esto es hacer primero cada uno de los controles y salir si no se cumplen. Esto permite una fácil comprensión de las condiciones que 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 quiere. De nuevo, esto es similar a una afirmación. Si la condición no se cumple, se ejecuta la instrucción else del guardián, que se rompe de la función.
  2. Si la condición pasa, la variable opcional aquí se desenrolla automáticamente dentro del scope al que se llamó la instrucción de guardia, en este caso, la función fooGuard (_ :).
  3. Está comprobando los casos graves al principio, haciendo que su función sea más legible y más fácil de mantener

Este mismo patrón también se aplica a los 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: statement de Swift Guard.

Terminando

Y finalmente, leyendo y probando, encontré que si usa guardia para desenvolver cualquier opción,

esos valores desenvueltos permanecen alrededor para que los use en el rest de su bloque de código

.

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

Aquí el valor desenvuelto 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)") 

A diferencia de if , guard crea la variable a la que se puede acceder desde fuera de su bloque. Es útil desenvolver un montón de s Optional .

En realidad, hay dos grandes beneficios que guard . Uno está evitando la pirámide de la perdición, como han mencionado otros, muchas molestas if let declaraciones dejadas anidadas una dentro de la otra se mueven más y 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: supongamos que desea implementar accumulate : un cruce entre el map y reduce donde 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 guardia, pero aún usando first que 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 [] } } } 

La anidación adicional es molesta, pero tampoco es tan lógico tener el if y el else tan separados. Es mucho más legible tener la salida anticipada para la caja vacía, y luego continuar con el rest de la función como si eso no fuera una posibilidad.

Cuando se cumple una condición usando guard , expone las variables declaradas dentro del bloque de guard al rest del bloque de código, poniéndolo dentro de su scope. Lo cual, como se dijo anteriormente, sin duda será útil con declaraciones anidadas if let .

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

Analizando JSON con Guardia

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 patio de recreo que puede 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 parque infantil: guardar el patio

Más información:

Aquí hay un extracto de The Swift Programming Language Guide:

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

Si no se cumple esa condición, se ejecuta el código dentro de la twig else. Esa twig debe transferir el control para salir del bloque de código en el que aparece esa instrucción de guardia. Puede hacer esto con una instrucción de transferencia de control, como devolver, interrumpir o continuar, o puede llamar a una función o método que no retorna, tales como fatalError ().

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

Con el uso de la guardia, nuestra intención es clara. no queremos ejecutar el rest del código si esa condición particular no está satisfecha. aquí también podemos extender la cadena, por favor revise el siguiente código:

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

Cuándo usar guardias

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

Así que 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 lidiar con valores nulos o enviar erróneamente valores no válidos al servidor, por lo que primero desenvolveremos esos valores de entrada con la protección.

 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ás que nuestra función de comunicación con el servidor toma valores de Cadena no opcionales como parámetros, por lo tanto, el protector se desenrolla de antemano. El desenvolvimiento es poco intuitivo porque estamos acostumbrados a desenvolver con if let, que desenvuelve valores para usar dentro de un bloque. Aquí el enunciado de guardia 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 el enunciado en sí.

// separación de intereses

Sin guardia

Sin usar guardia, terminaríamos con una gran stack de código que se asemeja a una pirámide de fatalidad . Esto no 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, especialmente con tantas otras afirmaciones 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 todos estos si las declaraciones de let en una única statement separada con comas, pero perderíamos la capacidad de averiguar qué sentencia falló y presentar un mensaje al usuario.

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

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

1) me permite reducir la statement if anidada
2) es boost mi scope que mi variable accesible

si la statement

 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 } 

De la documentación de Apple:

Declaración de guardia

Una statement de guardia se usa para transferir el control del progtwig fuera de un scope si no se cumplen una o más condiciones.

Synatx:

 guard condition else { statements } 

Ventaja:

1. Al usar la statement de guard podemos deshacernos de condicionales profundamente nesteds 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 lo dejamos abajo es el código como 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 un scope 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: Salir temprano con guardia 2. Udacity 3. Declaración de guardia

Realmente hace que el flujo de una secuencia con varias búsquedas y opciones sea mucho más conciso y claro, y reduce la cantidad de anidamientos. Ver 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 (" cannot go in that direction.", nil, nil)} guard filteredLinks.count == 1 else {return (" cannot decide which route to take.", nil, nil)} guard let nextLocation = foundLink.toLocation else {return (" cannot go in that direction.", nil, nil)} 

Ve si eso se pega.

Al igual que una sentencia if, guard ejecuta sentencias basadas en un valor booleano de una expresión. A diferencia de una instrucción if, las declaraciones de guardia solo se ejecutan si no se cumplen las condiciones. Puedes pensar en guardias más como un Assert, pero en lugar de chocar, puedes salir con gracia.

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

En pocas palabras, proporciona una forma de validar los campos antes de la ejecución. Este es un buen estilo de progtwigció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 proporciona opciones, no podemos verificar si es nula y asignarle su valor a una variable. Por el contrario, if let comprobar que no es nulo y asigna una variable para mantener el valor real. Aquí es donde entran en juego los guard . Le da una forma más concisa de salir temprano usando opcionales.