SwiftUI text selection still isn’t good enough
My most anticipated API improvement in iOS 27 actually involved no API changes at all. SwiftUI has had a textSelection(_:) View modifier since iOS 15 but its user experience was always very bare bones: long-pressing on a Text view would bring up a system menu with items appropriate for the entire contents of the Text view. This meant that copying the contents of a Text view was an all or nothing proposition – good enough for individual chat messages or data points in a table but exceedingly awkward for long paragraphs of text.
With iOS 27, the same modifier got a lot more powerful: without any changes to the code, enabling text selection on a view tree now gives users the familiar text selection interface.
While that’s an incredibly welcome and overdue addition, I’m afraid it’s simply not good enough.
In Linear, much like every other contemporary communication or document authoring app, users have long come to expect WYSIWYG editing as a baseline feature. Being able to create inline lists, block quotes, todo items, collapsible sections is table stakes and most text fields in Linear, from comments to document descriptions, have rich text support.
In the iOS app, we implement document viewing by parsing a ProseMirror document and building a view tree roughly like so:
switch node.nodeType {
case let .blockQuote(content):
NodeView(content)
.padding(.leading, 8)
.overlay(alignment: .leading) {
Capsule().fill(.secondary).frame(width: 4)
}
case let .paragraph(content):
Text(content)
case let .image(url):
AsyncImage(url: url)
// many, many more node types
}
A ProseMirror node can get resolved to one of many different SwiftUI views, some of which recursively contain the resolved views of the original node’s child nodes.
Since views need to be decorated and laid out in various ways, we end up with a view hierarchy that contains roughly one Text view per paragraph at various levels of nesting.
Unfortunately, SwiftUI’s new text selection does not actually support selecting across Text views. Even in the most basic example, users will only be able to select within one of these text views, even though they look like one unit:
VStack(alignment: .leading) {
Text("Hello, world!")
Text("How are you?")
}
I’ll spare you the screen recording here since it doesn’t capture the frustration of the experience – everything looks like it should work but the selction just won’t expand. Being able to freely select text across the screen is something we had since iPhone OS 3.0 – prehistoric times!
For Linear, this isn’t something we can ship outside of a few short labels for which the previous behavior worked arguably better. And what are the alternatives that we have? Dropping down to UIKit rendering with UITextInteraction, using a web view or presenting a sheet with a plain-text Markdown representation for the user to select.
We’re about two months away from the release of iOS 27 so I’m still hopeful a solution will be available at launch. Text-based interfaces have been quite popular recently so this one simple API would help a lot to make SwiftUI apps feel native to the platform.