Introducci贸n al curso

1

Construyamos una app para iOS

2

Introducci贸n a la arquitectura MVVM

3

Planeando nuestra app

Creando las primeras pantallas de la app

4

Programando la primera pantalla en m贸dulos

5

Escribiendo la l贸gica para mostrar dos pantallas

6

Pantalla de inicio de sesi贸n con SecureField y Scroll

7

Completando nuestra pantalla de inicio de sesi贸n

8

Pantalla de registro de usuario

9

Comprobando el funcionamiento de nuestras pantallas

10

Estructura de las pantallas con TabView

11

Creando nuestra pantalla home

12

Pantalla home: logo y barra de b煤squeda

13

Pantalla home: programaci贸n de interfaces est谩ticas

14

Pantalla home: carruseles

Aplicando arquitectura MVVM

15

Creando estructura para arquitectura MVVM

16

Modelando nuestro JSON

17

Peticiones al servidor

18

Mostrar informaci贸n de un servidor de manera din谩mica

19

Mostrar im谩genes de forma din谩mica y eficiente con LazyVGrid

Reproductor y b煤squeda de video

20

Pasar datos entre pantallas

21

Darle datos de inicio a un Canvas

22

Reproducir videos din谩micamente de un servidor

23

Mostrar im谩genes din谩micamente de un servidor

24

Mostrar alertas

25

Programar clase de b煤squeda

26

Programar m茅todo de b煤squeda

脷ltimas pantallas de la app

27

Pantalla de favoritos

28

Pantalla de perfil de usuario

29

M贸dulo de ajustes de perfil con Toggle

30

Pantalla de edici贸n de perfil

31

M贸dulo de edici贸n de perfil

32

Guardado interno de datos

Utilizando la c谩mara y fotos del iPhone

33

Captura de foto de perfil: ImagePicker y vista Sheet

34

Captura de foto de perfil con la c谩mara: modificar librer铆as de terceros

35

Captura de foto de perfil con la c谩mara: recuperar im谩genes guardadas

驴Qu茅 m谩s posibilidades tiene SwiftUI?

36

Mejoremos nuestra app

No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Guardado interno de datos

32/36
Recursos

Aportes 3

Preguntas 4

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

  • Reto:

Inicio de sesi贸n

( Usando los m茅todos de la clase SaveData() )

  1. Acci贸n ejecutada al Iniciar Sesi贸n. A帽ad铆 la nueva variable bool 鈥渋fNotUserFound鈥 para activar la alerta en caso que no encuentre usuario.
@State var ifNotUserFound = false

func iniciarSesion()
    {
        // Comienza el inicio de sesi贸n
        print("Estoy iniciando sesi贸n.")
        
        // Creaci贸n de instancias. En este caso la clase SaveData
        let objetoActualizadorDatos = SaveData()
        let resultado = objetoActualizadorDatos.validar(correo: self.correo, contrasena: self.contrase帽a)
        
        if resultado == true
        {
            // Se activa la pantalla Home
            ifNotUserFound = false
            isPantallaHomeActive = true
        }else
        {
            ifNotUserFound = true
        }
        
    }


  1. Bot贸n

Button(action: {iniciarSesion()},
                       label:
                       {
                            Text("Iniciar Sesi贸n").fontWeight(.bold)
                                .foregroundColor(.white)
                                .frame(maxWidth: .infinity, alignment: .center)
                                .padding(EdgeInsets(top: 11, leading: 18, bottom: 11, trailing: 18))
                                .overlay(RoundedRectangle(cornerRadius: 6.0)
                                .stroke(Color("Dark-cian"), lineWidth: 1.0)
                                .shadow(color: .white, radius: 6))
                       }).alert(isPresented: $ifNotUserFound, content:
                                    {
                                        Alert(title: Text("Error"), message: Text("No se encontr贸 nig煤n usuario o la contrase帽a es incorrecta"), dismissButton: .default(Text("Entendido")))
                                    }
                                         )

Registro

  1. Acci贸n ejecutada al Registrar

@State var isPantallaHomeActive = false 

func registrar()
{
        // Confirmar que la contrase帽a haya sido escrita correctamente
        if contrase帽a != confirmarContrase帽a
        {
            contrase帽aIsNotConfirmed = true
        }else
        {
            // Contrase帽a confirmada. No se muestra la alerta.
            contrase帽aIsNotConfirmed = false
            
            // Objeto de la clase SavData.
            let objetoActualizadorDatos = SaveData()
            // Registro que no requiere nombre.
            let resultado = objetoActualizadorDatos.registrar(correo: self.correo, contrasena: self.contrase帽a)
            
            // Activar la pantalla home
            isPantallaHomeActive = true
            print("Se guardaron los datos con exito?: \(resultado)")
        }
        
        
}

  1. Bot贸n Registrar
  • Si la contrase帽a no se confirma, es decir si no son iguales contrase帽a y confirmarContrase帽a, se activa la alerta con la variable contrase帽aIsNotConfirmed

