Advertisement
  1. Code
  2. Mobile Development
  3. iOS Development

iOS desde cero con Swift: Persistencia de datos y Sandboxing en iOS

Scroll to top
Read Time: 16 min
This post is part of a series called iOS From Scratch With Swift.
iOS From Scratch With Swift: Exploring Tab Bar Controllers
iOS From Scratch With Swift: Building a Shopping List Application 1

() translation by (you can also view the original English article)

La persistencia de datos entre los lanzamientos de aplicaciones es un requisito que tienen la mayoría de las aplicaciones de iOS, desde el almacenamiento de las preferencias del usuario en el sistema predeterminado hasta la administración de grandes conjuntos de datos en una base de datos relacional. En este artículo, exploraremos las estrategias más comunes utilizadas para almacenar datos en una aplicación de iOS. También hablaré sobre el sistema de archivos en iOS y cómo el sandboxing de la aplicación afecta la persistencia de los datos.

Introducción

Has recorrido un largo camino, saltamontes, y has aprendido mucho. Pero hay un aspecto vital del desarrollo de iOS que aún no hemos discutido, la persistencia de los datos. Prácticamente todas las aplicaciones de iOS almacenan datos para su uso posterior. Los datos que almacena su aplicación pueden ser cualquier cosa, desde preferencias del usuario hasta cachés temporales o incluso grandes conjuntos de datos relacionales.

Antes de discutir las estrategias de persistencia de datos más comunes que los desarrolladores tienen en la plataforma iOS, voy a dedicar unos minutos a debatir sobre el sistema de archivos y el concepto de sandboxing de aplicaciones. ¿De verdad crees que podrías almacenar los datos de tu aplicación donde quieras en el sistema de archivos? Piensa otra vez, padawan. 

Sistema de archivos y aplicación de espacio aislado

La seguridad en la plataforma iOS ha sido una de las principales prioridades de Apple desde que se introdujo el iPhone en 2007. A diferencia de las aplicaciones OS X, una aplicación iOS se coloca en un entorno limitado de aplicaciones. La zona de pruebas de una aplicación no solo se refiere al directorio de la zona de pruebas de una aplicación en el sistema de archivos. También incluye acceso controlado y limitado a los datos del usuario almacenados en el dispositivo, servicios del sistema y hardware.

Con la presentación de la Mac App Store, Apple también ha comenzado a aplicar el sandboxing de aplicaciones en OS X. Aunque las restricciones impuestas a las aplicaciones OS X no son tan estrictas como las que se aplican a las aplicaciones iOS, el concepto básico es similar. Hay diferencias, sin embargo. El entorno limitado de la aplicación de una aplicación iOS, por ejemplo, contiene el paquete de aplicaciones, que no es cierto para las aplicaciones OS X. Las razones de estas diferencias son principalmente históricas.

Sandboxing y directorios

El sistema operativo instala cada aplicación de iOS en un directorio de espacio aislado que contiene el directorio del conjunto de aplicaciones y tres directorios adicionales, Documentos, Biblioteca y tmp. Se puede acceder al directorio sandbox de la aplicación, a menudo denominado directorio principal, llamando a una función Foundation simple, NSHomeDirectory ().

1
print(NSHomeDirectory())

Puedes probar esto tú mismo. Cree un nuevo proyecto de Xcode basado en la plantilla de aplicación de vista única y asígnele el nombre Persistencia de datos.

Project SetupProject SetupProject Setup

Abra AppDelegate.swift y agregue el fragmento de código anterior a la aplicación (_: didFinishLaunchingWithOptions :). Si ejecuta la aplicación en el simulador, la salida en la consola debería verse más o menos así:

1
/Users/Bart/Library/Developer/CoreSimulator/Devices/14F00EFB-2EAB-438C-B401-7FEFDA1D94AB/data/Containers/Data/Application/81B23594-3BA2-4AF9-B91A-F74A53FD6945

Sin embargo, si ejecuta la aplicación en un dispositivo físico, la salida se ve un poco diferente, como puede ver a continuación. La zona de pruebas de la aplicación y las limitaciones impuestas son idénticas, sin embargo. 

1
/var/mobile/Containers/Data/Application/41E7939B-6A39-4005-9C28-372FD9C7AD99

Recuperar la ruta al directorio Documentos de la aplicación requiere un poco más de trabajo, como puede ver en el siguiente fragmento de código.

1
let directories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
2
3
if let documents = directories.first {
4
    print(documents)
5
}

