Categories
Swift Development

100 Días de Swift Día 12

Estas son mis notas para el curso https://www.hackingwithswift.com/100

Entrada anterior

“I call it my billion-dollar mistake ”

Tony Hoare (talking about the invention of optionals)

«Lo llamo mi error de un millón de dólares»

Tony Hoare (hablando de la invención de los opcionales )

Optionals

Null references cuando una variable no contiene nigua valor, pueden causar varios dolores de cabeza, Swift vida con ellas usando optionals.

Comencemos con un dato sencillo como la edad de una persona, un valor que podemos no conocer, así que podemos crear una variable de tipo Int vacía que contenga ninguna valor y al cual podemos asignarle uno después.

var edad: Int? = nil

Esta variable no guarda ningún valor pero podemos asignarle uno después

edad = 24

Unwrapping optionals

Ahora imaginemos que tenemos un variable de tipo String, una String norma tienen una propiedad count pero un variable vacía no.

var nombre: String? = nil

Por tanto Swift no nos permite usar esta propiedad en este tipo de variables o por lo menos no sin antes hacerles un unwrapping

 if let unwrapped = nombre {
     print("tiene \(unwrapped.count)  letras")
 } else {
     print("No hay un nombre.")
 }

Lo norma es usar la sintaxis if let que hace el unwrapping usando una condición.

Si nombre contienen un string ejecuta el código entre corchetes si no la condición falla y ejecuta el otro código.

Unwrapping with guard

De forma alternativa al unwrapping con if let podemos usar guard let, que espera que al encontrar un nil seas tu quién termine la función, loop o condición.

La mayor diferencia con if let es que tu valor unwrapped permanece usable después del código guard

A continuación un ejemplo

 var nombre: String? = nil

 func saludo(_ nombre: String?) {
     guard let unwrapped = nombre else {
         print("¡No suministraste un nombre!")
         return
     }

     print("Hola, ¡\(unwrapped)!")
 }

 saludo(nombre)

usar guard let nos permite lidiar con los problemas al inicio de nuestro código y lo que queda es código feliz.

Force unwrapping

En ocaciones estamos seguros que un valor opcional no es nil, por ejemplo si queremos convertir un String a Int

let str = "5"
let num = Int(str)

Esto convierte num a un Int opcional pues swift no sabe que el valor que tratas dé convertir es un “5” o un “cinco” . Pero nosotros sí lo sabemos por lo que es seguro forzar el unwrapping usando !

let num = Int(str)!

Como resultado swift convertirá tu opcional en un Int regular pero cuidada si tu valor contienen un nil tu código fallara y tu app se detendrá. Así que ten cuidado de solo forzar el unwrapping cuando estes seguro de que el valor no sera nil.

Implicitly unwrapped optionals

Los opcionales implícitos son similares a los opcionales normales, pero ya están unwrapped, los Implicitly unwrapped optionals son creados al añadir un signo de admiración después del tipo de valor.

let edad: Int! = nil

Este tipo de opcionales también harán fallar tu condigo si contienen un nil así que solo deben ser usados cuando necesitas una variable y luego estas seguro que le asignaras un valor antes de usarla.

Nil coalescing

El operador Nil coalescing permite hacer el unwrapping y si el valor es nil puedes proporcionar un valor por default el operador se represan con dos signos de interrogación ?? después de nuestra variable opcional y después debemos proporcionar un valor.

//Esta función regresa un String opcional 
func nombreDeUsuario(for id: Int) -> String? {
     if id == 1 {
         return "Taylor Swift"
     } else {
         return nil
     }
 }
  
//Aqui usamos el nil coalescing operador 
let usuario = nombreDeUsuario(for: 15) ?? "Anonymoux"

En el ejemplo de arriba, se usa el nil coalescing operator si el valor que nos regresa la función no es nil ese es el que se usa, pero si es nil se usa lo que esta después de operador

Optional chaining

Swift nos proveer un atajo cuando usamos opcionales, si quieres acceder a algo como a.b.c y b es opcional, puedes usar un ? para activar el optional chaining.

Cuando el código corre swift checara si b tiene un valor, y si es nil el resto de la línea sera ignorado y swift regresara nil, pero si tienen un valor sera unwrapped y seguirá ejecutando el código.

let nombres = ["John", "Paul", "George", "Ringo"]

let beatle = nombres.first?.uppercased()

Optional try

En dias pasados vimos como manejar errores en una función, cuando se suministra un dato erróneo

Writing throwing functions
enum errorDeContraseña: Error {
    case muyObvia
}

func revisarContraseña(_ contraseña: String) throws -> Bool {
    if contraseña == "contraseña" {
        throw errorDeContraseña.muyObvia
    }

    return true
}

do {
     try revisarContraseña("contraseña")
     print("¡Buena contraseña!")
 } catch {
     print("¿En serio 😐?")
 }

Para correr una throwing function usamos do, try y catch

Hay dos alternativas para try la primera es try? que cambia la throwing function por una función que regresa un nil como resultado


if let resultado = try? revisarContraseña("contraseña") {
    print("El resultado fue  \(resultado)")
} else {
   print("¿En serio 😐?")
}

La otra alternativa es try! que solo debes usar en caso que estes seguro que la función no fallara, pues si lo hace tu código fallara y se detendrá (crash)

try! revisarContraseña("Secreto")
print("ok")

Failable initializers

Cuando vimos force unwrapping convertimos un string a un integer opcional

let str = "5"
let num = Int(str)

Esto es se conoce como un failable initializer un initializer que podría o no funcionar.

Puedes escribir los propios usando init?() en lugar de init y responder nil si las cosas no van bien.

como en el siguiente ejemplo

struct Persona {
    var id: String

    init?(id: String) {
        if id.count == 9 {
            self.id = id
        } else {
            return nil
        }
    }
}

Typecasting

Swift siempre debe saber el tipo de valores de tus variables, pero a veces tenemos mas información que swift, tomemos el siguiente ejemplo.

class Animal { }
class Pez: Animal { }

class Perro: Animal {
    func hacerRuido() {
        print("¡Guau!")
    }
}
let mascotas = [Pez(), Perro(), Pez(), Perro()]

Swift puede ver que tanto Perro como Pez heredan de Animal así que infiere que el array es de Animal

Si queremos un loop sobre mascotas y le pida a los perros que hagan ruido, swift puede revisar que cada elemento de mascotas sea o no perro y entonces llamar a hacerRuido

Paro eso usamos as? lo cual responde con un opcional, nil si no es de la clase esperada.

for mascota in mascotas {
    if let perro = mascota as? Perro {
        perro.hacerRuido()
    }
}

Resumen

Los opciones nos dejan presentar la ausencia de un n valor de una forma limpia y no ambigua

Swift no nos deja usar opcionales sin hacerles primero un unwrapping, usando if let o guard let.

Puedes forzar un unwrapping con !, pero recuerda que si tu valor es nil tu código fallara y se detendrá.

