Swift Tip: Decomposing Emoji

Now that emoji are common everywhere, we need to be aware of unicode, even without an international userbase. For example, the emoji ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ (a family of four glyph) has a very different length across String implementations:

								"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ".count // 1
("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ" as NSString).length // 11


Javascript also evaluates to 11. In Ruby "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ".length evaluates to 7, and in Python 2 len("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ") evaluates to 25 (depending on your settings). One string, four different lengths.

Perhaps even more surprising: none of these implementations are wrong. They're all counting different things. In Swift, we get 1 as the answer because Swift counts the characters -- ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ is a single character. The NSString variant and Javascript evaluate to 11 because they're counting the number of UTF-16 code units. We can replicate this in Swift:

								"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ".utf16.count // 11


We can also see how Python gets to 25 -- in this case, it counts the UTF-8 code units:

								"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ".utf8.count // 25


And finally, Ruby and Python 3 evalute to 7 because they count the unicode scalars, and ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ consists of the following scalars: ๐Ÿ‘จ + zero width joiner + ๐Ÿ‘ฉ + zero width joiner + ๐Ÿ‘ง + zero width joiner + ๐Ÿ‘ฆ.

								"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ".unicodeScalars.count // 7


When you're dealing with strings where length is significant, keep this in mind. To learn more, watch last week's Swift Talk episode or read the transcript . If you'd like to learn more about unicode and how it's implemented in Swift, read our book Advanced Swift .

Stay up-to-date with our newsletter or follow us on Twitter .

Back to the Blog

Recent Posts