Invocamos la función NSSearchPathForDirectoriesInDomains (), que se define en el marco de Foundation. Como primer argumento, pasamos en DocumentDirectory del tipo NSSearchPathDirectory para indicar que solo estamos interesados ​​en el directorio Documents de la aplicación. El segundo y tercer argumento son de menor importancia para nuestra discusión. La función devuelve una matriz de tipo [String], que contiene un resultado, la ruta al directorio de Documentos de la aplicación.

¿Por qué Sandboxing?

¿Cuál es el beneficio de sandboxing? La razón principal para las aplicaciones de sandboxing es la seguridad. Al limitar las aplicaciones a su propio entorno limitado, las aplicaciones comprometidas no pueden causar daños al sistema operativo u otras aplicaciones.

Por aplicaciones comprometidas, me refiero tanto a las aplicaciones que han sido pirateadas, como a las aplicaciones intencionalmente maliciosas, como a las aplicaciones que contienen errores críticos que pueden causar daños inadvertidamente.

Aunque las aplicaciones se encuentran en la zona de pruebas de la plataforma iOS, las aplicaciones pueden solicitar acceso a ciertos archivos o activos que se encuentran fuera del entorno limitado de la aplicación a través de varias interfaces del sistema. Un ejemplo de esto es la biblioteca de música almacenada en un dispositivo. Sin embargo, sepa que los marcos del sistema están a cargo de cualquier operación relacionada con el acceso a archivos.

¿Qué va a dónde?

Aunque puede hacer prácticamente todo lo que desee en el entorno limitado de su aplicación, Apple ha proporcionado algunas pautas con respecto a lo que debe almacenarse. Es importante conocer estas pautas por varias razones. Cuando se realiza una copia de seguridad de un dispositivo iOS en su computadora o en iCloud, no todos los archivos en la caja de arena se incluyen en la copia de seguridad.

El directorio tmp, por ejemplo, solo debe usarse para almacenar archivos temporalmente. El sistema operativo puede vaciar este directorio en cualquier momento, por ejemplo, cuando el dispositivo tiene poco espacio en disco. El directorio tmp no está incluido en las copias de seguridad.

El directorio de documentos está destinado a los datos del usuario, mientras que el directorio de la biblioteca se utiliza para datos de aplicaciones que no están estrictamente vinculados al usuario. El directorio Caches en el directorio de la Biblioteca es otro directorio del que no se realiza una copia de seguridad.

También tenga en cuenta que su aplicación no debe modificar los contenidos del directorio del paquete de aplicaciones. El directorio del paquete de aplicaciones se firma cuando se instala la aplicación.  Al modificar los contenidos del directorio del paquete de aplicaciones de cualquier manera, la firma mencionada se altera, lo que significa que el sistema operativo no permite que la aplicación se inicie nuevamente.  Esta es otra medida de seguridad implementada por Apple para proteger a los clientes. 

Opciones de persistencia de datos

Existen varias estrategias para almacenar datos de aplicaciones en el disco. En este artículo, damos un breve vistazo a cuatro enfoques comunes en iOS: 

  • sistema predeterminado 
  • listas de propiedades
  • SQLite
  • Datos principales

Las opciones descritas en este artículo no deben considerarse intercambiables. Cada estrategia tiene beneficios e inconvenientes. Comencemos echando un vistazo al sistema predeterminado.

Valores predeterminados del usuario

El sistema predeterminado es algo que iOS hereda de OS X. Aunque fue creado y diseñado para almacenar las preferencias del usuario, se puede usar para almacenar cualquier tipo de datos siempre que sea un tipo de lista de propiedades, NSString, NSNumber, NSDate, NSArray , NSDictionary y NSData, o cualquiera de sus variantes mutables.

¿Qué pasa con los tipos de datos de Swift? Afortunadamente, Swift es lo suficientemente inteligente. Puede almacenar cadenas y números convirtiéndolos a NSString y NSNumber. Lo mismo se aplica a las matrices y diccionarios Swift.

El sistema predeterminado no es más que una colección de listas de propiedades, una lista de propiedades por aplicación. La lista de propiedades se almacena en una carpeta de Preferencias en la carpeta Biblioteca de la aplicación, haciendo alusión al propósito y función de la lista de propiedades.

Una de las razones por las que los desarrolladores prefieren el sistema predeterminado es porque es muy fácil de usar. Eche un vistazo al siguiente ejemplo para ver a qué me refiero.