Implicitly unwrapped optionals no requieren que hagas un unwrapping pero si no te aseguras de asignarles un valor tu código fallara y se detendrá.

Puedes usar nil coalescing (??) para hacer unwrap y proveer un valor por default.

Optional chaining es usado para manipular un opcional, si el opcional resulta ser nil la linea de código es solo ignorado.

Puedes usar try? para convertir una throwing function en un valor opcional, o usar try! para detener tu código si hay un error.

Puedes usar init?() para regresar nil si recibes un dato incorrecto.

Puedes usar typecasting (as?) para convertir un tipo de valor en otro.

Referencias

https://www.hackingwithswift.com/100/12

https://www.hackingwithswift.com/100

Categories
Swift Development

100 Días de Swift Día 11

Estas son mis notas para el curso https://www.hackingwithswift.com/100

Entrada anterior

“Inside every large program, there is a small program trying to get out.”

Tony Hoare

«Dentro de todo gran programa, hay un pequeño programa tratado de salir»

Tony Hoare

Protocols

Los protocolos son una forma de describir que propiedades y métodos algo debe tener

Por ejemplo quizás queramos crear una función que acepte algo con un ID y no nos interesa precisamente que tipo de dato se este usando.

Comenzamos creando nuestro propio protocolo, en el definimos que requerirá que los tipos que se adapten al protocolo tengan una variable ID que pueda ser escrita y leída («get» and «set» )

protocol Identificable {
    var id: String { get set }
}

No podemos crear instancias de este protocolo directamente pues su objetivo es describir simplemente los que queremos.

Por ejemplo podemos usarlo en un struct

struct Usuario: Identificable {
    var id: String
}

Esta struct tendrá que tener un id tipo string si quiere conformarse al protocolo que definimos.

De la misma forma podemos crear una función

func mostrarID (loQueSea : Identificable ){
    print("El id es \(loQueSea.id)")
}

Protocol inheritance

Al igual que las clases los protocolos pueden usar herencia, pero con la diferencia de que puedes heredar de multiples protocolos al mismo tiempo y luego poner tu propio protocolo sobre todos ellos.

Comencemos creando tres protocolos nuevos.

protocol SeLePuedePagar {
    func calcularSueldo() -> Int
}

protocol NecesitaCapacitacion {
    func capacitar()
}

protocol TieneVacaciones {
    func tomarVacaciones(dias: Int)
}

Ahora lo mas genial, podemos combinar estos tres protocolos en uno solo

protocol Empleado: SeLePuedePagar, NecesitaCapacitacion, TieneVacaciones {}

Ahora en lugar de hacer que nuestros datos se conformen a cada uno de estos tres protocolos, podemos hacer que se conformen solo al protocolo Empleado

Extensions

Las extensiones hacen precisamente lo que dicen sus nombres, nos permiten añadir funciones o métodos a tipos de datos ya existentes, por ejemplo podemos añadir un método para obtener el cuadrado de un Int

extension Int {
    func alCuadrado() -> Int {
        return self * self
    }
}

var numero =  1
numero.alCuadrado()

Swift no nos permite añadir propiedades almacenadas (stored properties) a una extensión pero si propiedades calculadas (computed properties) por ejemplo

extension Int {
    var esPar: Bool {
        return self % 2 == 0
    }
}

Protocol extensions

Los protocolos nos permiten describir que métodos algo debe tener, pero no provee el código dentro.

Las extensiones nos dejan tener nuestros propio código y métodos pero lo aplican solo a un tipo de datos.

Protocol extensions resuelven ambos problemas, son similares a las extensiones nórmales pero en lugar de aplicarse a un tipo de dato como Int se extienden a todo un protocolo así que todos los datos que se conformen a ese protocolo lo obtienen.

En este ejemplo tenemos un Array y un Set con algunos nombres

let pythons = ["Eric", "Graham", "John", "Michael", "Terry", "Terry"]
let beatles = Set(["John", "Paul", "George", "Ringo"])

Ambos tipos de datos se conforman con el protocolo Collection, así que podemos crear una extensión para ese protocolo.


let pythons = ["Eric", "Graham", "John", "Michael", "Terry", "Terry"]
let beatles = Set(["John", "Paul", "George", "Ringo"])

extension Collection {
    func recapitular() {
        print("Son \(count) de nosotros:")

        for name in self {
            print(name)
        }
    }
}

El nuevo método que hemos creado se añadió a todos los tipos de datos con ese protocolo.

pythons.recapitular()
beatles.recapitular()

Protocol-oriented programming

Protocol extensions nos permite añadir métodos por default a nuestros protocolos, eso hace mucho mas fácil que los tipos de datos se conformen a nuestros protocolos

Por ejemplo si creamos un protocolo llamado Identificable que requiere que se tenga un ID y un método llamado identificarse()

protocol Identificable {
    var id: String { get set }
    func identificarse()
}

En este caos todas los tipos de datos que quieran conformarse al protocolo Identificable necesitan implementar su propio método identificarse()

¿Pero qué tal si nosotros los proporcionamos por default con un Protocol extension?

extension Identificable {
    func identificarse() {
        print("Mi ID es \(id).")
    }
}

Ahora podemos crear una Struct que se conforme a nuestro protocolo, y que ademas de manera gratuita obtenga una implementación de identificarse()

struct Agente: Identificable {
    var id: String
}

let james = Agente(id: "007")
james.identificarse()

Resumen

Los protocolos describen que métodos y propiedades debe tener un tipo de dato que quiere conformarse a ellos. Pero no proveen la implementación de esos métodos.

Puedes construir protocolos sobre otros protocolos, igual que las clases.

Las extensiones nos permiten añadir métodos y propiedades computadas a tipos de datos ya existentes

Las extensiones de protocolos nos permiten añadir métodos y propiedades computadas a protocolos ya existentes.

Protocol-oriented programming consisten en basar la arquitectura de tu app en una serie de protocolos , y luego usar una serie de protocolos para proveer implementaciones de métodos por default.

//y ese fue el resumen de protocolos, sin duda una tema fascinante que te ayudará a reducir la cantidad de código que reescribes, ademas de proveer un nuevo tipo de paradigma de programación. 

Referencias

https://www.hackingwithswift.com/100/11

https://www.hackingwithswift.com/100

https://developer.apple.com/videos/play/wwdc2015/408/

Proximo dia

Categories
Articulo personal Swift Development

100 Días de Swift Día 10

Estas son mis notas para el curso https://www.hackingwithswift.com/100

Entrada anterior

“Any fool can write code that a computer can understand, but good programmers write code that humans can understand.”

Martin Fowler

«Cualquier tonto puede escribir código que una computadora pueda entender, pero buenos programadores pueden escribir código qué humanos pueden entender.»

Martin Fowler

Classes

Las clases son similares a las structs, te permiten nuevos tipos de datos, con propiedades y métodos.

Las clases tienen por lo menos cinco diferencias principales con las Clases

Creating your own classes

La primera es que las clases no vienen un un inicializador por default, siempre debes crearlo tú.

