Why emoji 👩‍👩‍👧‍👦 "break" strings in Swift? Code analysis

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

👩‍👩‍👧‍👦: One character that drives you crazy

\n\n

Imagine: you're writing an iOS app, processing text, counting characters. Everything works perfectly until you encounter the family emoji of three women and two girls. It looks like one character on screen, but Swift says it's... 4 characters? Or 7? Or maybe 11? Sound familiar? Welcome to the world of Unicode and Swift Strings!

\n\n

This question is one of the most popular on Stack Overflow in the Swift section. And for good reason. The apparent simplicity of strings turns into a real quest when composite emoji come into play. In this article, we will not just explain why this happens, but also show how to work correctly with such strings in Swift. If you write in Swift and deal with user input processing, emoji, or internationalization — this material is for you.

\n\n

🔍 The Problem: "How many characters are in this string?"

\n\n

Let's get straight to the point. Open Xcode Playground and write:

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

If you expected 1, you're right from the user's perspective. If you expected 4 (four figures) — that's also logical. And Swift will output 1. But try familyEmoji.unicodeScalars.count — and you'll get 7! And if you break it down to utf16.count — it will be 11.

\n\n

What's the catch? The emoji 👩‍👩‍‍👧‍👦 is a composite grapheme cluster. It consists of several Unicode scalars joined by invisible connector characters (ZWJ — Zero Width Joiner). Swift treats such sequences as one visible character (grapheme), which is correct from the user's perspective, but confusing when you try to work with the "internals" of the string.

\n\n

🧱 Anatomy of an Emoji: Scalars, Clusters, and ZWJ

\n\n

To understand the magic, let's break down our emoji into its components. Each Unicode character is a scalar (numeric code). For 👩‍👩‍‍👧‍👦 the sequence looks like this:

\n\n
let scalars = familyEmoji.unicodeScalars\nfor scalar in scalars {\n    print(scalar.value, scalar.properties.name ?? "unknown")\n}\n// Output (simplified):\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

See? Four figures (two women, a girl, a boy) are joined by three invisible ZWJ characters (code 8205). Swift, following the Unicode standard, interprets this entire sequence as one extended grapheme cluster. That's why .count returns 1 — it's the number of "user-perceived characters".

\n\n

🔄 Comparison of Approaches: count vs unicodeScalars vs utf16

\n\n

Let's systematize. For the string "👩‍👩‍👧‍👦" we get three different answers depending on which API we use:

\n\n
    \n
  • .count (grapheme clusters) — 1. Correct for UI, display in UILabel.
  • \n
  • .unicodeScalars.count (Unicode scalars) — 7. Useful for low-level text processing.
  • \n
  • .utf16.count (UTF-16 code units) — 11. Used for compatibility with Objective-C NSString.
  • \n
  • .utf8.count (UTF-8 bytes) — 25. For working with networks and files.
  • \n
\n\n

Each of these approaches solves its own task. A beginner's mistake is to use .count to limit input length in a text field (e.g., "no more than 140 characters"). For the user, "👩‍👩‍👧‍👦" is one character, and a limit of 140 such emoji is fine. But if you store the string in a database with a fixed byte length — you need .utf8.count.

\n\n

⚙️ Practical Cases: How to Avoid Pitfalls

Blogs

Book Recommendations