1
let userDefaults = NSUserDefaults.standardUserDefaults()
2
3
// Setting Values

4
userDefaults.setBool(true, forKey: "Key1")
5
userDefaults.setInteger(123, forKey: "Key2")
6
userDefaults.setObject("Some Object", forKey: "Key3")
7
userDefaults.setObject([1, 2, 3, 4], forKey: "Key4")
8
9
// Getting Values

10
userDefaults.boolForKey("Key1")
11
userDefaults.integerForKey("Key2")
12
userDefaults.objectForKey("Key3")
13
userDefaults.objectForKey("Key4")
14
15
userDefaults.synchronize()

Al llamar a standardUserDefaults () en NSUserDefaults, se devuelve una referencia al objeto predeterminado compartido.

En la última línea, llamamos a synchronize () en el objeto predeterminado compartido para escribir cualquier cambio en el disco. Rara vez es necesario invocar synchronize (), porque el sistema predeterminado guarda los cambios cuando sea necesario. Sin embargo, si almacena o actualiza una configuración usando el sistema predeterminado, algunas veces puede ser útil o necesario guardar los cambios en el disco de manera explícita.

A primera vista, el sistema predeterminado parece ser nada más que un almacén de valores clave ubicado en una ubicación específica. Sin embargo, la clase NSUserDefaults, definida en el marco de Foundation, es más que una interfaz para administrar un almacén de clave-valor. Eche un vistazo a su referencia de clase para más información.

Antes de continuar, pegue el fragmento de código anterior en el método de la aplicación del delegado de aplicación (_: didFinishLaunchingWithOptions :) y ejecute la aplicación en el simulador. Abra una nueva ventana del Finder y navegue a Library>Developer>CoreSimulator>Devices><DEVICE_ID>data>Containers>Data>Application><APPLICATION_ID>.

<DEVICE_ID> y <APPLICATION_ID>  son dos identificadores únicos para el simulador y su aplicación, respectivamente. La ubicación del entorno limitado de la aplicación para el simulador depende de la versión de Xcode que esté utilizando. Si no está utilizando Xcode 7, la ruta probablemente será diferente.

Puede hacer su vida más fácil al imprimir la ruta al directorio de inicio de su aplicación a la consola de Xcode. Agregue la siguiente instrucción de impresión a la aplicación (_: didFinishLaunchingWithOptions :)

1
print(NSHomeDirectory())

La carpeta con nombre críptico es el directorio sandbox de la aplicación. En el directorio de la zona de pruebas de la aplicación, abra la carpeta Preferencias, ubicada en la carpeta Biblioteca, e inspeccione su contenido

Application SandboxApplication SandboxApplication Sandbox

Debería ver una lista de propiedades con un nombre idéntico al identificador de paquete de la aplicación. Esta es la tienda predeterminada de usuario para su aplicación. Así es como se ve la lista de propiedades en un editor de texto. Como puede ver, la lista de propiedades se almacena en el disco como un archivo XML.

1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
<plist version="1.0">
4
<dict>
5
    <key>Key1</key>
6
  <true/>
7
	<key>Key2</key>
8
	<integer>123</integer>
9
	<key>Key3</key>
10
	<string>Some Object</string>
11
	<key>Key4</key>
12
	<array>
13
		<integer>1</integer>
14
		<integer>2</integer>
15
		<integer>3</integer>
16
		<integer>4</integer>
17
	</array>
18
</dict>
19
</plist>

Si desea facilitar el acceso al recinto de seguridad de una aplicación en el simulador, le recomiendo que eche un vistazo a SimPholders. Es una pequeña utilidad que hace que trabajar con el simulador sea mucho, mucho más fácil.

Listas de propiedades

Ya hemos cubierto las listas de propiedades de esta serie. Como cuestión de hecho, el almacén de respaldo de la base de datos predeterminada del usuario es una lista de propiedades. Usar listas de propiedades es una estrategia conveniente para almacenar y recuperar un gráfico de objetos. Las listas de propiedades han existido durante años, son fáciles de usar y, por lo tanto, son una excelente opción para almacenar datos en una aplicación de iOS.

Como mencioné anteriormente, es importante tener en cuenta que una lista de propiedades solo puede almacenar datos de listas de propiedades. ¿Esto significa que no es posible almacenar objetos de modelo personalizados utilizando listas de propiedades? Eso es posible. Sin embargo, los objetos del modelo personalizado deben archivarse, una forma de serialización, antes de que puedan almacenarse en una lista de propiedades. Archivar un objeto simplemente significa que el objeto se debe convertir a un tipo de datos que se pueda almacenar en una lista de propiedades, como una instancia de NSData.

