Introdução: Por que "comprimento de string" não é apenas um número?
\n\nImagine que você está escrevendo um aplicativo para iOS. O usuário digita seu nome em um campo de texto. Simples, certo? E se o nome contiver emojis, sinais diacríticos (por exemplo, "café") ou ideogramas? De repente, a simples tarefa de "obter o comprimento da string" se transforma em um detetive com elementos de Unicode.
\n\nEm Swift, trabalhar com strings não é apenas contar caracteres. É uma jornada pelo mundo dos clusters de grafemas, escalares Unicode e o legado do Objective-C (sim, NSString ainda está conosco). Se você está começando a aprender Swift ou migrando de outras linguagens, este artigo é para você. Vamos analisar como determinar corretamente o comprimento de uma string, quais armadilhas esperam por você e quando usar count e quando usar NSString.
Este material é baseado em uma pergunta popular real do Stack Overflow. Milhares de desenvolvedores tropeçaram neste tópico. Não vamos repetir seus erros. Vamos lá!
\n\n\n\n
1. Fundamentos: a propriedade count de String
\n\nA maneira mais óbvia de obter o comprimento de uma string em Swift é usar a propriedade count. Ela retorna o número de "caracteres" (Character) na string. Mas o que é um caractere para o Swift?
let greeting = "Olá, mundo!"\nprint(greeting.count) // 12 (incluindo espaço e ponto de exclamação)\n\nTudo funciona como esperado. Mas vamos complicar a tarefa:
\n\nlet emojiString = "👨👩👧👦"\nprint(emojiString.count) // 1 (!!!)\n\nUm? Sério? Visualmente, é uma família inteira de quatro pessoas. Qual é o truque?
\n\nA resposta está em como o Swift processa strings. count não conta bytes nem caracteres Unicode individuais, mas sim clusters de grafemas estendidos (extended grapheme clusters). Esta é uma sequência de um ou mais escalares Unicode que o usuário percebe como um único caractere. O emoji "família" consiste em várias partes, mas o Swift o considera um único caractere, porque é assim que o ser humano o vê.
Esse comportamento não é um bug, é uma funcionalidade. O Swift foi projetado com a internacionalização em mente. Se count contasse cada escalar Unicode, a string "café" (com um único caractere é) poderia dar resultados diferentes dependendo de como o é é codificado: como um único caractere U+00E9 ou como uma combinação de e + acento (U+0065 + U+0301).
let cafe1 = "café" // é como um único caractere\nlet cafe2 = "cafe\\u{0301}" // e + acento\nprint(cafe1.count) // 4\nprint(cafe2.count) // 4 (embora a string tenha 5 escalares)\n\nO Swift conta ambas as variantes como 4 caracteres, porque o usuário vê "café" com 4 letras. Esta é a mágica dos clusters de grafemas.
\n\n\n\n
2. Abordagem alternativa: NSString e a propriedade length
\n\nÀs vezes, você precisa obter não o número "humano" de caracteres, mas o número de unidades de codificação UTF-16. Por exemplo, ao trabalhar com APIs legadas, Core Text ou ao transmitir dados em formatos que esperam comprimento em UTF-16 (como alguns protocolos de rede).
\n\nÉ aqui que o NSString entra em ação — a boa e velha classe do Objective-C. Strings do Swift são automaticamente convertidas (bridge) para NSString, então você pode usar sua propriedade length:
let swiftString = "👨👩👧👦"\nlet nsString = swiftString as NSString\nprint(nsString.length) // 11 (número de unidades UTF-16)\n\nPor que 11? Porque o emoji composto "família" ocupa 11 unidades em UTF-16 (cada componente é codificado como um par substituto ou caractere separado). Se você estiver trabalhando com texto que será processado por bibliotecas C antigas ou APIs que esperam NSString, use length. Mas lembre-se: não é o número de caracteres que o usuário vê.
Outra nuance: NSString não leva em conta clusters de grafemas. Para a string "café" (segunda variante), length retornará 5, embora count retorne 4. Conclusão: escolha o mét