Character vs Unicode.Scalar vs UTF-8 code unit?
Predict first
What does this print?
let family = "👨👩👧👦"
print(family.count)
- 1
- 3
- 5
Mental model. Scalars are the alphabet, a Character is a written word, and bytes are the ink. Different layers, different counts.
Unicode scalar — one code point (roughly, one “letter” in the Unicode table). Character — a grapheme cluster: one or more scalars that a human reads as one character. UTF-8 code unit — one byte of the encoded form.
For "👨👩👧👦": 1 Character, 7 scalars (four people + three ZWJ), 25 UTF-8 bytes. All correct at the same time.
Four people joined by three zero-width joiners are one grapheme cluster, so one Character, even though it is 7 scalars and 25 UTF-8 bytes.
Code
let family = "👨👩👧👦"
print(family.count) // Characters
print(family.unicodeScalars.count) // scalars
print(family.utf8.count) // bytes
1, 7, 25
Common traps
- Using
countfor a network payload size. Usedata.countorstring.utf8.count. - Truncating by byte count. You can split a scalar in half and produce invalid UTF-8. Truncate on grapheme or scalar boundaries.
They might follow up with
- What is a combining character? A scalar that modifies the previous one (accents, skin tones). It merges into one grapheme cluster.
- What is normalization? Different scalar sequences can be canonically equivalent (“é” as one scalar vs “e” + accent). Swift compares canonically equivalent strings as equal.
Related questions
- 1.9.1 Why is String not a RandomAccessCollection of Character? Why is string[i + 1] not O(1)?
- 1.9.3 How do you compare strings for UI versus for identifiers?
← Why is String not a RandomAccessCollection of Character? Why is… How do you compare strings for UI versus for identifiers? →