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

¿Cómo crear una clase para guardar y recuperar datos del usuario en Swift?

Habilitar funcionalidades que permitan a los usuarios guardar y recuperar datos es esencial para la mayoría de las aplicaciones móviles modernas. En esta sección, exploraremos cómo lograr esta tarea en una aplicación de iOS usando el lenguaje Swift. Vamos a crear una estructura que permita almacenar datos importantes como el correo, la contraseña y el nombre del usuario.

class SaveData {
    var correo: String = ""
    var contraseña: String = ""
    var nombre: String = ""

    func guardarDatos(correo: String, contraseña: String, nombre: String) -> Bool {
        print("Dentro de la función guardar datos obtuve: \(correo), \(contraseña), \(nombre)")
        UserDefaults.standard.set([correo, contraseña, nombre], forKey: "datosUsuario")
        return true
    }

    func recuperarDatos() -> [String] {
        let datosUsuario = UserDefaults.standard.stringArray(forKey: "datosUsuario") ?? [""]
        print("Recuperé los datos: \(datosUsuario)")
        return datosUsuario
    }

    func validar(correo: String, contraseña: String) -> Bool {
        guard let datosUsuario = UserDefaults.standard.stringArray(forKey: "datosUsuario") else {
            print("No hay datos de usuario guardados en UserDefaults")
            return false
        }
        
        let correoGuardado = datosUsuario[0]
        let contraseñaGuardada = datosUsuario[1]

        print("Validando datos. Correo guardado: \(correoGuardado), Contraseña guardada: \(contraseñaGuardada)")
        
        if correo == correoGuardado && contraseña == contraseñaGuardada {
            return true
        } else {
            return false
        }
    }
}

¿Cómo utilizamos UserDefaults para almacenar datos?

Swift facilita el almacenamiento de pequeñas cantidades de datos en un formato persistente mediante UserDefaults. Esto es ideal para información que no es demasiado pesada y requiere persistencia entre sesiones de la aplicación, como en el caso de almacenar strings, booleans y números.

  • Key-Value Pair: UserDefaults utiliza un sistema de llave-valor similar a un diccionario. Para recuperar los valores, utilizamos la llave asociada.

  • Tipos de datos soportados: UserDefaults es adecuado para tipos de datos simples como strings, booleans y números. No es recomendable para datos grandes como imágenes y videos.

¿Cómo validar datos de usuario en el inicio de sesión?

Al implementar la función de validación, creamos una lógica que compara el correo y la contraseña que ingresó el usuario con los ya almacenados. Esto es útil, por ejemplo, al validar un inicio de sesión:

func validar(correo: String, contraseña: String) -> Bool {
    if correo == correoGuardado && contraseña == contraseñaGuardada {
        return true
    } else {
        return false
    }
}

Con esta función, puedes indicar al usuario si introdujo correctamente los datos, y puedes hacerlo mostrando una alerta en caso de que no coincida.

Retos adicionales para el desarrollo

Para el ejercicio práctico propuesto, debes implementar la lógica que permita a un usuario registrarse y luego validar sus datos al iniciar sesión. Si los datos no son correctos, se debe mostrar una alerta:

  • Crea la interfaz gráfica que permita al usuario ingresar su correo y contraseña.

  • Implementa mensajes de alerta para darle retroalimentación al usuario si los datos son incorrectos.

Recuerda que este tipo de ejercicios mejora tus habilidades al trabajar con lógica de aplicación y manejo de usuarios, permitiéndote crear aplicaciones más funcionales y seguras. ¡Mucho ánimo, sigue practicando y avanzando en tu proceso de aprendizaje!

Aportes 3

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

  • 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 “ifNotUserFound” 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 “duro”… 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
    }
 }```