Button(action: {registrar()},
                       label:
                       {
                            Text("REG脥STRATE").fontWeight(.bold)
                                .foregroundColor(.white)
                                .frame(maxWidth: .infinity, alignment: .center)
                                .padding(EdgeInsets(top: 11, leading: 18, bottom: 11, trailing: 18))
                                .overlay(RoundedRectangle(cornerRadius: 6.0)
                                .stroke(Color("Dark-cian"), lineWidth: 1.0)
                                .shadow(color: .white, radius: 6))
                       }).alert(isPresented: $contrase帽aIsNotConfirmed, content:
                                    {
                                        Alert(title: Text("Error"), message: Text("La contrase帽a no coincide. Confirma tu contrase帽a"), dismissButton: .default(Text("Entendido")))
                                    }
                                         )


// Navegar a la pantalla Home
        NavigationLink(destination: Home(), isActive: $isPantallaHomeActive, label: {EmptyView()})

De manera constructiva me pareci贸 muy decepcionante tu explicaci贸n y mas que hacer el ejemplo con contrase帽as en 鈥渄uro鈥濃 gente que est谩 partiendo en la programaci贸n puede cometer errores graves.
Un abrazo

Aqui subo mi aporte porque creo que creo que deber铆amos guardar datos sensibles como contrase帽as en los userdefaults, si no en los keychain:

import Foundation
import Security

class SaveData {
    
    let defaultsKey = "GameUserData"
    var correo:String = ""
    var contrasena:String = ""
    var nombre:String = ""
    
    func RetrivePass(username:String) -> String {
        
        // Set query
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: username,
            kSecMatchLimit as String: kSecMatchLimitOne,
            kSecReturnAttributes as String: true,
            kSecReturnData as String: true,
        ]
        var item: CFTypeRef?

        // Check if user exists in the keychain
        if SecItemCopyMatching(query as CFDictionary, &item) == noErr {
            // Extract result
            if let existingItem = item as? [String: Any],
               // let username = existingItem[kSecAttrAccount as String] as? String,
               let passwordData = existingItem[kSecValueData as String] as? Data,
               let password = String(data: passwordData, encoding: .utf8)
            {
                print(username)
                print(password)
                return password
            }
        } else {
            print("Something went wrong trying to find the user in the keychain")
        }
        
        return ""

    }
    
    func SaveDatas(correo:String, contrasena:String, nombre:String) -> Bool {
        
        UserDefaults.standard.set([correo, nombre], forKey: defaultsKey)
        
        let username = correo
        let password = contrasena.data(using: .utf8)!
        

        // Check if user exists in the keychain
        if RetrivePass(username: username) != "" {

            // Set query
            let queryUpdate: [String: Any] = [
                kSecClass as String: kSecClassGenericPassword,
                kSecAttrAccount as String: username,
            ]

            // Set attributes for the new password
            let attributesUpdate: [String: Any] = [kSecValueData as String: password]

            // Find user and update
            if SecItemUpdate(queryUpdate as CFDictionary, attributesUpdate as CFDictionary) == noErr {
                print("Password has changed")
            } else {
                print("Something went wrong trying to update the password")
            }
            
            
        } else {
            
            // Set attributes
            let attributes: [String: Any] = [
                kSecClass as String: kSecClassGenericPassword,
                kSecAttrAccount as String: username,
                kSecValueData as String: password,
            ]

            // Add user
            if SecItemAdd(attributes as CFDictionary, nil) == noErr {
                print("User saved successfully in the keychain")
            } else {
                print("Something went wrong trying to save the user in the keychain")
            }
            
        }
        
        return true
        
    }
    
    func RetriveData() -> [String]? {
        if UserDefaults.standard.object(forKey: defaultsKey) != nil {
            var gameUserData:[String] = UserDefaults.standard.stringArray(forKey: defaultsKey)!
            
            let username = gameUserData[0]
            let password = RetrivePass(username: username)
            gameUserData.insert(password, at: 2)
            
            return gameUserData
        }
        
        return nil
        
    }
    
    func validateEmail(correo:String, contrasena:String) -> Bool {
        
        var correoGuardado = ""
        var contrasenaGuardada = ""
        
        if UserDefaults.standard.object(forKey: defaultsKey) != nil {
            
            correoGuardado = UserDefaults.standard.stringArray(forKey: defaultsKey)![0]
            // contrasenaGuardada = UserDefaults.standard.stringArray(forKey: defaultsKey)![]
            
            contrasenaGuardada = RetrivePass(username: correoGuardado)
            
        }
        
        if (correoGuardado == correo && contrasenaGuardada == contrasena) {
            return true
        } else {
            return false
        }
        
        
    }
    
    func deleteKeychain(username:String) -> Bool {
        
        if UserDefaults.standard.object(forKey: defaultsKey) != nil {
            UserDefaults.standard.removeObject(forKey: defaultsKey)
        }
        
        if RetrivePass(username: username) != "" {

            // Set query
            let query: [String: Any] = [
                kSecClass as String: kSecClassGenericPassword,
                kSecAttrAccount as String: username,
            ]

            // Find user and delete
            if SecItemDelete(query as CFDictionary) == noErr {
                print("User removed successfully from the keychain")
                return true
            } else {
                print("Something went wrong trying to remove the user from the keychain")
                return false
            }
        }
        
        return false
    }
 }```