A Complete Step-by-Step Tutorial in Xcode
Introduction
Rich Text Editors are a core component in many modern applications: notes, email, messaging, blogging, productivity apps, and more. They allow users to apply styles such as bold, italic, underline, colors, font sizes, lists, and links, all while typing.
Historically, building a rich text editor on iOS was complex and required UIKit (UITextView) and NSAttributedString. With the evolution of SwiftUI, especially looking ahead to iOS 26, Apple has significantly improved rich text support, making it possible to build powerful editors without abandoning the declarative paradigm.
In this tutorial you will learn:
- What a Rich Text Editor is and how it works internally
- How to handle rich text using
AttributedString - How to integrate an editable text view in SwiftUI
- How to build a formatting toolbar (bold, italic, underline, color)
- How to synchronize state, selection, and formatting
- Best practices for architecture in SwiftUI
By the end, you’ll have a fully functional, extensible Rich Text Editor ready for production.
Prerequisites
Before starting, make sure you have:
- Xcode compatible with iOS 26
- Basic knowledge of Swift and SwiftUI
- Familiarity with
@State,@Binding, andObservableObject - A Mac running an up-to-date version of macOS
1. What Is a Rich Text Editor on iOS?
A Rich Text Editor is a component that allows editing text along with formatting information. On iOS, this formatting is represented by attributes attached to text ranges.
Examples of attributes include:
- Font (
font) - Color (
foregroundColor) - Style (bold, italic)
- Underline
- Links
- Alignment
In modern Swift, Apple encourages the use of:
AttributedStringinstead of NSAttributedString, because it is:
- Safer
- More expressive
- Fully compatible with SwiftUI
2. Editor Architecture
Our editor will have three main layers:
- Text model
UsesAttributedStringas the single source of truth. - Editing view
A SwiftUI component that renders and edits the text. - Formatting toolbar
Buttons that apply styles to the selected text.
Separating these layers keeps the code clean, scalable, and maintainable.
3. Creating the Project in Xcode
- Open Xcode
- Select Create a new Xcode project
- Choose App
- Configure:
- Interface: SwiftUI
- Language: Swift
- Target: iOS 26
Name the project something like:
RichTextEditorSwiftUI4. The Data Model: AttributedString
We start by creating an observable model that holds the text:
import SwiftUI
class RichTextViewModel: ObservableObject {
@Published var text: AttributedString = AttributedString("Start typing your text here...")
}This will be the central state of the editor.
5. Displaying Rich Text in SwiftUI
SwiftUI can render AttributedString directly:
Text(viewModel.text)However, Text is not editable. To edit rich text, we need a dedicated editable component.
6. Using TextEditor with AttributedString
In iOS 26, TextEditor supports AttributedString directly:
struct RichTextEditorView: View {
@StateObject private var viewModel = RichTextViewModel()
var body: some View {
TextEditor(text: $viewModel.text)
.padding()
}
}With this, you already have a basic rich text editor.
7. Tracking Text Selection
To apply formatting, we need to know which part of the text is selected.
@Published var selectedRange: Range<AttributedString.Index>?In iOS 26, TextEditor exposes selection changes via modifiers:
.onSelectionChange { range in
viewModel.selectedRange = range
}8. Applying Formatting: Bold
We create a function to toggle bold formatting on the selected text:
func toggleBold() {
guard let range = selectedRange else { return }
let isBold = text[range].font?.weight == .bold
text[range].font = .system(
size: 16,
weight: isBold ? .regular : .bold
)
}This approach modifies only the selected range.
9. Formatting Toolbar
Next, we build a toolbar with formatting buttons:
struct FormatToolbar: View {
@ObservedObject var viewModel: RichTextViewModel
var body: some View {
HStack {
Button("B") {
viewModel.toggleBold()
}
.font(.system(size: 18, weight: .bold))
Button("I") {
viewModel.toggleItalic()
}
.italic()
Button("U") {
viewModel.toggleUnderline()
}
}
.padding()
.background(.ultraThinMaterial)
}
}10. Italic and Underline
We implement the remaining formatting functions:
func toggleItalic() {
guard let range = selectedRange else { return }
text[range].font = .system(size: 16).italic()
}
func toggleUnderline() {
guard let range = selectedRange else { return }
text[range].underlineStyle =
text[range].underlineStyle == nil ? .single : nil
}11. Text Color
We add support for text color:
func setTextColor(_ color: Color) {
guard let range = selectedRange else { return }
text[range].foregroundColor = color
}And color buttons:
Button {
viewModel.setTextColor(.red)
} label: {
Circle().fill(Color.red).frame(width: 20)
}12. Putting Everything Together
struct ContentView: View {
@StateObject private var viewModel = RichTextViewModel()
var body: some View {
VStack(spacing: 0) {
FormatToolbar(viewModel: viewModel)
TextEditor(text: $viewModel.text)
.padding()
}
}
}13. Saving and Loading Rich Text
You can serialize the text to Data:
let data = try? viewModel.text.data(using: .rtf)And restore it later:
if let data {
viewModel.text = try AttributedString(data: data)
}This is ideal for Core Data, files, or iCloud storage.
14. Best Practices
✔ Use AttributedString as the single source of truth
✔ Avoid unnecessary UIKit bridging
✔ Centralize formatting logic in the ViewModel
✔ Keep the UI declarative
✔ Design with extensibility in mind (lists, headers, links)
15. Ideas to Extend the Editor
- Numbered and bulleted lists
- Headings (H1, H2, H3)
- Link support
- Custom Undo / Redo
- Export to Markdown or HTML
- External keyboard support
Conclusion
Building a Rich Text Editor in SwiftUI for iOS 26 is no longer a complex task reserved for UIKit. Thanks to AttributedString, improvements in TextEditor, and the declarative model of SwiftUI, we can now build powerful, elegant, and maintainable editors.
This tutorial provides a solid, professional foundation for integrating advanced text editing into modern iOS applications. From here, the only limits are your imagination—and a bit of UX design.
If you have any questions about this article, please contact me and I will be happy to help you 🙂. You can contact me on my X profile or on my Instagram profile.