class Perro {
    var nombre: String
    var raza: String

    init(nombre: String, raza: String) {
        self.nombre = nombre
        self.raza = raza
    }
}

Crear instancias de una clase es igual que crear instancias de una estructura.

let cachorrito = perro(nombre: "Toby", raza: "Poodle")

Class inheritance

La segunda diferencia es que puedes crear una clase partir de otra clase ya existente, esto hereda todas las propiedades y métodos de la clase padre.

Por ejemplo creemos una clase en base a nuestra clase anterior Perro

class Sabueso: Perro {

}

Esta clase nueva hereda el inicializador y los parámetros de su clase padre

Aun así podemos crear un inicializador propio de Sabueso por ejemplo ya que sabemos que nuestro perro es un sabueso podemos iniciar nuestra clase sabueso solo con el nombre.

class Sabueso: Perro {
    
    init(nombre: String) {
        super.init(nombre: nombre, raza: "Sabueso")
    } 
}

Por razones de seguridad, swift siempre te pedirá llamar al «super.init» al crear una clase hija

let cazador = Sabueso(nombre: "Maylo")

Overriding methods

Las clases hijas pueden remplazar los métodos de sus padres con implementaciones propias.

Este proceso se conoce como «overriding»

Añadamos un método a nuestra clase Perro

class Perro {
    var nombre: String
    var raza: String

    init(nombre: String, raza: String) {
        self.nombre = nombre
        self.raza = raza
    }

 
}

Ahora podemos llamar este método desde cualquier instancia de su clase hija

cazador.ladrar()

Ahora podemos remplazar el método en nuestra clase hija para ponerle nuestra propia implementación.

Para eso usamos la palabra reservada «override» esto sire para evitar remplazar métodos por accidente.

 class Sabueso: Perro {
     
     init(nombre: String) {
         super.init(nombre: nombre, raza: "Sabueso")
     }

    override func ladrar() {
        print("¡Woolf! ¡Woolf! ¡Woolf!")
    }
 }
 
 let cazador = Sabueso(nombre: "Maylo")
cazador.ladrar()
// esto imprime "¡Woolf! ¡Woolf! ¡Woolf!"

Final classes

Sin bien la herencia es algo genial, no es algo que siempre queramos, por ejemplo si queremos marcar nuestras clases como finales y evitar que otros desarrolladores remplacen nuestros métodos solo debemos marcar la clase como final.

 final class Perro {
     var nombre: String
     var raza: String

     init(nombre: String, raza: String) {
         self.nombre = nombre
         self.raza = raza
     }
     
     func ladrar (){
         print("¡Guau! ¡Guau! ¡Guau!")
     }
 }
Cómo vemos eso impide la herencia.

Copying objects

La tercera diferencia entre clases y estructuras es lo que ocurre al copiarla

Al copiar una estructura, tanto la original como la copia son instancias diferentes, cambiar una no afecta la otra.

Cuando copias una clase, tanto la original como la copia son la misma, cambiar algo en una afecta a la otra.

let cachorrito = Perro(nombre: "Toby", raza: "Poodle")

let copiaDeCachorrito = cachorrito

copiaDeCachorrito.nombre = "Firulais"

print(cachorrito.nombre)

Al correr este código veras que al editar la copia de cachorrito también afecta al original. Esto ocurre por que ambas instancias apuntan al mismo espacio en al memoria

Deinitializers

La curta diferencia entre clases y estructuras es que las clases pueden tener «Deinitializers» código que se ejecuta cuando una instancia de la clase es destruida.

Para crear un Deinitializer usamos la palabra reservada «deinit»

class Chihuahua: Perro {
    init() {
        
        super.init(nombre: "Panchito", raza: "Chihuahua")
        print("¡Ahy ahy \(nombre) esta vivo!")
    }
    
    deinit {
        print("¡Ahy ahy 🤧😭 \(nombre) ya se murio!")
    }
}
 
for _ in 1...3 {
    let perro = Chihuahua()
}
Eso fue divertido

Mutability

La quinta y ultima diferencia entre clases y estructuras es la manera en como lidian con las constantes

En una estructura si tenemos una instancia que es una constante, y dentro de ella variables, la variable no puede ser cambiada pues toda la estructura es una constante.

Sin embargo en una clase si tenemos una instancia que es una constante y dentro tenemos una variable esta si que puede ser cambiada, por eso clases no necesitan mutating en sus métodos que cambian propiedades.

let perrito = Perro(nombre: "Toby", raza: "Callejero")
perrito.nombre = "Clifor"

print(perrito.nombre)

Si queremos evitar que esto pase debemos definir nuestra parámetro como una constante.

class Perro {
    //ahora ya no podrá ser cambiado
     let nombre: String
     var raza: String

     init(nombre: String, raza: String) {
         self.nombre = nombre
         self.raza = raza
     }
     
     func ladrar (){
         print("¡Guau! ¡Guau! ¡Guau!")
     }
  
 }

Resumen

Las clases y las estructuras son similares, ambas pueden crear tipos de datos personalizados, con sus propias propiedades y métodos

Las clases pueden heredar propiedades y métodos a otras, mediante un sistema de jerarquía llamado herencia en el cual creas clases a partir de otras

Puedes evitar la herencia marcando tu clase como final

El remplazo de métodos en una clase hija permite crear un remplazo del método de la clase padre con su propia implementación esto en POO se conoce como polimorfismo.

Cuando dos variables (o instancias) apuntan a la misma clase ambos comparten el mismo espacio de memoria, modificar una modifica a la otra.

Las clases tienen desinicializadores que es código que corre cuando una instancia de la clase es destruida.

Las clases aunque sean constantes pueden ser modificadas si alguna de sus propiedades es una variable.

//y bueno esas fueron mis notas para clases sin duda un tema super interesante y muy relacionado con POO

Referencias

https://www.hackingwithswift.com

https://www.hackingwithswift.com/100/10

Proximo día

Categories
Swift Development

100 Días de Swift Día 9

Estas son mis notas para el curso https://www.hackingwithswift.com

Entrada anterior

“I choose a lazy person to do a hard job, Because a lazy person will find an easy way to do it.”

Bill Gates

«Escojo a una persona floja para hacer un trabajo difícil, por que una persona floja buscara la forma mas fácil de hacerlo»

Bill Gates

Structs, part two

Referring to the current instance

Cuando creas un metodo con parámetros que tienen el mismo nombre que los parámetros de tu Struc puedes usar la constante self para distinguir entre uno y otro

struct Usuario {
    var nombre: String
    
    init(nombre: String) {
        self.nombre = nombre
    }
}

var usuario = Usuario(nombre: "Juan")
print(usuario.nombre)

Lazy properties

Como una optimización de recursos, Swift deja crear «Lazy vars» que son variables que no se crean hasta que son llamadas por primera vez, por ejemplo en teoría crear una familia debería tomar algo de tiempo para una persona


struct Familia {
    init() {
        print("Esto deberia tomar un par de años por lo menos ")
    }
}


