NEW: Learn SwiftUI with my free YouTube video series! >>

< Previous: Creando el proyecto   Next: Diseñando nuestra interfaz >

Listando imágenes con FileManager

Las imágenes que le he proporcionado vienen de la National Oceanic and Atmospheric Administration (NOAA), que es una agencia del gobierno de USA que produce contenido de dominio público que podemos reutilizar libremente. Una vez que han sido copiadas en su proyecto, Xcode las incluirá automáticamente en su app y podrá acceder a ellas.

Entre bastidores, una app de iOS es realmente un directorio que contiene muchos archivos: el propio binario (que es la versión compilada de su código, lista para ejecutarse), todos los activos de medios que usa su aplicación, cualquier fichero de diseño visual que tenga, además de una variedad de otras cosas como metadatos o autorizaciones de seguridad.

Estos directorios de aplicación son llamados paquetes (bundles), y tienen la extensión .app. Ya que nuestros ficheros de medios están sueltos dentro de la carpeta, podemos pedirle al sistema que nos diga todos los ficheros que están ahí y quedarnos con los que queramos. Debe haber advertido que todas las imágenes comienzan el nombre "nssl" (abreviatura de National Severe Storms Laboratory), así que nuestra tarea es simple: listar todos los ficheros de la carpeta de nuestra app y quedarnos con los que comiencen por "nssl".

Por ahora, cargaremos esta lista y simplemente la mostraremos en el visor de log propio de Xcode, pero pronto la haremos aparecer en nuestra app.

Así que, paso 1: abra ViewController.swift. Un controlador de vista se puede considerar como una pantalla de información, y para nosotros esto es simplemente una gran pantalla blanca. ViewController.swift es responsable de mostrar esa pantalla blanca, y justo ahora no tendrá mucho código. Debería ver algo como:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
}

