Generics en Swift: guía completa con ejemplos de código

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Generics en Swift: para qué sirven y cómo usarlos en la práctica



Los generics (programación genérica) son una de las características más potentes y fundamentales del lenguaje Swift. Permiten escribir código flexible, type-safe y reutilizable, evitando la duplicación. Si alguna vez has usado Array<Int>, Dictionary<String, Any> o Optional<T>, ya te has encontrado con generics. En este artículo, analizaremos para qué sirven, cómo aplicarlos correctamente y veremos ejemplos prácticos para el desarrollo iOS/macOS.



¿Para qué sirven los Generics en Swift?



El principal problema que resuelven los generics es la necesidad de escribir el mismo código para diferentes tipos de datos. Sin ellos, tendrías que crear funciones separadas para trabajar con Int, String, Double y estructuras personalizadas. Esto provoca duplicación de código, errores y dificulta el mantenimiento.



Ventajas clave de los generics:

  • Type-safety: el compilador verifica los tipos en tiempo de compilación, evitando errores en tiempo de ejecución.
  • Reutilización de código: una función o tipo funciona con cualquier tipo de dato.
  • Rendimiento: los generics en Swift se implementan mediante especialización — el compilador crea código optimizado para cada tipo específico, comparable a escribir código duplicado manualmente.
  • Legibilidad y abstracción: el código se vuelve más conciso y expresivo.


Veamos un ejemplo sencillo. Supongamos que necesitamos una función para intercambiar los valores de dos variables. Sin generics, tendríamos que escribir así:



func swapInts(_ a: inout Int, _ b: inout Int) {    let temp = a    a = b    b = temp}

func swapStrings(_ a: inout String, _ b: inout String) { let temp = a a = b b = temp}// ... y así para cada tipo


Con generics, la solución se vuelve universal:



func swapValues<T>(_ a: inout T, _ b: inout T) {    let temp = a    a = b    b = temp}

var x = 10var y = 20swapValues(&x, &y) // Funciona con Int

var str1 = "Hello"var str2 = "World"swapValues(&str1, &str2) // Funciona con String


Como puedes ver, <T> es un placeholder (marcador de posición) para el tipo. El compilador deduce automáticamente el tipo concreto al llamar a la función.



Funciones y tipos genéricos



Los generics se pueden usar no solo con funciones, sino también con tipos personalizados: estructuras, clases y enumeraciones.



Funciones genéricas

Además del placeholder simple, se pueden imponer restricciones al tipo. Por ejemplo, una función que solo funciona con tipos que implementan el protocolo Equatable:



func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {    for (index, item) in array.enumerated() {        if item == value {            return index        }    }    return nil}

let numbers = [1, 5, 3, 12]if let index = findIndex(of: 5, in: numbers) { print("Índice encontrado: \\(index)") // Imprime: Índice encontrado: 1}


La restricción <T: Equatable> garantiza que el operador == esté definido para el tipo T.



Tipos genéricos (estructuras y clases)

Creemos una pila simple — una estructura de datos que funciona bajo el principio LIFO (Last In, First Out):



struct Stack<Element> {    private var items = [Element]()        mutating func push(_ item: Element) {        items.append(item)    }        mutating func pop() -> Element? {        return items.popLast()    }        var top: Element? {        return items.last    }}

var intStack = Stack<Int>()intStack.push(10)intStack.push(20)print(intStack.pop() ?? 0) // 20

var str

Blogs

Book Recommendations