iOS desde cero con Swift: Explorando el Framework Foundation
() translation by (you can also view the original English article)
El framework Foundation es el pan y la mantequilla en la caja de herramientas de un desarrollador de iOS. Proporciona la clase raíz de NSObject
y una gran cantidad de bloques de construcción fundamentales para el desarrollo de iOS, desde clases para números y cadenas hasta matrices y diccionarios. El framework Foundation puede parecer un poco aburrido al principio, pero aprovecha una gran cantidad de energía y es indispensable para el desarrollo de aplicaciones de iOS.
Una palabra sobre la base de Foundation
En el artículo anterior, mencioné brevemente el Core de Foundation y su relación con Foundation. Aunque no utilizaremos explícitamente el framework Core Foundation en el resto de esta serie, es útil familiarizarse con el framework y saber cómo difiere de su hermano orientado a objetos, Foundation, que utilizarás ampliamente.
Mientras que el framework Foundation se implementa en Objective-C, el framework Core Foundation se basa en C. A pesar de esta diferencia, el framework Core Foundation implementa un modelo de objetos limitado. Este modelo de objetos permite la definición de una colección de tipos opacos a los que a menudo se hace referencia como objetos, a pesar de que, estrictamente hablando, no son objetos.
El objetivo principal de ambos frameworks es similar, lo que permite compartir datos y códigos entre las distintas bibliotecas y frameworks . Core Foundation también incluye soporte para la internacionalización. Un componente clave de este soporte se proporciona a través del tipo opaco CFString
, que administra de manera eficiente una matriz de caracteres Unicode.
Como mencioné anteriormente, el puente gratuito cierra literalmente la brecha entre ambos frameworks al permitir la sustitución de objetos Cocoa por objetos Core Foundation en parámetros de funciones y viceversa.
Es importante tener en cuenta que el recuento automático de referencias (ARC) no administra los "objetos" de Core Foundation. Esto significa que eres responsable de administrar la memoria cuando trabajas con Core Foundation. Mike Ash escribió un excelente artículo sobre el conteo automático de referencias y cómo usar ARC con Core Foundation y el puente sin cargo.
Visita la referencia del framework Apple's Core Foundation si deseas obtener más información sobre el framework y una lista completa de los tipos opacos del framework .
Práctica, práctica, práctica
Aprender una nueva habilidad se hace mejor a través de la práctica. Comencemos creando un playground en Xcode. Nombra el playground Foundation configura la Plataforma en iOS. Haz clic en Siguiente para continuar. Dile a Xcode dónde quieres guardar el playground y haz clic en Crear.