struct Usuario {
    var nombre: String
    lazy var familia = Familia()
    init(nombre: String) {
        self.nombre = nombre
    }
}


var usuario = Usuario(nombre: "Juan")
print(usuario.nombre)

Si colocamos la palabra lazy antes de nuestra var la variable no se creara hasta que esta sea llamada por primera vez.

Algunos de nuestros usuarios quizás ni la usen.

Como ves nuestra variable no es creada hasta la llamamos por primera vez

Static properties and methods

Todas las propiedades y métodos que creamos pertenecen a las instancias individuales de cada estructura, pero si marcamos una variable como estática, esta pertenecía a la estructura y no a su instancia, esto quiere decir que sera compartida entre todas las instancias que creemos.

struct Estudiantes {
    var nombre : String
    static var tamañoDelaClase = 0
    
    init(nombre: String) {
        self.nombre = nombre
        Estudiantes.tamañoDelaClase +=  1
    }
}


let mario = Estudiantes(nombre: "Mario")
let juan = Estudiantes(nombre: "Juan")
print(Estudiantes.tamañoDelaClase)

Puedes ver que para acceder a esta variable entramos directo en Estudiantes en lugar de usar self o el nombre de la instancia.

Access control

El control de acceso te deja restringir que código pueden usar propiedades y métodos, esto es importante por que nos sirve para evitar que la gente lea propiedades de forma directa.

Algo muy importante al realizar la Abstracción en POO

Para proteger una variable solo tenemos que añadir la palabra clave «private»

Otra palabra clave es «public» que hace sorpresa sorpresa lo opuesto.

Resumen

Puedes crear tus propios tipos de datos usando Structures, las cuales pueden tener sus propias propiedades y métodos

Puedes usar stored properties o usar computed properties para calcular valores al momento

Los Initializers son métodos especiales para crear structs, las structs tienen uno por default, pero podemos crear nuestro propio inicializador personalizado. Solo debemos asegurarnos todos los parámetros tenga un valor antes de que termine.

Usamos la constate self. para referirnos a la instancia actual de nuestra struct dentro de un método.

Usamos Lazy para crear una propiedad hasta que esta es llamada por primera vez

Puedes compartir propiedades y métodos entre instancias a usar la palabra reservada Static

Access control te deja restringir que código puede usar propiedades y métodos.

//y esas fueron las structs, un concepto muy importe en Swift 

Referencias

https://www.hackingwithswift.com

https://www.hackingwithswift.com/100/9

Categories
Swift Development

100 Dias de Swift Día 8

Estas son mis notas para el curso de Swift https://www.hackingwithswift.com/100/8

Entrada anterior

“The programmer at wit’s end… can often do best by disentangling themself from their code, rearing back, and contemplating their data. Representation is the essence of programming.”

Fred Brooks

«El programador al final, hace mejor al distanciarse del código, leerlo otra vez, y contemplar su contenido. Representar es la esencia de programar.»

Fred Brooks

Structs part one

Creating your own structs

Las estructuras o «Structs» nos permiten crear nuestros propios tipos de datos, las structs pueden tener sus propias variables y funciones


struct Deporte {
    var nombre: String
}

var juegoConPelota = Deporte(nombre: "Baloncesto")
print(juegoConPelota)

ahora dado que tanto nuestro struct como su propiedad nombre son variables podemos modificarlas como cualquier variable

juegoConPelota.nombre = "Tenis"

print(juegoConPelota.nombre)

Las propiedades de una struct pueden tener valores por default y también funciona con la inferencia de tipo de valor.

Computed properties

Ahora que tenemos nuestra sctruct podemos añadir mas propiedades por ejemplo una propiedad calculada o «Computed propertie» que es básicamente lo que dice su nombre una propiedad que corre un pedazo de código para calcular su valor.

Aquí un ejemplo, añadimos dos propiedades mas a nuestra struct una un booleano y la otra una propiedad calculada.

struct Deporte {
    var nombre: String
    var esOlimpico: Bool
    var estatusOlimpico : String {
        if esOlimpico {
            return "\(nombre) es un deporte olimpico"
        } else {
            return "\(nombre) no es un deporte olimpico"
        }
    }
}

var juegoConPelota = Deporte(nombre: "Damas Chinas", esOlimpico: false)
print(juegoConPelota.estatusOlimpico )

Como vez las propiedades calculadas nos dan infinitas posibilidades.

Property observers

Podemos añadir observadores a nuestras variables para correr código antes o después de que cambien sus propiedades


struct Tarea {
    var nombre: String
    var avance: Int {
        didSet {
            print("\(nombre) esta ahora \(avance)% completada")
        }
    }
}

var lavarRopa = Tarea(nombre: "Lavar la ropa", avance: 10)

lavarRopa.avance = 30
lavarRopa.avance = 60
lavarRopa.avance = 100

También existe «willSet» que ejecuta código cuando el valor cambia, pero casi nadie lo usa.

Methods

Las structs pueden tener métodos dentro de ellas, y esas funciones pueden usar las propiedades de la struct si lo necesitan.

Las funciones dentro de una struct se llaman métodos o «Methods» pero aun usan la palabra reservada func

struct Ciudad {
    var poblacion: Int

    func recolectarImpuestos() -> Int {
        return poblacion * 1000
    }
}

let CDMX = Ciudad(poblacion: 1_000_000)
CDMX.recolectarImpuestos() 

Como el métodos pertenece a la estructura tenemos que llamarlo a partir de ella.

Mutating methods

Si una estructura tiene una variable, pero la instancia de la estructura fue creada como una constante esa propiedad no se puede cambiar, sin importar si tu propiedad es una variable.

El problema es que al crear una struct, swift no sabe si sera una variable o una constante, por eso swift no te deja crear métodos que modifiquen propiedades a menos que lo especifiques

Si quieres cambiar una propiedad debes usar «mutating» antes de func

struct Ciudad {
    var poblacion: Int

    func recolectarImpuestos() -> Int {
        return poblacion * 1000
    }
    
    mutating func cambiarPoblacion (nuevaPoblacion: Int) {
        poblacion = nuevaPoblacion
    }
}

var CDMX = Ciudad(poblacion: 1_000_000)
CDMX.cambiarPoblacion(nuevaPoblacion: 2_000_000)
print(CDMX.poblacion)

Swift solo dejara usar este método en instancias que sean variables.

Properties and methods of strings

Resulta que todo este tiempo que hemos estado usando Strings están son structs 🤯

Comencemos creando una nueva string

let yodaDice = "Do or do not, there is no try."

ahora podemos acceder a una de sus propiedad como count para saber cuantos caracteres tiene nuestra string

print(yodaDice.count)

Strings también tienen un método llamada «hasPrefix» que


print(yodaDice.hasPrefix("Do"))

Que regresa falso o verdadero si nuestra string inicia con esos caracteres.

Podemos también poner nuestra string en mayúsculas usando el método «uppercased()»

print(yodaDice.uppercased())

Hay muchos métodos mas que string tiene y que podemos descubrir con la auto compleción deXcode al escribir «.»

