Por qué los emojis 👩‍👩‍👧‍👦 "rompen" las líneas en Swift? Análisis con 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

👩‍👩‍👧‍👦: Un solo carácter que vuelve loco

\n\n

Imagina: estás escribiendo una aplicación para iOS, procesando texto, contando caracteres. Todo funciona perfectamente, hasta que aparece el emoji de una familia de tres mujeres y dos niñas. En la pantalla parece un solo carácter, pero Swift dice que son... ¿4 caracteres? ¿O 7? ¿Quizás 11? ¿Te suena familiar? ¡Bienvenido al mundo de Unicode y las cadenas de Swift!

\n\n

Esta pregunta es una de las más populares en Stack Overflow en la sección de Swift. Y con razón. La aparente simplicidad de las cadenas se convierte en una verdadera odisea cuando entran en juego los emojis compuestos. En este artículo no solo explicaremos por qué sucede esto, sino que también mostraremos cómo trabajar correctamente con estas cadenas en Swift. Si programas en Swift y te enfrentas al procesamiento de entrada de usuario, emojis o internacionalización, este contenido es para ti.

\n\n

🔍 El problema: "¿Cuántos caracteres tiene esta cadena?"

\n\n

Vamos al grano. Abre Xcode Playground y escribe:

\n\n
import Foundation\n\nlet familyEmoji = "👩‍👩‍‍👧‍👦"\nprint(familyEmoji.count) // 1? 4? 7?\n
\n\n

Si esperabas 1, tienes razón desde la perspectiva del usuario. Si esperabas 4 (cuatro figuras), también es lógico. Y Swift devolverá 1. Pero prueba familyEmoji.unicodeScalars.count — ¡y obtendrás 7! Y si lo descompones con .utf16.count — será 11.

\n\n

¿Cuál es el truco? El emoji 👩‍👩‍‍👧‍👦 es un clúster de grafemas compuesto. Está formado por varios escalares Unicode, unidos por caracteres invisibles de unión (ZWJ — Zero Width Joiner). Swift trata estas secuencias como un solo carácter visible (un grafema), lo cual es correcto desde la perspectiva del usuario, pero resulta confuso cuando intentas trabajar con las "entrañas" de la cadena.

\n\n

🧱 Anatomía de un emoji: escalares, clústeres y ZWJ

\n\n

Para entender la magia, descompongamos nuestro emoji en sus partes. Cada carácter Unicode es un escalar (un código numérico). Para 👩‍👩‍‍👧‍👦, la secuencia es la siguiente:

\n\n
let scalars = familyEmoji.unicodeScalars\nfor scalar in scalars {\n    print(scalar.value, scalar.properties.name ?? "unknown")\n}\n// Salida (simplificada):\n// 128105 (WOMAN)\n// 8205 (ZERO WIDTH JOINER)\n// 128105 (WOMAN)\n// 8205 (ZERO WIDTH JOINER)\n// 128103 (GIRL)\n// 8205 (ZERO WIDTH JOINER)\n// 128102 (BOY)\n
\n\n

¿Ves? Cuatro figuras (dos mujeres, una niña, un niño) unidas por tres caracteres ZWJ invisibles (código 8205). Swift, siguiendo el estándar Unicode, interpreta toda esta secuencia como un único clúster de grafemas extendido. Por eso .count devuelve 1: es la cantidad de "caracteres de usuario".

\n\n

🔄 Comparación de enfoques: count vs unicodeScalars vs utf16

\n\n

Organicemos la información. Para la cadena "👩‍👩‍👧‍👦" obtenemos tres respuestas diferentes según la API que usemos:

\n\n
    \n
  • .count (clústeres de grafemas) — 1. Correcto para la interfaz de usuario, para mostrar en UILabel.
  • \n
  • .unicodeScalars.count (escalares Unicode) — 7. Útil para procesamiento de texto de bajo nivel.
  • \n
  • .utf16.count (unidades de código UTF-16) — 11. Se usa para compatibilidad con Objective-C NSString.
  • \n
  • .utf8.count (bytes UTF-8) — 25. Para trabajar con redes y archivos.
  • \n
\n\n

Cada uno de estos enfoques resuelve un problema específico. El error típico de un principiante es usar .count para limitar la longitud de entrada en un campo de texto (por ejemplo, "no más de 140 caracteres"). Para el usuario, "👩‍👩‍👧‍👦" es un solo carácter, y un límite de 140 emojis como este es normal. Pero si almacenas la cadena en una base de datos con una longitud fija en bytes, necesitarás .utf8.count.

\n\n

⚙️ Casos prácticos: cómo no quemarse

Blogs

Book Recommendations