Archivar objetos

¿Recuerda el protocolo NSCoding definido en el marco de Foundation? El protocolo NSCoding define dos métodos, init (codificador :) y encodeWithCoder (_ :). Una clase implementa estos métodos para permitir que las instancias de la clase sean codificadas y decodificadas.

La codificación y la decodificación son los mecanismos subyacentes para el archivo y la distribución de objetos. Cómo funciona el archivado de objetos se aclarará un poco más adelante en esta serie. En esta lección, solo te muestro cómo escribir matrices y diccionarios en el disco usando listas de propiedades.

Escribir en archivo

El siguiente fragmento de código debería darle una idea de lo fácil que es escribir una matriz o diccionario en el disco. En teoría, el gráfico de objetos almacenado en una lista de propiedades puede ser tan complejo o tan grande como desee. Sin embargo, tenga en cuenta que las listas de propiedades no están destinadas a almacenar decenas o cientos de megabytes de datos, el intentar usarlos de esa manera probablemente resultará en un rendimiento degradado.

1
let directories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
2
3
if let documents = directories.first {
4
    if let urlDocuments = NSURL(string: documents) {
5
        let urlFruits = urlDocuments.URLByAppendingPathComponent("fruits.plist")
6
        let urlDictionary = urlDocuments.URLByAppendingPathComponent("dictionary.plist")
7
        
8
        // Write Array to Disk

9
        let fruits = ["Apple", "Mango", "Pineapple", "Plum", "Apricot"] as NSArray
10
        let dictionary = ["anArray" : fruits, "aNumber" : 12345, "aBoolean" : true] as NSDictionary
11
        
12
        fruits.writeToFile(urlFruits.path!, atomically: true)
13
        dictionary.writeToFile(urlDictionary.path!, atomically: true)
14
        
15
        // Load from Disk

16
        let loadedFruits = NSArray(contentsOfURL: urlFruits)
17
        if let fruits = loadedFruits {
18
            print(fruits)
19
        }
20
        
21
        let loadedDictionary = NSDictionary(contentsOfURL: urlDictionary)
22
        if let dictionary = loadedDictionary {
23
            print(dictionary)
24
        }
25
    }
26
}

Echemos un vistazo al fragmento de código anterior. Comenzamos por almacenar una referencia a una matriz literal en una variable llamada frutas. Creamos la URL del archivo para almacenar la lista de propiedades que vamos a crear. La URL del archivo se crea al anexar una cadena a la URL del archivo del directorio de Documentos. La cadena que agregamos es el nombre de la lista de propiedades que creamos en un segundo, incluida su extensión, .plist.

Escribir la matriz en el disco es tan fácil como llamar a writeToFile (_: atómico :) en la matriz. Puede ignorar la bandera atómica por ahora. Como lo ilustra el ejemplo, escribir un diccionario en un disco sigue un patrón similar. El ejemplo también ilustra cómo crear matrices y diccionarios a partir de una lista de propiedades, pero esto es algo que ya hemos tratado anteriormente en esta serie. Ejecute la aplicación en el simulador y navegue hasta el directorio de Documentos de la aplicación. En este directorio, debería ver las dos listas de propiedades que acabamos de crear.

Writing Objects to a Property ListWriting Objects to a Property ListWriting Objects to a Property List

Así es como se ve la lista de propiedades del diccionario cuando lo abre en un editor de texto.

1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
<plist version="1.0">
4
<dict>
5
    <key>aBoolean</key>
6
	<true/>
7
	<key>aNumber</key>
8
	<integer>12345</integer>
9
	<key>anArray</key>
10
	<array>
11
		<string>Apple</string>
12
		<string>Mango</string>
13
		<string>Pineapple</string>
14
		<string>Plum</string>
15
		<string>Apricot</string>
16
	</array>
17
</dict>
18
</plist>

SQLite

Si su aplicación es impulsada por datos y funciona con grandes cantidades de datos, entonces es posible que desee examinar SQLite. ¿Qué es SQLite? El eslogan en el sitio web de SQLite dice "Pequeño, rápido, confiable, elija tres", que lo resume muy bien.

SQLite es una biblioteca que implementa una base de datos relacional incrustada ligera. Como su nombre lo indica, está basado en el estándar SQL (Structured Query Language) al igual que MySQL y PostgreSQL.