Properties and methods of arrays

Resulta que los arrays son también structs 😲 y tienen sus métodos también.

Aqui un par de ejemplos

//Array de ejemplo
var toys = ["Woody"]

//Contar elementos en el array 
print(toys.count)

//Añadir nuevo elemento a Array
toys.append("Buzz")

//Reordenar elementos del array alfabéticamente 
toys.firstIndex(of: "Buzz")
print(toys.sorted())

Initializers

Los Inicializadores son métodos especiales para te proveen diferentes maneras de crear tus structs, las structs vienen con un «Initializer» por defecto, que te pide que un valor por cada propiedad de tu struct

struct Usuario {
     var nombre: String
 }
var usuario = Usuario(nombre: "Juanito")

Podemos remplaza el incializador por default de la siguiente manera

struct Usuario {
     var nombre: String
    init() {
        nombre = "Anonimo"
        print("Usuario anonimux creado")
    }
 }

var usuario = Usuario()
usuario.nombre = "Juan"

El initializer no requiere que escribas func al principio pero debes asegurarte que todas las propiedades tengan un valor antes de que termine.


//y bueno esa fue la primera parte de Structs mañana seguimos con la segunda parte. 

Proximo día

Referencias

https://www.hackingwithswift.com/100/8

https://www.hackingwithswift.com/100

Categories
Swift Development

100 Dias de Swift Día 7

Estas son mis notas para el curso de swift https://www.hackingwithswift.com/100

Entrada anterior

“Energy and persistence conquer all things”

Benjamin Franklin 

«Energía y tenacidad, conquistan todas las cosas»

Benjamin Franklin

Closures, part two

Esta es la segunda parte del tema de closures y aquí es donde las cosas se vuelven mas complejas.

Using closures as parameters when they accept parameters

Anteriormente usamos «() -> Void» para pasar un closure que no acepta parámetros y nos regresa nada, pero ahora especificaremos un tipo de valor dentro de los paréntesis

func viajar(acción: (String) -> Void) {
     print("Estoy listo para partir.")
     acción("CDMX")
     print("¡Ya llegue!")
 }

viajar { (lugar: String) in
    print("voy para \(lugar) en mi auto")
}

Recuerda los nombres de los parámetros en los closures se indican dentro de las llaves

Using closures as parameters when they return values

De la misma manera podemos remplazar Void por un valor para que nuestra Closure regrese y luego usarlo dentro de la función.

func viajar(acción: (String) -> String) {
     print("Estoy listo para partir.")
     let adondeVas = acción("CDMX")
     print(adondeVas)
     print("¡Ya llegue!")
 }

viajar { (lugar: String) -> String in
    return "voy para \(lugar) en mi auto"
}

Shorthand parameter names

Hemos creado una función que acepta un closure como parámetro que a su vez acepta una String parámetro y regresa una String

Como Swift sabe que nuestro parámetro es una String y que estamos regresando una String podemos simplificar nuestra expresión de esta forma

//Sin especificar el tipo de parametro
viajar { lugar -> String in
    return "voy para \(lugar) en mi auto"
}

//Sin especificar el tipo de valor que retorna
viajar { lugar  in
    return "voy para \(lugar) en mi auto"
}

Closures with multiple parameters

También es posible utilizar Closures con multiples parámetros

func viajar(acción: (String, Int) -> String) {
     print("Estoy listo para partir.")
     let adondeVas = acción("CDMX", 60)
     print(adondeVas)
     print("¡Ya llegue!")
 } 

viajar { (lugar, km)  in
    return "voy para \(lugar) a \(km)km/hora en mi auto"
}

//version simplificada
viajar {
    return "voy para \($0) a \($1)km/hora en mi auto"
}
 

Como puedes ver en el segundo ejemplo es incluso posible simplificar aun mas la expresión usando nombres de parámetros acortados «$0» y «$1» aun así muchas personas evitan usarlos pues pueden dificultar la legibilidad de tu código.

Returning closures from functions

De la misma forma que podemos pasar closures como parámetros en una función, también podemos devolverlas como respuesta de una función.

La sintaxis aquí es un poco confusa por que usamos dos veces «->»

En el siguiente ejemplo creamos la función viajar que no acepta parámetros pero regresa un closure, este closure acepta una string y regresa nada como respuesta.

func viajar() -> (String) -> Void {
    return {
        print("Voy a \($0)")
    }
}
//función sin parámetros devuelve como respuesta un closure que acepta una string y devuelve como respuesta nada 

Ahora podemos llamar a viajar para obtener esa closure y luego usarla

let resultado = viajar()
resultado("Guadalajara")

Ahora técnicamente podemos llamar directamente a la closure pero la verdad no es muy recomendado


let resultado = viajar()("Guadalajara")

Capturing values

Si usas un valor externo dentro de tu closure, swift lo captura, esto quiere decir que lo guarda junto tu closure.

Modifiquemos nuestra closure en viajar para contar cuantas veces es llamada.

func viajar() -> (String) -> Void {
    var contador = 1
    return {
        print("Por \(contador)vez  Voy a \($0)")
        contador += 1
    }
}

Ahora cuando llamemos este closure podremos ver que el valor del contador es conservado

let resultado = viajar()
 
resultado("Guadalajara")
resultado("Guadalajara")
resultado("Guadalajara")
¡Genial! ¿no?

Resumen

Puedes asignar closures a variables y luego llamarlas

Los closures pueden aceptar parámetros y responder con valores, como una función normal

Puedes pasar closures como parámetros en una función, estas closures pueden tener sus propios parámetros y regresar respuestas con sus propios valores

Si el ultimo parámetro de una fungino es una closure, puedes usar una sintaxis reducida

Swift provee de manera automática nombres cortos para los parámetros de tu closure como $0 y $1

Sí usas valores externos en una closure, estos son capturados y guardados. para que puedas hacer referencia a ellos después

//y así terminamos con closures un tema bastante complejo pero que es usado mucho en SwiftUI veremos que tal en UIKIT

Referencias

https://www.hackingwithswift.com/100/7

https://www.hackingwithswift.com/100

Proximo día

Categories
Swift Development

100 Dias de Swift Día 6

Día 6

Estas son mis notas para el curso de Swift https://www.hackingwithswift.com

Entrada anterior

“you can’t expect to hit the jackpot if you don’t put a few nickels in the machine.”

Flip Wilson’s law

«No esperes darle al premio gordo, sin antes poner un par de monedas en la maquina»

Flip Wilson’s law

Closures, part one

Este tema es la principal razón por la que decidí volver a a retomar las lecciones básicas de Swift este tema se me había complicado bastante y no lo entendí al 100% la primera vez

Creating basic closures

Swift nos deja crear funciones como si fueran cualquier otro tipo de dato como Strings o Int, asignarla a una variable e incluso pasar esta función a otra función como parámetro.

Comencemos un simple ejemplo

let manejar = {
    print("Estoy manejando un auto")
}

