What is AttributedString, and how is it different from NSAttributedString?
Predict first
How many runs does this have?
var s = AttributedString("Hello world")
s[s.range(of: "world")!].link = URL(string: "https://x.com")
print(s.runs.count)
- 1
- 2
- 3
Predict first
What does this print?
let s = try! AttributedString(markdown: "**bold** text")
print(s.runs.count, String(s.characters))
- 1 **bold** text
- 2 bold text
- 2 **bold** text
Mental model. An attributed string is plain text with highlighter marks. A run is one stretch drawn in the same colour.
AttributedString (iOS 15) is the Swift-native styled text: a value type that holds characters plus attributes stored in runs (stretches that share the same attributes). It is Hashable, Codable and Sendable. You style a part by subscripting with a range: s[range].link = url. It can also parse inline Markdown with AttributedString(markdown:).
NSAttributedString is the older Objective-C class (with a mutable subclass), which UIKit and TextKit still use. Attributes belong to scopes: the Foundation scope has link, while font and foregroundColor live in the UIKit, AppKit and SwiftUI scopes, so you pick the scope when you read or write them.
The link splits the text into two stretches with different attributes: "Hello " and "world".
The Markdown markers are consumed and become an attribute on the first run, so the characters are plain bold text.
Common traps
- Looking for
fontwithout a scope. It exists only in the UIKit, AppKit or SwiftUI scope. - Assuming a mutation affects the original. It is a value type, so copies are independent.
They might follow up with
- How do you iterate styling?
for run in s.runs { run.range; run.link }. - What does Markdown parsing give you? Attributes such as
inlinePresentationIntentfor bold and italic, which you map to fonts yourself.
Related questions
- 2.8.2 Why do ranges break when you move between String, AttributedString and NSAttributedString?
- 1.9.2 Character vs Unicode.Scalar vs UTF-8 code unit?
Why do ranges break when you move between String, AttributedString… →