La principal diferencia con otras bases de datos SQL es que SQLite es portátil y muy liviano. En lugar de un proceso separado al que se accede desde la aplicación cliente, SQLite no tiene servidor En otras palabras, está integrado en la aplicación o administrado por el sistema en el que se ejecuta la aplicación, lo que significa que es muy rápido.

El sitio web de SQLite afirma que es la base de datos SQL más ampliamente implementada. No sé si ese es todavía el caso, pero sin duda es una opción popular para el almacenamiento de datos del lado del cliente. La ventaja de SQLite sobre trabajar directamente con objetos es que SQLite es mucho, mucho más rápido. Esto se debe en gran parte a cómo las bases de datos relacionales y los lenguajes de programación orientados a objetos difieren fundamentalmente.

Para cerrar la brecha entre SQLite y Objective-C, se han creado varias soluciones de mapeo relacional de objetos (ORM) a lo largo del tiempo. El ORM que Apple ha creado para iOS y OS X se llama Core Data, que veremos más adelante en esta lección.

FMDB de Flying Meat

Usar SQLite en iOS significa trabajar con una biblioteca basada en C. Si prefiere una solución orientada a objetos, le recomiendo encarecidamente el envoltorio Objective-C de Gus Mueller (Flying Meat, Inc.) para SQLite, FMDB.

La biblioteca es muy eficiente. He usado FMDB en el pasado y he estado muy contento con su API y la solidez y confiabilidad de la biblioteca.

Datos principales

Los desarrolladores nuevos en Core Data a menudo confunden Core Data con una base de datos mientras que realmente es una solución de mapeo relacional de objetos creada y mantenida por Apple. Matt Gallagher ha escrito una excelente publicación sobre las diferencias entre Core Data y una base de datos. Core Data proporciona un modelo relacional orientado a objetos que se puede serializar en una tienda XML, binaria o SQLite. Core Data incluso tiene soporte para tiendas en memoria.

¿Por qué debería usar Core Data en lugar de SQLite? Al hacer esta pregunta, asume erróneamente que Core Data es una base de datos. La ventaja de utilizar Core Data es que trabaja con objetos en lugar de datos sin procesar, como filas en una base de datos SQLite o datos almacenados en un archivo XML. A pesar de que Core Data ha tenido algunos años difíciles cuando se lanzó por primera vez, se ha convertido en un sólido marco con muchas características, que incluyen migraciones automáticas, seguimiento de cambios, fallas y validación integrada.

Otra gran característica que muchos desarrolladores aprecian es el editor de modelos Core Data, integrado en Xcode. El editor permite a los desarrolladores modelar el modelo de datos de la aplicación a través de una interfaz gráfica.

Core Data Model EditorCore Data Model EditorCore Data Model Editor

Si Core Data es la solución adecuada para su aplicación, depende de los datos que planea administrar, tanto en términos de la cantidad de datos como del modelo subyacente. Si planea administrar conjuntos de datos muy grandes, Core Data podría convertirse en un cuello de botella de rendimiento a lo largo del tiempo. En ese caso, SQLite puede ser una mejor solución.

iCloud

Probablemente hayas oído hablar de iCloud y te estarás preguntando dónde encaja iCloud en la historia de la persistencia de los datos. A diferencia de SQLite y Core Data, iCloud no es una forma de persistencia de datos. En cambio, es una plataforma o servicio para hacer que los datos del usuario estén disponibles en múltiples dispositivos y múltiples instancias de una aplicación, o incluso en una familia de aplicaciones.

La plataforma iCloud abarca varios servicios o componentes. El componente que nos interesa es iCloud Storage, que incluye cuatro tipos de almacenamiento:

  • CloudKit
  • almacenamiento de clave-valor
  • almacenamiento de documento
  • Almacenamiento de datos centrales

Si desea leer más sobre iCloud Storage, le recomiendo leer mi serie sobre iCloud Storage.

Conclusión

Ahora debe tener una buena idea de las opciones que tiene para almacenar datos al desarrollar para la plataforma iOS. Tenga en cuenta que no todas las estrategias que hemos cubierto son iguales.

Esta serie está llegando a su fin lentamente. En las próximas dos entregas, crearemos otra aplicación para poner en práctica lo que hemos aprendido hasta ahora. La mejor forma de aprender es haciendo.

Si tiene preguntas o comentarios, puede dejarlos en los comentarios a continuación o comunicarse conmigo en Twitter.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.