Esto crea una función sin nombre que ahora podemos llamar a esa función con el nombre de la constante que creamos

manejar()

Accepting parameters in a closure

Aceptar parámetros en un «closure» es posible pero de forma un poco diferente al de una función , los parámetros se listan dentro de las llaves, dentro de paréntesis y seguidos de la palabra reservada «in»

let manejar = { (destino: String) in 
    print("Estoy manejando a \(destino) en un auto")
}

Una de las diferencias entre closures y functions es que closures no necesitan etiquetas para los parámetros

Returning values from a closure

Closures también pueden regresar valores, de manera similar a los parámetros, solo debemos indicarlos antes del «in» y usar «return» como en una función normal

 let manejar = { (destino: String) -> String in
     return "Estoy manejando a \(destino) en un auto"
 }
 
let mensaje = manejar("Pachuca")
print(mensaje)

Closures as parameters

Los closures pueden ser usados como parámetros dentro de una función, pero la sintaxis puede ser complicada

let manejar = {
    print("Estoy manejando un auto")
}

Si queremos pasar ese closure en una función debemos especificar el parámetro como «( ) -> Void». Eso significa que que no acepta parámetros y no regresa nada.

 let manejar = {
     print("Estoy manejando en un auto")
 }
 
 func viajar(acción: () -> Void) {
     print("Estoy listo para partir.")
     acción()
     print("¡Ya llegue!")
 }

 viajar(acción: manejar)

Trailing closure syntax

Si el ultimo parámetro de tu función es un closures Swift te permite pasarlo de manera especial esto es llamado trailing closure syntax. En lugar de pasar tu closure como un parámetro, lo pasas directo después de la función entre llaves.

 func viajar(acción: () -> Void) {
     print("Estoy listo para partir.")
     acción()
     print("¡Ya llegue!")
 }
 
 viajar(){
    print("Estoy viajando en un Barco")
 }

De hecho como esta función no tiene ningún parámetro podemos omitir los paréntesis

 viajar{
    print("Estoy viajando en un Avión")
 }

// Aquí termina la parte 1 mañana continuare la parte 2 y  hare el resumen de ambas 

Referencias

https://www.hackingwithswift.com/100

https://www.hackingwithswift.com/100/6

Proximo día

Categories
Swift Development

100 Dias de Swift Día 5

Día 5

Estas son mis notas para el curso de Swift https://www.hackingwithswift.com

Entrada anterior

“Dennis Ritchie (the creator of the C programming language) encouraged modularity by telling all and sundry that function calls were really, really cheap in C. Everybody started writing small functions and modularizing. Years later we found out that function calls were still expensive, and our code was often spending 50% of its time just calling them. Dennis had lied to us! But it was too late; we were all hooked…”

Steve Johnson

“Dennis Ritchie (el creador del lenguaje de programación C) alentó la modularidad diciendo a todos que cada una de las llamadas a funciones eran realmente muy rápidas en C. Todos comenzaron a escribir pequeñas funciones y modularizar. Años después descubrimos que las llamadas a funciones seguían siendo lentas, y nuestro código a menudo pasaba el 50% de su tiempo simplemente haciendo las llamadas. ¡Dennis nos había mentido! Pero era demasiado tarde; todos estábamos enganchados … “

Steve Johnson

Esta cita es de tiempos mas oscuros cuando las llamadas a funciones eran algo lento

Writing functions

Las funciones nos permiten reciclar código y usarlo en varios lugares después, repertir código es normalmente una mala idea.

Para crear una función usamos la palabra reservada «func» luego el nombre de tu función, luego abrimos y cerramos paréntesis como en el siguiente ejemplo.

func bienvenida() {
 let mensaje = "Bienvenido a mi App"
 print(mensaje)
}

ahora podemos correr este código solo usando «bienvenida()»

bienvenida()

Correr el código de una función también se conoce como Llamar a una función

Accepting parameters

Las funcione se vuelven mas poderosas cuando permitimos que acepten parámetros para hacer esto solo debemos especificar entre paréntesis el nombre del parámetro y el tipo de dato.

func bienvenida(nombre: String) {
 let mensaje = "Bienvenido \(nombre) a mi App"
 print(mensaje)
}

bienvenida(nombre: "Misael")
bienvenida(nombre: "Juan")

Este código puede imprimir un mensaje de saludo personalizado para cada usuario

Otro ejemplo para sumar números

func suma(numeroA: Int , NumeroB: Int){
    let suma = numeroA + NumeroB
    print(suma)
}

suma(numeroA: 4, NumeroB: 5)
suma(numeroA: 12, NumeroB: 10)

Returning values

Regresar valores

Así como podemos proporcionar parámetros podemos devolver valores para hacer esto usamos « ->» y ademas el tipo de dato, para devolver el valor usamos «return»

func suma(numeroA: Int , NumeroB: Int) -> Int{
     let suma = numeroA + NumeroB
     return suma
     // ningún código sera ejecutado después del return
 }

Ahora podemos usar el valor que nos regresa la función para algo mas

let resultadoDeLaSuma = suma(numeroA: 4, NumeroB: 12)
print("El resultado de la suma es \(resultadoDeLaSuma)")

Parameter labels

Etiquetas para los parámetros

Podemos usar las etiquetas de los parámetros para hacer mas legible nuestro código, para eso solo suministramos dos nombres separados por un espacio. El primero se usa fuera de la función y el otro de manera externa.

 func bienvenida(a nombre: String) {
  let mensaje = "Bienvenido \(nombre) a mi App"
  print(mensaje)
 }

bienvenida(a: "Misael")

Omitting parameter labels

Ahora habrás notado que funciones como «print(“Hello”)» no tienen una etiqueta en el parámetro, en ciertas ocasiones es también mas legible omitir el nombre del parámetro por ejemplo

 func saludarA(_ nombre: String) {
  let mensaje = "Bienvenido \(nombre) a mi App"
  print(mensaje)
 }

saludarA("Misael")
saludarA("Juan")

Esto puede hacer mas legible nuestro código, pero también podría hacerlo mas confuso por ejemplo

ponerAlarma(5)

Este código es bastante confuso ¿La alarma es para las 5am o 5pm o es para dentro de 5 minutos u horas o días?

Así que piensa con cuidado como hacer tu código mas legible

Default parameters

Podemos suministrar valores por default para nuestras funciones solo agregando «=» después del tipo de dato

func saludarA(_ nombre: String, amablemente: Bool = true) {
    var mensaje = ""
    if amablemente == true {
        mensaje = "Bienvenido \(nombre) a mi App"
    } else {
        mensaje = "¡Oh no, es \(nombre) de nuevo 🤢!"
    }
    
  print(mensaje)
 }

saludarA("Misael")
saludarA("Juan", amablemente: false)

Como ves podemos llamar a esta función de dos maneras diferentes

Variadic functions

Algunas funciones pueden usar varios parámetros del mismo tipo, para eso solo añadimos «…» después del tipo de dato, swift creara un Array con los valores que suministremos

