Instruction

Attributed Strings

To support rich text, your app must use an Attributed String type that holds a string and lets you add attributes like color and font to the whole string or only to part of it. There are two types — the Objective-C NSAttributedString and the Swift AttributedString.

NSAttributedString

An NSAttributedString object holds a string plus key-value pairs (attributes) that specify additional information to apply to ranges of characters within the string. Attributes include:

  • Rendering attributes that specify font, color, kerning, ligatures, and other details
  • Semantic attributes such as link URLs or tool-tip information
  • Language attributes to support automatic gender agreement and text layout
  • Accessibility attributes that provide information for assistive technologies
  • Attributes that summarize details of the Markdown import process
  • Custom attributes you define for your app
  • Attributes for attachments and adaptive image glyphs

You can create an NSAttributedString object from a string of characters and a dictionary of attributes or from the contents of a file, including files that contain RTF, RTFD, HTML, Markdown, or other file formats. NSAttributedString is an immutable type. Use NSMutableAttributedString if you need to modify the contents of an attributed string later. To store an attributed string in your app’s database, serialize it into an RTFD data object.

Swift Apprentice, Chapter 20: Result Builders uses NSAttributedString to construct fancy greetings.

AttributedString

AttributedString is a Swift struct introduced in iOS 15, enabling Swift devs to create styled text Swiftly. Attributes provide features such as visual styles for display, accessibility guidance, and hyperlink data for linking between data sources.

AttributedString conforms to Codable so you can directly encode and decode one along with its attributes just like working with a normal String. AttributedString is fully localizable — you can define styles in your text directly in the localization file. AttributedString is more limited than NSAttributedString — it supports only Markdown, not RTF, RTFD, HTML, or other file formats.

You can learn more about AttributedString and Markdown in AttributedString Tutorial for Swift: Getting Started

NSAttributedString vs AttributedString

Initializing an NSAttributedString with a String and an attributes dictionary:

let string = "Attributed String"
let attributes: [NSAttributedString.Key : Any] = [
    NSAttributedString.Key.foregroundColor: UIColor.systemPink,
    NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 40),
    NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue
]
let attributedString = NSAttributedString(string: string, attributes: attributes)

let label = UILabel()
label.attributedText = attributedString

You have to be careful typing the dictionary keys and values — the compiler can’t help you much.

Initializing an AttributedString with a String and attributes:

private var attributedString: AttributedString {
    let string = "Attributed String"
    var attributedString = AttributedString(string)
    
    attributedString.foregroundColor = .pink
    attributedString.font = .boldSystemFont(ofSize: 40)
    attributedString.underlineStyle = .single

    return attributedString
}

var body: some View {
    Text(attributedString)
}

The attributes are strong types with code completion and compiler checking.

NSAttributedString <==> AttributedString

You can convert one to the other, but each type has attributes that the other doesn’t. Keeping this in mind, you can leverage the strengths of each type. For example:

  • Parse HTML with NSAttributedString then convert it to AttributedString.
  • Create a type-safe string with AttributedString then convert it to NSAttributedString for display or for saving to disk.

Adaptive Image Glyphs

An Adaptive Image Glyph is a data object for an emoji-like image that can appear in attributed text. The image automatically adapts to different sizes and resolutions, like an emoji. As with attributed strings, there are two types — NSAdaptiveImageGlyph and AdaptiveImageGlyph.

Note: Using NSTextAttachment, it was already possible to display images inline in an NSMutableAttributedString. However, if you convert the NSMutableAttributedString to an AttributedString, the image doesn’t display.

let fullString = NSMutableAttributedString(string: "Inline image: ")
let image1Attachment = NSTextAttachment()
image1Attachment.image = UIImage(systemName: "globe")
let image1String = NSAttributedString(attachment: image1Attachment)
fullString.append(image1String)

AttributedString(fullString)  // displays only "Inline image: ", no globe image

NSAdaptiveImageGlyph

When a user creates a new custom emoji and inserts it into their text, TextKit creates an instance of NSAdaptiveImageGlyph to represent the image and enable it to behave like an emoji, automatically adapting to different sizes and resolutions. This type manages multiple images, along with metadata describing how to adapt those images correctly to different fonts and font attributes. It’s another type of attachment to an NSAttributedString object.

NSAdaptiveImageGlyph has a standard image format in a square aspect ratio with multiple resolutions and additional metadata:

  • contentIdentifier: a globally unique and stable identifier.
  • contentDescription: a textual description that can be used for accessibility.
  • contentType: includes alignment metrics to allow proper layout and placement of images so they can be used with and formatted alongside regular text.

NSAdaptiveImageGlyph has two initializers, but both depend on previously saved data:

init(imageContent: Data)
init(coder: NSCoder)

There’s also a conversion initializer init(AttributedString.AdaptiveImageGlyph).

You can’t write code to create an adaptive image glyph from image data. The only way to get one is by text input.

AdaptiveImageGlyph

AdaptiveImageGlyph is a struct component of AttributedString. Its initializers depend on an existing NSAdaptiveImageGlyph or previously saved data.

init(NSAdaptiveImageGlyph)
init(from: any Decoder)
init(imageContent: Data)

There is also an adaptiveImageGlyph instance property in AttributeScopes.SwiftUIAttributes as well as in AttributeScopes.UIKitAttributes and AttributeScopes.AppKitAttributes.

init(adaptiveImageGlyph:attributes:) creates an attributed string — appropriate to the attribute scope — with an adaptive image glyph and applies the specified attributes to it.

A convenience initializer uses an NSAdaptiveImageGlyph — either an existing one or one obtained from the text input system — and its associated NSAttributedString attributes.

convenience init(
  adaptiveImageGlyph: NSAdaptiveImageGlyph,
  attributes: [NSAttributedString.Key : Any] = [:]
)
  • adaptiveImageGlyph: The adaptive image glyph to place in the string. Typically, you get this type from the text input system.
  • attributes: The attributes to apply to the adaptive image glyph. Specify an empty dictionary to create the string without any extra attributes.

Entering Genmoji in Your App

The only way to create an attributed string with an adaptive image glyph is via the text input system, using the new emoji keyboard. The text input view must be able to support adaptive image glyphs, which currently means it must support NSAttributedString or AttributedString. Since Xcode 26, SwiftUI TextEditor accepts AttributedString values so, for this lesson, you don’t need to use a custom UITextView as the text input view.

See forum comments
Download course materials from Github
Previous: Introduction Next: Demo