Este es el aspecto que debería tener el contenido de playground .
1 |
//: Playground - noun: a place where people can play
|
2 |
|
3 |
import UIKit |
4 |
|
5 |
var str = "Hello, playground" |
El playground contiene un comentario en la parte superior, una declaración de importación para el framework UIKit y una declaración de variables. A la derecha, puedes ver la salida del playground.
Ya que trabajaremos con el framework Foundation, tenemos que reemplazar la declaración de importación de UIKit con una declaración de importación para Foundation. Elimina el comentario en la parte superior, actualiza la declaración de importación y elimina la declaración de la variable.
1 |
import Foundation |
Foundation
The Framework Foundation es mucho más que una colección de clases para trabajar con números, cadenas y colecciones (matrices, diccionarios y conjuntos). También define docenas de protocolos, funciones, tipos de datos y constantes.
En el resto de este artículo, me centraré principalmente en las clases que usarás más a menudo al desarrollar aplicaciones de iOS. Sin embargo, también hablaré brevemente sobre tres protocolos clave definidos por el framework Foundation, NSObject
, NSCoding
y NSCopying
.
Protocolos
Varios idiomas, como Perl, Python y C ++, brindan soporte para herencia múltiple. Esto significa que una clase puede descender de una subclase de más de una clase.
Aunque Swift y Objective-C no brindan soporte para herencia múltiple, admiten herencia múltiple a través de la especificación en forma de protocolos. Ya hemos cubierto los protocolos anteriormente en esta serie, pero hay un detalle importante que aún no hemos discutido.
Métodos opcionales y requeridos
Recuerda que se requiere cada método de un protocolo Swift. Si un tipo conforme a un protocolo Swift no implementa cada propiedad y método definido por el protocolo, el compilador emitirá un error. Esto no es cierto para los protocolos de Objective-C. En Objective-C, los métodos de un protocolo se pueden marcar como opcionales. Si un método de protocolo es opcional, no se requiere el tipo conforme al protocolo para implementar el método.
Debido a que la gran mayoría de los frameworks del SDK de iOS están escritos en Objective-C, conoceremos varios protocolos de Objective-C que definen métodos opcionales. Lo que es importante recordar es que se requieren todas las propiedades y métodos de un protocolo Swift.
Beneficios
Los beneficios de los protocolos son múltiples. Cuando una clase adopta o cumple un protocolo, se espera que la clase implemente los métodos declarados por el protocolo. Para algunos de ustedes, los protocolos pueden ser una reminiscencia de las interfaces en Java. Esto significa que se puede usar un protocolo para declarar la interfaz a un objeto sin revelar el tipo de objeto.
La herencia múltiple tiene sus beneficios, pero sin duda tiene sus desventajas. La ventaja de los protocolos es que los tipos, enumeraciones, clases y estructuras no relacionados aún pueden compartir un comportamiento similar mediante el uso de protocolos.
NSObject
Además de la clase raíz NSObject
, el framework Foundation también define el protocolo NSObject
. En Swift, sin embargo, las clases y los protocolos no pueden tener el mismo nombre. Como resultado, el protocolo NSObject
se conoce como NSObjectProtocol
en Swift.
A los objetos que se ajustan al protocolo NSObjectProtocol
se les puede preguntar acerca de su clase y superclase, se pueden comparar con otros objetos y responder a self
. Este es solo un pequeño subconjunto del comportamiento agregado a los objetos que se ajustan al protocolo NSObjectProtocol
.
NSCoding
Los objetos que se ajustan al protocolo NSCoding
se pueden codificar y decodificar. Esto es necesario para los objetos que necesitan ser archivados o distribuidos. El archivo de objetos, por ejemplo, se lleva a cabo cuando un objeto o gráfico de objetos se almacena en el disco.
NSCopying
El protocolo NSCopying
declara solo un método, copyWithZone(_:)
. Si una clase admite la copia de objetos, debe cumplir con el protocolo NSCopying
. La copia de un objeto se realiza enviándole un mensaje de copy()
o copyWithZone(_:)
.
Clases
NSObject
La clase NSObject
es la clase raíz de la gran mayoría de las jerarquías de clase Objective-C. ¿Por qué es tan importante si estamos interesados en Swift? Recuerda que la mayoría de los frameworks del SDK de iOS funcionan con Objective-C. Incluso si decides desarrollar una aplicación en Swift, seguirás utilizando Objective-C bajo el capó.
Al heredar de la clase raíz NSObject
, los objetos saben cómo comportarse como objetos Objective-C y cómo interactuar con el tiempo de ejecución de Objective-C. No debería sorprender que NSObject
cumpla con el protocolo NSObject/NSObjectProtocol
, que discutimos hace unos momentos.
Veamos lo que obtenemos de forma libre si una clase hereda de la clase NSObject
. Agrega el siguiente fragmento de código a tu playground. Esto debería resultar familiar para ti si has leído los artículos anteriores de esta serie. Declaramos una clase, Book
, que hereda de NSObject
. La clase Book
implementa dos métodos de instancia, chapters()
y pages()
.
1 |
class Book: NSObject { |
2 |
func chapters() { |
3 |
|
4 |
}
|
5 |
|
6 |
func pages() { |
7 |
|
8 |
}
|
9 |
}
|
Debido a que Book
hereda de NSObject
y NSObject cumple con el protocolo NSObjectProtocol
, podemos preguntarle a la clase Book
sobre su comportamiento y el árbol de herencia. Echa un vistazo al siguiente ejemplo.



Primero preguntamos acerca de la clase Book
invocando classForCoder()
en la clase Book
. El método classForCoder()
es un método de clase, lo que significa que podemos invocar este método en la clase Book
. A continuación, le preguntamos a la clase Book
acerca de su superclase o clase principal, la clase de la que hereda directamente. Esto devuelve una opción ya que no todas las clases tienen una clase principal. Como era de esperar, la superclase de Book
es NSObject
.
La siguiente línea nos dice que Book
cumple con el protocolo NSObjectProtocol
, que no es inesperado ya que hereda este rasgo de la clase NSObject
. También aprendemos que Book
no se ajusta al protocolo NSCoding
.
Parece que la clase Book
no responde al método chapters()
. Esto no es extraño, ya que chapters()
es un método de instancia, no un método de clase. Esto se demuestra en las dos últimas líneas de nuestro ejemplo en el que instanciamos una instancia de Book
y le hacemos la misma pregunta a la instancia.
En el ejemplo anterior, usamos el método respondsToSelector(_:)
, ¿pero te estarás preguntando qué es un selector? ¿Qué significa "responder a un selector"? Un selector es una palabra elegante para el nombre de una función o método. El tiempo de ejecución de Objective-C usa un selector para enviar mensajes a los objetos. Para abreviar, si lees el selector de palabras, entonces piensa en función o método. Los detalles más finos están más allá del alcance de este tutorial.
NSNumber
La clase NSNumber
es una clase de utilidad que administra cualquiera de los tipos de datos numéricos básicos. Es una subclase de la clase NSValue
, que proporciona un contenedor orientado a objetos para tipos escalares, punteros, estructuras e identificadores de objeto. La clase NSNumber
define métodos para recuperar el valor que almacena, para comparar valores y también para devolver una representación de cadena del valor almacenado.
Ten en cuenta que el valor recuperado de una instancia de NSNumber
debe ser coherente con el valor que se almacena en él. La clase NSNumber
intentará convertir dinámicamente el valor almacenado al tipo solicitado, pero no hace falta decir que existen limitaciones inherentes a los tipos de datos que NSNumber
puede gestionar.
Déjame ilustrar esto con un ejemplo. Agrega el siguiente fragmento de código a tu playground.