func calcularCuadrados(numeros: Int...) {
    for numero in numeros {
        print("\(numero) al cuadrado es \(numero * numero)")
    }
}

calcularCuadrados(numeros: 1,2,3,4,5,6,7,8,9,10)

Writing throwing functions

Cuando se suministra un dato erróneo a una función esta puede fallar, swift nos deja «tirar» errores cuando algo sale mal, usando la palabra reservada «throw»

Para empezar debemos crear un enum con los tipos de error

enum errorDeContraseña: Error {
    case muyObvia
}
func revisarContraseña(_ contraseña: String) throws -> Bool {
    if contraseña == "contraseña" {
        throw errorDeContraseña.muyObvia
    }

    return true
}
Sin embargo podemos ver que usar la función de esta forma nos muestra una alerta

y así podemos saber exactamente en que fallo nuestra función

Running throwing functions

Ahora bien a Swift no le gusta tener errores cuando llamamos a una función así que necesitamos llamarla de manera segura, para hacer esto usamos las palabras reservadas «do» para iniciar un código que podría causar problemas, «try» frente a la función que puede causar problemas, y «catch» para manejar el error de manera mas elegante

 do {
     try revisarContraseña("contraseña")
     print("¡Buena contraseña!")
 } catch {
     print("¿Enserio 😐?")
 }
Ahora todo funciona fluido

Inout parameters

Los parámetros de una función son constantes, si queremos que nuestros parámetros cambien el valor suministrado en lugar de regresar uno nuevo debemos usar «inout» antes del tipo de dato y ademas «&» antes de el nombre de tu variable, que por cierto no puedes usar constates pues tu valor sera cambiado

 func dobleOnada(numero: inout Int) {
     numero *= 2
 }
 
 var miNumero = 10
 dobleOnada(numero: &miNumero)
 
 print(miNumero)
como puedes ver esta función cambia efectivamente el valor de tu variable

Resumen

Las funciones nos permiten reutilizar código

Las funciones aceptan parámetros solo especia a Swift que tipo de parámetros son

Las funciones pueden regresaros valores, solo especifica que tipo de valor deseas recibir

Puedes usar diferentes etiquetas para los parámetros u omitir sus nombres para hacer mas legible tu código

Los parámetros pueden tener valores por default lo cual es util para escribir menos código que tiende a repetirse

Podemos usar parámetros que usen cero o mas valores y Swift los convertirá en un Array

Las funciones pueden arrojar errores (throw errors) pero debemos usarlas con try y catch

Podemos usar inout para cambiar variables dentro de una función, aunque lo mejor es devolver una valor nuevo.

// y bueno eso fue el resumen de funciones, la verdad es que hay algunos temas muy simples como arrojar errores que son muy útiles 

Referencias

https://www.hackingwithswift.com/100

https://www.hackingwithswift.com/100/5

Proximo Día

Categories
Swift Development

100 Dias de Swift Día 4

Día 4

Estas son mis notas del curso hackingwithswift

Entrada anterior

“it’s hardware that makes a machine fast, but it’s software that makes a fast machine slow.”

Craig Bruce

«Es el hardware lo que hace rápida a una maquina, pero el software es lo que la hace lenta.»

Craig Bruce

Loops

Los loops adoptan varias formas pero su mecánica es la misma, un código que se repite hasta que una condición se vuelva falsa

For

El loop mas comun es «For», que itera sobre Arrays y Ranges, cada iteración nueva asigna un elemento a una constante. Por ejemplo en el siguiente Range

let contar = 1...10
for numero in contar {
    print("el numero es \(numero)")
}

super simple verdad, lo mismo con los Arrays

let albums = ["Red", "1989", "Reputation"]

for album in albums {
    print("\(album) esta en Apple Music")
}

Si no necesitas crear ninguna constante puedes hacer el loop de esta manera

for _ in 1...5 {
    print("Hoy no se fia, mañana si")
}

While

«While» es también un loop muy usado, básicamente ejecuta una acción si se cumple la condición

var numero = 1

while numero <= 10 {
    print(numero)
    numero += 1
}

print("Listos o no, allá voy!")

Repeat

Es similar a While solo que la condición va al final esto quiere decir que el código se ejecuta primero y luego verifica la condición

var numero = 1

repeat {
    print(numero)
    numero += 1
} while number <= 20

print("Listos o no, allá voy!")

La mayor diferencia como puedes ver es que el código con «repeat» se ejecutara por lo menos una vez

while false {
//Este código nunca se ejecuta
    print("Esto es falso")
}

repeat {
//Este código se ejecutara una vez
    print("Esto es falso")
} while false

Break

Si quieres salir de un bucle puedes usar «break»

var numero = 1

repeat {
    print(numero)
    if numero == 10 {
      print("Ya me aburrí, Listos o no, allá voy!")
      break
    }
    numero += 1

} while number <= 20

print("Listos o no, allá voy!")

Si tienes loops anidados y quieres salir de ambos puedes usar «break (etiqueta de tu loop principal)»

etiquetaLoopPadre: for i in 1...10 {
    for j in 1...10 {
        let resultado = i * j
        print ("\(i) * \(j) es igual a \(resultado)")

        if resultado == 50 {
            print("Eso es todo")
            break etiquetaLoopPadre
        }
    }
}

Continue

Así como podemos salir de un loop podemos omitir ciertos elementos usando «continue» por ejemplo en el siguiente ejemplo solo imprimimos números pares

for i in 1...10 {
    if i % 2 == 1 {
    //omitir si es primo
        continue
    }

    print(i)
}

Loops infinitos

Los loop infinitos son algo muy común en iOS básicamente son loops que se ejecutan de manera indeterminada, pero ten cuidado recuerda establecer una salida para tu bucle si no se repetirá para siempre.

Lo mas común es poner una condición verdadera en un While

var contador = 0

while true {
    print(" ")
    contador += 1

    if contador == 273 {
        break
    }
}

Resumen

Los loops se repiten hasta que una condición es falsa

Los loops mas comunes son For y While

Puedes omitir usar la constate de For si no la necesitas

Puedes usar Repeat en lugar de While si quieres que tu código se ejecute por lo menos una vez

Para salir de un bucle usa break y para omitir un elemento continue

y por ultimo los loops infinitos necesitan que les suministres una salida para

//y bueno esos son los loops 

Referencias

https://www.hackingwithswift.com/100/4

https://www.hackingwithswift.com/100

Proximo Día

Categories
Swift Development

100 Dias de Swift Día 3

Día 3

Estas son mis notas del curso hackingwithswift

Entrada Anterior

“Computers are like Old Testament gods: lots of rules and no mercy.”

Joseph Campbell

“Las computadoras son como el Antiguo Testamento : Un montón de reglas y sin misericordia .”

Joseph Campbell

Operators and conditions

Operadores y condiciones

Los operadores nos permiten combinar diferentes tipos de datos, los operadores son símbolos matemáticos, como «+» y «-», en Swift hay muchos de estos.

Empecemos con dos variables de ejemplos

