Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Tal como dije en mi ltimo artculo del 2015, una de mis prioridades de 2016 es aprender Swift. Creo que lo mejor para eso es ir
registrando en mi blog lo que voy descubriendo y aprendiendo. Seguro que a alguien podr serle de utilidad y adems a mi me
ayudar a asentar los conocimientos que vaya adquiriendo.
Esta serie de artculos que empieza aqu tratar de Swift y de lo que ir aprendiendo.
Qu es un opcional?
La primera vez que intent hacer algo con Swift, una de las primeras cosas que me ech para atrs fueron los opcionales. En
ese momento, no entend ni su significado, ni su funcionamiento ni mucho menos su utilidad. No estaba concentrado
posiblemente y al ver ese nuevo concepto que para mi era completamente nuevo, decid dejar de lado Swift y programar mi app
con el viejo Objective C.
Empecemos con un ejemplo:
1 var nombre: String?
nombre es una variable del tipo String?, es decir Optional. No es un String! Esta variable puede contener un valor o no.
No, esto no es el experimento del gato de Schrdinger.
Para utilizar esta variable, primero debemos extraer (unwrap) su valor. No es posible realizar ninguna accin que se realiza con
un String antes de realizar el unwrap.
Por supuesto, es un error definir una constante como un opcional utilizando let. Una constante no puede ser de un tipo
opcional, ya que una constante slo puede contener un valor. Un opcional puede contener algo o puede ser nil. Definir un
opcional como una constante provocar un error en tiempo de compilacin.
Cmo se utiliza un opcional?
Existen varias tcnicas para utilizar un opcional. No puedo traducir la mayora de las expresiones porque la verdad no sabra
traducir algunas palabras. De todas formas, la mayor parte de la documentacin de Swift (y de programacin en general) est en
ingls.
Conditional binding
1 var nombre: String?
2 if let nombreExtraido = nombre {
3
print("nombre: \(nombreExtraido)")
4 } else {
5
print("nombre es nil.")
6 }
Este ejemplo muestra como extraer el valor utilizando una tcnica llamada Conditional binding. Primero asignamos la variable a
una constante y si esta constante no es nil, la utilizamos. En caso de ser nil, mostramos un mensaje indicando que la variable
es nil. Esta manera es la que ms he utilizado hasta hoy sin duda.
Forced unwrapping
1 var nombre: String?
2 nombre = "Miguel"
3 print("nombre: \(nombre!)")
Esta tcnica se utiliza slo cuando ests completamente seguro de que la variable nombre tiene efectivamente un valor. Al
utilizar el modificador ! ests forzando la extraccin de la variable. Esto slo funciona si la variable tiene un valor. Si la variable
no tiene valor, esto provocara un error en tiempo de ejecucin. Esta tcnica puede resultar peligrosa por lo tanto.
Nil coalescing
1 var nombre: String?
2 let nombreNuevo = nombre ?? "Miguel"
3 print("nombreNuevo: \(nombreNuevo)")
Vaya nombrecito Esta tcnica me recuerda al operador condicional y ternario ? de php. Consiste en algo muy parecido.
Comprueba si el opcional tiene un valor y si es as se lo asigna a tu constante, si no tiene valor, asigna el valor que t indiques.
En el caso de mi ejemplo, la constante nombreNuevo contendr el valor de la variable nombre slo si esta contiene un valor.
En cambio si no contiene nada nil, la constante nombreNuevo contendr el valor Miguel. En los dos casos, la constante
resultante ser del tipo String y no ser un opcional.
Optional chaining
La ltima tcnica para extraer un valor de un opcional es utilizando Optional chaining. Esta tcnica es muy utilizada cuando ests
class Post {
var autor: Persona?
}
class Persona {
var nombre: String?
}
var p: Post?
[...] // Aqu ocurren cosas maravillosas.
let nombreAutor = p?.autor?.nombre
print("nombreAutor: \(nombreAutor)")
nombreAutor contendr el nombre del autor del artculo p slo si se cumplen las siguientes condiciones:
p contiene un valor y no es nil. Este valor debe ser un objeto del tipo Post.
El autor de p contiene un valor y no es nil. Este valor debe ser un objeto del tipo Persona.
El nombre del autor contiene un valor y no es nil. Este valor debe ser un String.
En lugar de las [], escribo el siguiente cdigo:
1
2
3
4
5
Primero, defino una variable a del tipo opcional Autor. Despus inicializo p con el constructor por defecto. Ahora mismo p
ya no es nil, contiene un valor, pero el autor de p sigue siendo nil. Por lo tanto, nombreAutor seguir siendo nil.
Despus inicializo el autor a con el constructor por defecto de Persona. Ahora, a ya no es nil, pero el nombre sigue
sindolo. Por lo tanto, la ltima condicin sigue sin cumplirse as que nombreAutor sigue siendo nil.
Finalmente, asigno la cadena Miguel a la propiedad nombre de a. Por lo tanto, ahora s, el valor de nombreAutor ser
Miguel.
El cdigo todo junto:
1
2
3
4
5
6
7
8
9
1
0
1
1
1
2
1
3
1
4
1
5
1
6
1
7
1
8
class Post {
var autor: Persona?
}
class Persona {
var nombre: String?
}
var p: Post? // p es nil.
var a: Autor? // a es nil.
p = Post() // p ya no es nil.
a = Persona() // a ya no es nil.
p?.autor = a // el autor de p ya no es nil, es a.
a?.nombre = "Miguel" // el nombre del autor a ya no es nil, es "Miguel"
let nombreAutor = p?.autor?.nombre
print("nombreAutor: \(nombreAutor)")
class Post {
var autor: Persona
init(autor: Persona) {
self.autor = autor
}
}
class Persona {
var nombre: String = ""
}
1
1
1
2
1
3
1
4
1
5
1
6
1
7
1
8
var a = Persona()
var p = Post(autor: a)
p.autor = a
a.nombre = "Miguel"
let nombreAutor = p.autor.nombre
En este caso, estoy obligado a definir valores por defecto o constructores ya que no puedo usar nil al no usar opcionales.
Cuando creo un objeto Post, tengo que pasarle el objeto Persona. Si se puede dar el caso de que un objeto Post no tenga autor,
entonces no puedo asignarlo a nil. Para utilizar nil, tengo que utilizar opcionales.
En Objective-C, lo que hacas era comprobar si tu variable era nil antes de utilizarlo o no? No! En Objective-C, podas
programar sin preocupaciones hasta que un buen da tu app dejaba de funcionar porque la variable que t pensabas deba
contener algo, contiene nil. S sincero y reconoce que has pasado horas y horas mirando tu cdigo sin entender por qu haba
fallado esto, hasta que te das cuenta que existe un remoto caso que t no habas previsto en el que esa variable que t
pensabas que tena que traer siempre un valor, de repente es nil. Los opcionales terminan con todo esto al menos en parte. Al
tener que extraer el valor de las variables no podrs tener ese error. Slo si te gusta vivir peligrosamente utilizando el mtodo
implcito podrs seguir cometiendo esos mismos errores.
http://donmik.com/diario-swift-dia-1-opcionales/
lenguaje nativo es PHP as que he tratado de buscar similitudes entre Swift y PHP
Conoc Core Data cuando empec con Objective C. Poco ha cambiado con Swift y lo he agradecido porque la primera vez que
me met con Core Data, me cost adaptarme a ello.
NSManagedObjectModel
Es el modelo de datos. Representa cada objeto de nuestro modelo con sus propiedades, relaciones, Para dejarlo claro, es
como el esquema de una base de datos de MySQL.
En el proyecto de Xcode, tendrs un archivo con la extensin .xcdatamodeld que contendr el modelo de datos. Aqu se
pueden crear entidades con atributos y relaciones entre ellas.
NSPersistentStore
Es el almacenamiento que utiliza nuestro stack. Se pueden escoger cuatro tipos diferentes de almacenamiento:
NSQLiteStoreType: Una base de datos SQLite. Es la opcin ms utilizada y seleccionada por defecto.
NSXMLStoreType: Un archivo XML. Slo se puede utilizar para OS X, as que tampoco me interesa.
NSBinaryStoreType: Un archivo binario de datos. Tanto este tipo como el XML, son muy poco utilizados porque requieren ser
cargados completamente en memoria antes de poder utilizarlos, lo cual requiere un consumo de recursos poco eficiente.
NSInMemoryStoreType: Un almacenamiento en memoria. Lo curioso aqu es que este tipo de almacenamiento no persiste si
cierras tu app o apagas tu dispositivo. Por lo tanto, no es de mucha utilidad.
NSPersistentStoreCoordinator
Esta clase hace de intermediaria entre el modelo de datos (NSManagedObjectModel) y el almacenamiento (NSPersistentStore).
Permite por ejemplo abstraerte del tipo de almacenamiento seleccionado. Hace las funciones de un ORM, me recuerda a
Doctrine por ejemplo que me permite abstraer el tipo de base de datos que estoy utilizando.
NSManagedObjectContext
Esta clase es la ms importante y la clase con la que trabajars da a da constantemente. Representa el contexto en el que
ests trabajando. Todas tus operaciones de lectura y escritura se desarrollan en el contexto. Los cambios que realices en el
contexto no sern almacenados en disco hasta que hagas una llamada al mtodo save().
Un objeto est fuertemente ligado a un contexto y no puede cambiar de contexto a lo largo de su ciclo de vida. Esto se convertir
en algo muy a tener en cuenta cuando utilices en tu app mltiples hilos de ejecucin y necesites acceder a un mismo objeto
desde ellos y modificarlo.
Modelo de datos
Sin complicaciones, mi modelo de datos se compone de 2 entidades con una relacin entre ellas.
Entidad: Persona
nombre String
edad Integer 32
Entidad: Marca
marca String
ano_compra Integer 32
Relacin: Una persona puede ser propietaria de muchas coches (To Many) y un coche slo tiene un propietario (To One).
Insertar
1
2
3
4
5
6
7
8
9
1
0
1
1
1
2
1
3
func insertar() {
// Insertar una persona.
// Crear un tipo de entidad: Persona.
let personaEntity = NSEntityDescription.entityForName("Persona", inManagedObjectContext: managedContext)
// Crear una entidad del tipo: personaEntity o sea Persona.
let persona = Persona(entity: personaEntity!, insertIntoManagedObjectContext: managedContext)
// Asignar valores.
persona.nombre = "Miguel"
persona.edad = 32
// Insertar un coche.
// Crear un tipo de entidad: Coche.
let cocheEntity = NSEntityDescription.entityForName("Coche", inManagedObjectContext: managedContext)
// Crear una entidad del tipo: cocheEntity o sea Coche.
let coche = Coche(entity: cocheEntity!, insertIntoManagedObjectContext: managedContext)
// Asignar valores.
coche.marca = "Ferrari"
1
4
1
5
1
6
1
7
1
8
1
9
2
0
2
1
2
2
2
3
2
4
2
5
2
6
2
7
2
8
coche.ano_compra = 2045
// Relacionar el propietario con el coche.
coche.propietario = persona
// Guardar en disco.
do {
try managedContext.save()
} catch let error as NSError {
print("Error al insertar: \(error)")
}
}
El cdigo no es muy complicado pero intuitivo, intuitivo no es. Primero, debes insertar en el contexto una nueva entidad del tipo
que necesitas. Por eso, necesitas el primer paso para crear el tipo de entidad que necesitas, luego insertas un nuevo
NSManagedObject de ese tipo. Finalmente, debes llamar al mtodo save() para que tu contexto termine guardando los cambios
en disco, sino ninguno de tus cambios permanecer en el siguiente arranque de la app.
Recuperar
El siguiente paso es recuperar los datos de esa persona que acabo de crear y su coche. As de paso, compruebo que he
insertado algo realmente en la base de datos y no estoy haciendo esto para nada.
1
2
3
4
5
6
7
8
9
1
0
1
1
1
2
1
3
1
4
1
5
1
6
1
7
1
8
1
9
2
0
2
1
2
2
2
3
2
4
2
5
2
6
2
7
2
8
Parece que s que hay datos por lo tanto, est funcionando todo correctamente.
Para recuperar informacin, he utilizado NSFetchRequest con un simple predicado. No voy a entrar en detalles en este artculo
con esta clase pero es donde reposa toda la dificultad y la utilidad de Core Data. Con esta clase, podrs recuperar la informacin
de tu base de datos. Tiene muchas posibilidades, podrs utilizar predicados con NSPredicate para filtrar tus datos, podrs
ordenarlos utilizando NSSortDescriptor, limitar la cantidad de datos,
En otro artculo, tratar de hablar de todo lo que se puede hacer con NSFetchRequest. Bueno, quizs no todo, pero s muchas
cosas.
Modificar
1
2
3
4
5
6
7
8
9
1
0
1
1
1
2
1
3
1
4
1
5
1
6
1
7
1
8
1
9
2
0
2
1
2
2
// Asignar valores.
coche.marca = "Seat"
coche.ano_compra = 1989
// Relacionar el propietario con el coche.
coche.propietario = p
do {
try managedContext.save()
} catch let error as NSError {
print("Error al modificar: \(error)")
}
}
Posiblemente, de lo ms sencillo, una vez que tienes el objeto en una variable. Modificarlo es coser y cantar.
Eliminar
1
2
3
4
5
6
7
8
9
1
0
Igual que con modificar, si tienes el objeto recuperado, eliminarlo es muy sencillo. Puedes ver en la consola que ya no hay
personas en la base de datos, por lo tanto ha sido eliminada correctamente.
import UIKit
import CoreData
class ViewController: UIViewController {
var managedContext: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Insertar persona y coche.
insertar()
// Recuperar persona.
if let p = recuperarPersona("Miguel") {
// Modificar persona.
modificar(p)
}
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
try managedContext.save()
} catch let error as NSError {
print("Error al insertar: \(error)")
}
}
func recuperarPersona(nombre: String?) -> Persona? {
let fetchRequest = NSFetchRequest(entityName: "Persona")
if let nombre = nombre {
fetchRequest.predicate = NSPredicate(format: "nombre == %@", nombre)
}
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
if results.count > 0 {
let p = results.first! as! Persona
print("Encontrada una persona: \(p.nombre!) - \(p.edad!) con \
(p.coches!.count) coche.")
if let coches_de_p = p.coches {
for c in coches_de_p {
let c = c as! Coche
print("Coche de \(p.nombre!): \(c.marca!) - \(c.ano_compra!)")
}
}
return p
} else {
print("No hay personas.")
return nil
}
} catch let error as NSError {
print("Error al recuperar: \(error)")
}
return nil
}
func modificar(p: Persona) {
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
http://donmik.com/diario-swift-dia-2-core-data/