Esto contiene cinco cosas interesantes que las que quiero hablar antes de seguir.

  1. El archivo comienza con import UIKit, que significa "este archivo referenciará al toolkit de interfaz de iOS'
  2. La línea class ViewController: UIViewController signigica "quiero crear una nueva ventana de datos llamada ViewController, basada en UIViewController." Cuando ve un tipo de datos que comienza con "UI", significa que pertenece a UIKit. UIViewController es el tipo de ventana por defecto de Apple, que está vacía y es blanca hasta que la cambiemos.
  3. La línea override func viewDidLoad() comienza un método (un bloque de código), que es una pieza de código dentro de nuestra ventana ViewController. La palabra override es necesaria porque significa "queremos cambiar el comportamiento por defecto de Apple en UIViewController.” viewDidLoad() es llamada cuando la pantalla se ha cargado y está lista para ser personalizada.
  4. Hay muchos caracteres { y }. Estos símbolos, conocidos como corchetes (o algunas veces llaves), son usados mara marcar trozos de código, y es costumbre sangrar las líneas dentro de los corchetes para que sea más fácil identificar donde comienza y termina el bloque. Los corchetes más externos contienen al tipo de datos ViewController completo, y los corchetes interiores marcan el comienzo y el fin de el método viewDidLoad().
  5. El método viewDidLoad() contiene una línea de código que dice super.viewDidLoad() y una línea de comentario (es la línea que comienza con //). Esta llamada a super quiere decir "dile al UIViewController de Apple que ejecute su propio código antes de ejecutar el mío," y verá esto usado muchas veces.

Volveremos mucho a este código en futuros proyectos; no se preocupe si es un poco confuso por ahora.

¿Sin número de líneas? Mientras lee código, es frecuentemente de ayuda tener los número de línea habilitados para poder referirse fácilmente a código específico. Si su Xcode no está mostrando los número de línea por defecto, le sugiero que las active ahora: vaya al menú de Xcode y seleccione Preferences, entonces seleccione la pestaña Text Editing y asegúrese de que "Line Numbers" está seleccionado.

Como decía anteriormente, el método viewDidLoad() es llamado cuando la pantalla se ha cargado y está lista para que la personalice. Todo entre func viewDidLoad() { y }, que sigue a algunas líneas, es parte de ese método y será llamado cuando pueda comenzar a personalizar la pantalla.

Vamos a poner algo de más código dentro de este método para cargar las imágenes NSSL. Y lo haremos debajo de la línea que dice super.viewDidLoad():

let fm = FileManager.default
let path = Bundle.main.resourcePath!
let items = try! fm.contentsOfDirectory(atPath: path)

for item in items {
    if item.hasPrefix("nssl") {
        // this is a picture to load!
    }
}

Nota: Algunos desarrolladores de Swift experimentados leerán este código, verán try!, y me escribirán un email enfadados. Si está considerando hacer justo eso, por favor, antes de nada continúe leyendo.

Es un gran trozo de código, que es todo nuevo. Repasémoslo y veamos qué hace línea a línea:

  • La línea let fm = FileManager.default declara una constante llamada fm y le assigna el valor devuelto por FileManager.default. Esto es un tipo de dato que nos permite trabajar con el sistema de ficheros, y en nuestro caso la usaremos para buscar ficheros.
  • La línea let path = Bundle.main.resourcePath! declara una constante llamada path que es inicializada a la ruta de los recursos del paquete de nuestra app. Recuerde, un paquete es un directorio que contiene nuestro programa compilado y todos nuestros activos. Así, esta línea dice: "dime dónde puedo encontrar todas las imágenes que añadí a mi app"
  • La línea let items = try! fm.contentsOfDirectory(atPath: path) declara una tercera constante llamada items que es inicializada con los contenidos del directorio en una ruta. ¿Qué ruta?. Bueno, la devuelta por la línea anterior. Como puede ver, ¡los nombres largos de métodos de Apple hacen su código auto-descriptivo!. La constante items es un array - una collection - de nombres de todos los ficheros que fueron encontrados en el directorio de recursos de nuestra app.
  • La línea for item in items { comienza un bucle. Los bucles son un bloque de código que se ejecuta múltiples veces. En este caso, el bucle se ejecuta una vez para cada item que encontremos en el paquete de nuestra app. Note que la línea tiene un corchete de apertura al final, señalando el comienzo de un nuevo bloque de código, y hay un corchete de cierre emparejado con este cuatro líneas más abajo. Todo dentro de dichos corchetes será ejecutado cada vez que el bucle de una vuelta. Podríamos traducir eso como "trata items como una serie de cadenas de texto, extrae cada una de dichas cadenas de texto, dale el nombre item, entonces ejecuta el siguiente código..."
  • La línea if item.hasPrefix("nssl") { es la primera línea dentro de nuestro bucle. En este punto, tendremos el primer nombre de fichero preparado para trabajar con él, y se llamará item. Para decidir si es uno del que ocuparse o no, usaremos el método hasPrefix(): que toma un parámetro (el prefijo por el que buscar) y devuelve bien true o bien false. Este "if" al inicio significa que esta línea es una sentencia condicional: si el item tiene el prefijo "nssl", entonces... así es, otro corchete de apertura para marcar otro nuevo bloque de código. Esta vez, el código será ejecutado sólo si hasPrefix() devuelve true.
  • Por último, la línea // this is a picture to load! es un comentario - si llegamos aquí, item contiene el nombre de una imagen para cargar de nuestro paquete, así que podemos almacenarla en cualquier lugar.

En esas pocas líneas de código hay mucho que asimilar, así que antes de continuar, recapitulemos:

  • Usamos let para declarar constantes. Las constantes son piezas de datos que queremos referenciar, pero que sabemos que no cambiarán su valor. Por ejemplo, su cumpleaños es una constante, pero su edad no lo es - su edad es una variable, porque varía.
  • A los codificadores de Swift les gusta usar constantes en lugares donde otros desarrolladores usan variables. Esto es porque cuando está realmente codificando comienza a darse cuenta de que la mayoría de los datos que almacena no necesitan realmente cambiar mucho, así que debería hacerlos también constantes. Haciéndolo así permite al sistema hacer que su código se ejecute más rápido, y también añade seguridad extra porque si intenta cambiar una constante Xcode rechazará la construcción de su app.
  • El texto en Swif es representado usando el tipo de dato String. Los strings de Swift son extremadamente poderosos y garantizan el trabajar con cualquier lenguaje que pueda pensar - Inglés, Chino, Klingon y más.
  • Las colecciones de valores se llaman arrays (matrices), y están normalmente restringidas a almacenar un tipo de dato cada vez. Un array de cadenas de texto se escribe [String] y sólo puede almacenar cadenas de texto. Si intenta poner números en ella, Xcode no le dejará construir su app. La palabra clave try! significa "el código que sigue puede potencialmente ir mal, pero tengo la absoluta certeza que no lo hará." Si el código falla - por ejemplo si el directorio por el que preguntamos no existe - nuestra app fallará.
  • En este caso es perfectamente correcto llamar a try!, porque si nuestro código falla significa que nuestra app no puede leer sus propios datos o ¡algo está seriamente mal!. Algunos desarrolladores de Swift intentan escribir código para manejar estos errores catastróficos en tiempo de ejecución, pero desafortunadamente simplemente enmascaran el problema ocurrido.
  • Puede usar for someItem in someArray para iterar sobre cada item en el array. Swift extrae cada item y ejecuta el código dentro del bucle una vez para cada item.

Si es extremadamente observador debería haberse dado cuenta de una pequeña cosa que es también una de las partes más complicadas de Swift, así que lo voy a dejar tan simple como sea posible por ahora, luego lo ampliaremos con el tiempo: es el signo de exclamación al final de Bundle.main.resourcePath!

No, no fue una errata por mi parte. Si quita el signo de exclamación el código no funcionará más, así que claramente Xcode piensa que es importante - y en efecto lo es. Swift tiene tres maneras de trabajar con datos:

  1. Una variable o constante que almacena el dato. Por ejemplo, name: String es una cadena de caracteres llamada name.
  2. Una variable o constante que podría almacenar el dato, pero no estamos seguros. Esto se llama un tipo opcional, y se muestra como: name: String? No puede usarlo directamente, en su lugar debe pedir a Swift que compruebe si tiene un valor primero.
  3. Una variable o constante que podría almacenar el dato o podría no almacenarlo, pero estamos 100% seguros de que lo hace - al menos una vez que lo ha sido por primera vez. Esto se llama un opcional implícitamente desenvuelto, y se muestra como: name: String! Puede usarlo directamente.

Cuando le explico esto a las personas, se quedan confundidas, así que por favor no se preocupe si lo de arriba no tiene sentido para usted - volveremos sobre los opcionales una y otra vez en los siguientes proyectos, así que dese tiempo a si mismo.

Veremos los opcionales en mayor profundidad más tarde, pero por ahora lo que importa es que Bundle.main.resourcePath puede o no puede devolver una cadena, así que lo que devuelve es un String? - que es una cadena opcional. Añadiendo el signo de exclamación al final estamos forzando a desenvolver la cadena opcional, que significa que estamos diciendo "Estoy seguro de que devolverá una cadena real, nunca será nil, así que por favor dámela como una cadena normal."

Aviso importante: si nunca intenta usar una constante o variable que tiene un valor nil, su app nunca fallará. Como resultado, algunas personas han llamado ! el operador de fallo porque es fácil equivocarse. Lo mismo es cierto con try!, con el que es también fácil equivocarse. No se preocupe si todo esto le suena difícil por ahora - lo estará usando más tarde, y tendrá más sentido con el tiempo.

Ahora nuestro código carga la lista de ficheros que están dentro del paquete de nuestra app, luego itera sobre todas ellas para encontrar las que tienen un nombre que comienza por "nssl". Sin embargo, actualmente no hace nada con esos ficheros, así que nuestro siguiente paso es crear un array para todas las imágenes "nssl" para que podamos referirnos a ellas más tarde en lugar de tener que re-leer el directorio de recursos una y otra vez.

Las tres constantes que ya hemos creado - fm, path, e items – viven dentro del método viewDidLoad(), y serán destruidas tan pronto como el método finalice. Lo que queremos es una manera de adjuntar datos a todo el tipo ViewController para que exista mientras nuestra pantalla exista. En Swift esto se hace usando una "propiedad": podemos darle a ViewController tantas de estas propiedades como queramos, luego leerlas y escribirlas tan frecuentemente como necesitemos mientras la pantalla exista.

Para crear una propiedad, necesita declararla fuera de los métodos. Hasta aquí hemos estado creando constantes usando let, pero este array va a ser cambiado dentro de nuestro blucle así que necesitamos hacerlo variable. Necesitamos también decirle a Swift exactamente qué tipo de datos va a almacenar - en nuestro caso es un array de cadenas, donde cada item será el nombre de una imagen "nssl".

Añada esta línea de código delante de viewDidLoad():

var pictures = [String]()

Si la ha situado correctamente, su código debe mostrarse como sigue:

class ViewController: UIViewController {
    var pictures = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        let fm = FileManager.default

La palabra var es usada para crear variables, de la misma manera que let es usada para crear constantes. Cuando las cosas se ponen un poco locas es en la segunda mitad de la línea: [String](). Esto son realmente dos cosas en una: [String] significa "un array de cadenas" y () significa "crea uno ahora". Los paréntesis aquí son justo como los del método viewDidLoad() - señaliza el nombre de otro código que debería ser ejecutado. En este caso el código para crear un nuevo array de cadenas.

Este array pictures será creado cuando la pantalla ViewController sea creada, y existe mientras la pantalla exista. Estará vacío, porque no lo hemos rellenado con nada, pero al menos está ahí para que lo rellenemnos.

Lo que realmente queremos es añadir al array pictures todos los ficheros que coincidan dentro de nuestro bucle. Para hacer esto, necesitamos reemplazar el comentario existente // this is a picture to load! con código para añadir cada imagen al array pictures.

Agraciadamente, los arrays de Swift tienen un método incluido llamado append que podemos usar para añadir cualquier item que queramos. Así que, reemplace el comentario // this is a picture to load! con esto:

pictures.append(item)

¡Así es! Fastidiosamente, después de todo el trabajo su app no parecerá hacer nada cuando pulse play - verá la misma pantalla blanca que antes. ¿Funcionó, o las cosas simplemente fallaron de forma silenciosa?

Para descubrirlo, añada esta línea de código al final de viewDidLoad(), justo antes del corchete de cierre:

print(pictures)

Esto le dice a Swift que imprima los contenidos de pictures a la consola de depuración de Xcode. Cuando ejecute el programa ahora, debería ver este texto aparecer al pie de la pantalla de Xcode: “["nssl0033.jpg", "nssl0034.jpg", "nssl0041.jpg", "nssl0042.jpg", "nssl0043.jpg", "nssl0045.jpg", "nssl0046.jpg", "nssl0049.jpg", "nssl0051.jpg", "nssl0091.jpg”]”

Nota: a iOS le gusta imprimir muchos mensajes depuración no interesantes en la consola de depuración de Xcode. No se preocupe si ve mucho otro texto que no reconoce - solo desplácese hasta que vea el texto anterior, y si no lo ve entonces está listo para comenzar.

LEARN SWIFTUI FOR FREE I wrote a massive, free SwiftUI tutorial collection, and also have a growing list of free SwiftUI tutorials on YouTube – get started today!

< Previous: Creando el proyecto   Next: Diseñando nuestra interfaz >
MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>