let primerPuntaje = 12
let segundoPuntaje = 8

Podemos sumar y restar de esta forma

let total = primerPuntaje + segundoPuntaje
let diferencia = primerPuntaje - segundoPuntaje

y podemos dividir y multiplicar usando «*» y «/» :

let producto = primerPuntaje * segundoPuntaje
let división = primerPuntaje / segundoPuntaje

Swift tiene un operador especial para calcular el residuo de una operación, calcula cuántas veces un numero cabe en otro, entonces regresa el valor que queda.

let residuo = 25 % segundoPuntaje
El resultado es 1

Operator overloading

Operator overloading es una forma sofisticada de decir que lo que el operador hace depende del valor con el que lo usemos aquí un ejemplo + puede sumar números

let sentidoDeLaVidaElUniversoYTodoLoDemás = 42
let elDobleSentidoDeLaVidaElUniversoYTodoLoDemás = 42 + 42

//disculpa la fuerte referencia a The Hitchhiker's Guide to the Galaxy, de Douglas Adams así venia en el curso 😅

Pero también puede unir cadenas de texto

let celos = "Estos celos me hacen "
let acción =  celos + "Daño"

Incluso podemos unir Arrays

let primeraMitad = ["Jorge", "Hernán"]
let segundaMitad = ["Eduardo", "Luis", "Óscar"]

let 🐯Norte =  primeraMitad + segundaMitad

Recuerda que Swift no permite mezclar diferentes tipos de datos así que no trates de sumar un String y un Int

Compound assignment operators

Swift permite combinar los operadores con un cambio de valor, de esta forma podemos reasignar valores en un solo paso, usando los operados que ya conocemos «+,-,*,/» pero con «=» al final

Por ejemplo

var puntaje = 95
puntaje -= 5

De forma similar podemos combinar dos Strings en un paso

var refrán = "Camarón que se duerme"
refrán += " se lo lleva la corriente"

Comparison operators

Estos operadores funciona mas o menos como en matemáticas

let primerPuntaje = 6
let segundoPuntaje = 4

hay dos operadores para revisar igualdad:

« == » revisa que dos valores sean iguales y « != » que se pronuncia no iguales y revisa que dos valores no sean el mismo

primerPuntaje == segundoPuntaje
primerPuntaje != segundoPuntaje

Hay cuatro operadores para comparar valores como en matemáticas

« > » mayor que

« < » menor que

« >= » mayor o igual que

« <= » menor o igual que

primerPuntaje >= segundoPuntaje
primerPuntaje <= segundoPuntaje

Ademas estos funciona con Strings también, ya que las Strings tienen un orden alfabético natural

"Taylor" <= "Swift"

Conditions

Condiciones ahora que conocemos los operadores podemos usar declaraciones condicionales con « if »

Para demostrar esto usaremos una de las funciones de Swift llamada « print( )» que nos sirve para mostrar texto

Por ejemplo podemos usar una condición cuando se llega al puntaje mas alto para imprimir un mensaje de victoria

let primerPuntaje = 30
let segundoPuntaje = 70

if primerPuntaje + segundoPuntaje == 100 {
 print("Ganaste 🥳")
}

El código entre las llaves « { } » se ejecutara cuando la condición sea cierta, y puedes ademas proveer una alternativa si la condición retorna falso usando « else »

if primerPuntaje + segundoPuntaje == 100 {
 print("Ganaste 🥳")
} else {
 print("Sigue intentando")
}

Puedes ademas proveer mas condiciones usando « else if»

if primerPuntaje + segundoPuntaje == 100 {
 print("Ganaste 🥳")
} else if primerPuntaje + segundoPuntaje == 50 {
 print("Bien vas a la mitad sigue intentando")
}else {
 print("Sigue intentando")
}

Combining conditions

Swift tiene tiene dos operadores para combinar operadores estos son « && » (pronunciado «y») y « || » (pronunciado « o » )

Por ejemplo

let edad1 = 16
let edad2 = 21
if edad1 > 18 && edad2 > 18 {
 print("Ambos son mayores de edad")
}

En esta caso Swift no ejecutara el código pues la primera condición no es verdadera de hecho ni siquiera revisara la segunda pues la primera es falsa

Por otro lado el operador « Or » evaluara las segunda condición incluso si la primera no es verdadera

if edad1 > 18 || edad2 > 18 {
 print("Al menos uno es mayor de edad")
}

Aunque se pueden usar mas de un && y || en una condición lo mejor es mantener las cosas simples para facilitar la lectura

The ternary operator

Operadores terciarios

Existe un operador muy poco usado llamado operador terciario « : »

//Bueno el curso decía eso pero yo lo uso mucho en SwiftUI 

Aqui un ejemplo

print(edad1 > 18 && edad2 > 18 ? "Ambos son mayores de edad" : "Alguno de ellos no lo es")

Switch statements

Si tienes muchas condiciones if y else es mejor usar otra estructura llamada « switch case » veamos el siguiente ejemplo

let clima = "soleado"

Se puede usar switch en un bloque para para imprimir diferentes mensajes

switch clima {
case "lluvia":
    print("Lleva un paraguas")
case "nieve":
    print("Usa un abrigo ")
case "soleado":
    print("Usa protector solar")
default:
    print("¡Disfruta tu día!")
}

En el ultimo ejemplo « default » es requiero por Swift para asegurarnos que se cubran todos los posibles casos

Swift solo correrá el código dentro de cada caso si quieres que Swift pase al siguiente caso usa « fallthrough »

switch clima {
case "lluvia":
    print("Lleva un paraguas")
case "nieve":
    print("Usa un abrigo ")
case "soleado":
    print("Usa protector solar")
    fallthrough
default:
    print("¡Disfruta tu día!") 
}

Range operators

Swift nos ofrece dos maneras de hacer rangos « ranges » para eso tenemos los operados « ..< » y « … » el primer crear un rango de valores excluyendo el ultimo valor, el segundo crear un rango de valores que incluye el ultimo valor

Por ejemplo

let rango = 1..<5
//contiene los números 1,2,3 y 4

let rango2 = 1...5
//contiene los valores 1,2,3,4 y 5

Los rangos pueden ser usados en un switch

let puntaje = 85

switch puntaje {
case 0..<50:
    print("fallaste")
case 50..<85:
    print("lo hiciste bien")
default:
    print("¡Lo hiciste genial!")
}

Resumen

Los operados en swift sirven para hacer operaciones aritméticas +,-,*,/

Hay operadores que sirven para modificar tus variables en un paso como += y -=

Puedes usar if, else y else if para correr código en base a una condición

Swift tiene un operador terciario que si bien no es muy conveniente usarlo en bloques de código de Swift es mejor usarlo solo en SwiftUI

Si tenemos varias condiciones es mejor usar una estructura switch

Podemos crea rangos usando ..< y … dependiendo de cual usemos el ultimo numero podría excluirse

//y bueno ese fue le día 3

Referencias

https://www.hackingwithswift.com/100/3

https://www.hackingwithswift.com/100