Creamos una nueva instancia de NSNumber
pasando un valor double
a init(double:)
. A continuación, solicitamos el valor almacenado utilizando tres métodos NSNumber
diferentes. Como puedes ver, los resultados no siempre reflejan el valor almacenado en myNumber
.
La lección es simple, ser consistente al usar NSNumber
haciendo un seguimiento del tipo que se está almacenado en la instancia de NSNumber
.
NSString
Las instancias de la clase NSString
administran una matriz de caracteres unichar
formando una cadena de texto. La diferencia sutil pero importante con una cadena C normal, que maneja caracteres char
, es que un carácter unichar
es un carácter multibyte. Esto es similar al tipo String
de Swift.
Como su nombre lo indica, un carácter unichar
es ideal para manejar caracteres Unicode. Debido a esta implementación, la clase NSString
proporciona compatibilidad inmediata con la internacionalización.
Quiero enfatizar que la cadena administrada por una instancia de NSString
es inmutable. Esto significa que, una vez que se crea la cadena, no se puede modificar. Los desarrolladores procedentes de otros idiomas, como PHP, Ruby o JavaScript, pueden verse confundidos por este comportamiento.
El framework Foundation también define una subclase mutable de NSString
, NSMutableString
, que se puede modificar después de la inicialización.
Hay varias formas de crear objetos String. La forma más sencilla de crear un objeto de cadena es llamando al método string()
en la clase NSString
. Esto devuelve un objeto string vacío. Echa un vistazo a la referencia de clase de NSString
para obtener una lista completa de inicializadores.
Otra ruta común para crear objetos de cadena es a través de literales string. En el siguiente ejemplo, se asigna un literal de cadena a stringA
. En tiempo de compilación, el compilador reemplazará el literal de cadena con una instancia de NSString
.



