Por qué sin genéricos tu código Swift se convertirá pronto en espagueti
\n\nImagina que estás escribiendo una aplicación para entrega de pizzas. Necesitas procesar pedidos, clientes, repartidores — y cada vez escribir el mismo código para ordenar, buscar o filtrar, cambiando solo el tipo de datos. ¿Suena como la pesadilla de cualquier desarrollador? Aquí es donde entran en escena los Generics en Swift — una poderosa herramienta que permite escribir código flexible y reutilizable sin sacrificar la seguridad de tipos.
\n\nEn este artículo no solo repasaremos la sintaxis. Analizaremos cómo la tipificación en Swift y los genéricos trabajan juntos para que tu código no solo sea bonito, sino también robusto. Aprenderás cómo eliminar el copia-pega, cómo usar protocol con associatedtype y por qué las cláusulas where son tu mejor amigo.
Qué son los Generics y por qué cambian las reglas del juego
\n\nGenerics (genéricos, tipos genéricos) — es un mecanismo que permite escribir funciones, estructuras, clases y enumeraciones que funcionan con cualquier tipo de datos que cumpla ciertas condiciones. En lugar de escribir tres versiones de una función para Int, String y Double, escribes una que funciona con cualquier tipo.
En Swift la tipificación es estricta y estática. Esto significa que el compilador verifica los tipos en tiempo de compilación. Los Generics no rompen esta regla — la hacen más flexible. El compilador deduce el tipo concreto al llamar a la función, pero las verificaciones se mantienen.
\n\nPrimer ejemplo: función sin genéricos
\n\n// Mal: tres funciones para tres tipos\nfunc swapInts(_ a: inout Int, _ b: inout Int) {\n let temp = a\n a = b\n b = temp\n}\n\nfunc swapStrings(_ a: inout String, _ b: inout String) {\n let temp = a\n a = b\n b = temp\n}\n\nfunc swapDoubles(_ a: inout Double, _ b: inout Double) {\n let temp = a\n a = b\n b = temp\n}\n\nY ahora — la solución elegante con genéricos:
\n\n// Bien: una función para todos los tipos\nfunc swapValues<T>(_ a: inout T, _ b: inout T) {\n let temp = a\n a = b\n b = temp\n}\n\n// Uso:\nvar x = 10\nvar y = 20\nswapValues(&x, &y) // T = Int\n\nvar name1 = "Alice"\nvar name2 = "Bob"\nswapValues(&name1, &name2) // T = String\n\n¿Ves la magia? <T> es un marcador de posición (placeholder) de tipo. El compilador sustituye el tipo real al llamar a la función. Esta es la base de la programación genérica en Swift.
Tipos genéricos: creando nuestra pila universal
\n\nLos genéricos en Swift no solo funcionan con funciones, sino también con tipos completos. Vamos a crear una estructura Stack — el ejemplo clásico de estructura de datos LIFO (Last In, First Out).
struct Stack<Element> {\n private var items: [Element] = []\n \n mutating func push(_ item: Element) {\n items.append(item)\n }\n \n mutating func pop() -> Element? {\n return items.popLast()\n }\n \n func peek() -> Element? {\n return items.last\n }\n \n var count: Int {\n return items.count\n }\n}\n\n// Uso:\nvar intStack = Stack<Int>()\nintStack.push(1)\nintStack.push(2)\nintStack.push(3)\nprint(intStack.pop() ?? 0) // 3\n\nvar stringStack = Stack<String>()\nstringStack.push("Swift")\nstringStack.push("Generics")\nprint(stringStack.peek() ?? "") // "Generics"\n\nObserva: el tipo Element lo especificamos entre corchetes angulares al declarar la estructura. Al crear una instancia, lo concretamos — Stack<Int> o Stack<String>. El compilador garantiza que no puedas poner accidentalmente una cadena en una pila de enteros.
Protocol con associatedtype: genéricos para protocolos
\n\nEn Swift los protocolos no pueden tener parámetros genéricos directamente. En su lugar, se usa