Ten en cuenta que no confiamos en la inferencia de tipos de Swift para determinar el tipo de stringA
. El segundo ejemplo, stringB
, muestra por qué es así. Si no especificamos explícitamente el tipo de stringA
como NSString
, Swift cree que queremos crear una instancia de String
. El ejemplo también ilustra que NSString
hereda de NSObject
mientras que String
no lo hace. Recuerda que el tipo String
en Swift es una estructura y las estructuras no admiten la herencia.
La clase NSString
tiene una amplia gama de métodos de instancia y clase para crear y manipular cadenas y rara vez, o nunca, sentirás la necesidad de subclase NSString
.
Exploremos NSString
y su subclase mutable, NSMutableString
, añadiendo el siguiente fragmento a tu playground.
1 |
let stringA: NSString = "This is " |
2 |
let stringB: NSString = NSString(string: "a mutable string.") |
3 |
|
4 |
var mutableString = NSMutableString(string: stringA) |
5 |
mutableString.appendString(stringB as String) |
Comenzamos creando dos instancias NSString
, una con sintaxis literal y otra con el inicializador init(string:)
. Entonces se crea una cadena mutable pasando la primera cadena como argumento. Para ilustrar que las cadenas mutables se pueden modificar después de la creación, stringB
se agrega a la cadena mutable y se imprime su valor.
NSArray
y NSSet
La clase NSArray
maneja una lista inmutable y ordenada de objetos. El framework Foundation también define una subclase mutable de NSArray
, NSMutableArray
. La clase NSArray
se comporta de forma muy similar a una matriz C o Swift, con la diferencia de que una instancia de NSArray
administra los objetos. La clase NSArray
declara una amplia gama de métodos que facilitan el trabajo con matrices, como los métodos para buscar y clasificar objetos en la matriz.
Es importante comprender que las instancias de NSArray
, NSSet
y NSDictionary
solo pueden almacenar objetos. Esto significa que no es posible almacenar tipos escalares, punteros o estructuras en ninguna de estas clases de colecciones, o sus subclases, el compilador arrojará un error si lo haces. La solución es ajustar tipos escalares, punteros y estructuras en una instancia de NSValue
o NSNumber
como vimos anteriormente en este artículo.
Agrega el siguiente fragmento de código a tu playground para explorar NSArray
y su contraparte mutable, NSMutableArray
.
1 |
let myArray = NSArray(objects: "Bread", "Butter", "Milk", "Eggs") |
2 |
print(myArray.count) |
3 |
print(myArray.objectAtIndex(2)) |
4 |
|
5 |
var myMutableArray = NSMutableArray(object: NSNumber(int: 265)) |
6 |
myMutableArray.addObject(NSNumber(int: 45)) |
Creamos una matriz usando el inicializador init(objects:)
. Este método acepta una cantidad variable de argumentos. A continuación, imprimimos la cantidad de objetos en la matriz y le preguntamos a la matriz por el objeto en el índice 2
.
Debido a que NSMutableArray
hereda de NSArray
, se comporta de forma muy similar a NSArray
. La principal diferencia es que los objetos se pueden agregar y eliminar de la matriz después de la inicialización.
Antes de continuar, quiero decir algunas palabras sobre NSSet
. Esta clase es similar a NSArray
, pero las diferencias clave son que la colección de objetos que administra un conjunto no está ordenada y que un conjunto no puede contener duplicados.
La ventaja de NSSet
es que consultar sus objetos es más rápido si solo necesitas saber si un objeto está contenido en el conjunto. El framework Foundation también define NSOrderedSet
. Las instancias de esta clase tienen los beneficios de NSSet
, pero también hacen un seguimiento de la posición de cada objeto.
NSDictionary
Al igual que las matrices, los diccionarios son un concepto común en la mayoría de los lenguajes de programación. En Ruby, por ejemplo, se los conoce como hashes. El concepto básico es fácil, un diccionario administra una colección estática de pares o entradas clave-valor.
Al igual que en los hashes de Ruby, la clave de una entrada no necesita ser un objeto de cadena per se. Puede ser cualquier tipo de objeto que se ajuste al protocolo NSCopying
siempre que la clave sea única en el diccionario. Sin embargo, en la mayoría de los casos, se recomienda utilizar objetos de cadena como claves.
Al igual que las matrices, los diccionarios no pueden almacenar un valor nulo. Si quieres representar un valor nulo, puedes usar NSNull
. La clase NSNull
define un objeto singleton que se utiliza para simbolizar valores nulos en matrices, diccionarios y conjuntos.
El patrón singleton es un patrón importante en muchos lenguajes de programación. Limita la creación de instancias de una clase a un objeto. Tratarás con objetos singleton con frecuencia cuando desarrolles aplicaciones iOS.
Al igual que NSArray
, el framework Foundation define una subclase mutable de NSDictionary
, NSMutableDictionary
. Hay varias formas de crear instancias de un diccionario. Echa un vistazo al siguiente fragmento de código.
1 |
let keyA: NSString = "myKey" |
2 |
let keyB: NSString = "myKey" |
3 |
|
4 |
let myDictionary = NSDictionary(object: "This is a string literal", forKey: keyA) |
5 |
|
6 |
print(myDictionary.objectForKey(keyB)) |
Declaramos dos objetos NSString
separados que contienen la misma cadena. Luego instanciamos un diccionario invocando init(object:forKey:)
. A continuación, solicitamos al diccionario el objeto asociado con los contenidos de keyB
e imprimimos su valor.
Es importante prestar atención a los detalles. Aunque usamos keyA
como la clave del par clave-valor y keyB
como la clave para obtener el valor u objeto del par clave-valor, el diccionario nos dio el objeto correcto (como opcional). La clase NSDictionary
es lo suficientemente inteligente como para saber que queremos el objeto asociado con la cadena "myKey"
. ¿Qué significa esto? Aunque las claves keyA
y keyB
son objetos diferentes, la cadena que contienen es la misma y eso es precisamente lo que usa la clase NSDictionary
para hacer referencia al objeto.
Antes de terminar este tutorial, me gustaría mostrarte un ejemplo de matrices y diccionarios anidados y un ejemplo de NSMutableDictionary
. El siguiente fragmento de código muestra que un diccionario puede contener otro diccionario (o matriz) y también muestra cómo trabajar con diccionarios mutables.
1 |
let myMutableDictionary = NSMutableDictionary() |
2 |
myMutableDictionary.setObject(myDictionary, forKey: "myDictionary") |
Conclusión
A pesar de que cubrimos un montón de terreno en este artículo, apenas arañamos la superficie de lo que el framework Foundation tiene para ofrecer. Sin embargo, no es necesario conocer los detalles de cada clase o función definida en el framework Foundation para comenzar con el desarrollo de iOS. Aprenderás más sobre el framework Foundation mientras exploras el SDK de iOS.
En el próximo artículo, exploraremos el framework UIKit y también analizaremos los pormenores de una aplicación de iOS.
Si tienes preguntas o comentarios, puedes dejarlos en los comentarios a continuación o comunicarte conmigo en Twitter.