Enriching your text in text views
At a glance
| Item |
Summary |
| Purpose |
Demonstrates TextKit line numbers, collapsible sections, attachments, exclusion paths, and list markers. |
| App architecture |
SwiftUI owns navigation; thin representables construct platform views, and each platform container owns one independent TextKit demonstration. |
| Main patterns |
Adapter layer, compile-time platform abstraction, feature slices, delegate-driven filtering, and attachment view providers. |
| Project style |
Cross-platform SwiftUI shell around AppKit/UIKit TextKit views; feature behavior stays in one file per demonstration. |
Project structure
TextKitAndTextViewSampleApp/
├── SwiftUI/
│ ├── TextKitAndTextViewSampleApp.swift
│ ├── ContentView.swift
│ ├── Representables.swift
│ └── Platform.swift
├── LineNumberingView.swift
├── SectionCollapsingView.swift
├── InlineAttachmentsView.swift
├── TextAttachmentView.swift
├── ExclusionPathView.swift
├── TextListView.swift
└── Assets/
Structure observations
SwiftUI/ is a navigation/adaptation layer, not the TextKit implementation layer.
- Every demonstration file contains its container plus narrowly related private helper views, storage extensions, or attachment providers.
Platform.swift centralizes AppKit/UIKit typealiases and the small layout differences shared by container views.
Overall architecture
flowchart LR
App["SwiftUI App"] --> Content["ContentView NavigationSplitView"]
Content --> Selection["SidebarItem"]
Selection --> Wrapper["Feature SwiftUI View"]
Wrapper --> Representable["UIViewRepresentable or NSViewRepresentable"]
Representable --> Container["Platform container view"]
Platform["Platform.swift typealiases"] --> Container
Container --> TextView["UITextView or NSTextView"]
TextView --> TextKit["NSTextLayoutManager / NSTextContentStorage"]
Container --> Gutter["Gutter or attachment UI"]
Assets["RTF, JSON, text, movie assets"] --> TextKit
Reference code
TextKitAndTextViewSampleApp/SwiftUI/Representables.swift:18 — compile-time adapters construct the same feature container for AppKit or UIKit.
#if os(macOS)
private struct LineNumberingRepresentable: NSViewRepresentable {
func makeNSView(context: Context) -> LineNumberingContainerView { LineNumberingContainerView() }
func updateNSView(_ nsView: LineNumberingContainerView, context: Context) {}
}
#else
private struct LineNumberingRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> LineNumberingContainerView { LineNumberingContainerView() }
func updateUIView(_ uiView: LineNumberingContainerView, context: Context) {}
}
#endif
Interpretation
The representables are construction adapters only: they carry no domain state and have empty update methods. TextKit objects remain owned by reference-type platform views, which is the layer receiving layout and delegate callbacks.
Ownership and state
classDiagram
ContentView *-- SidebarItem : owns selection state
LineNumberingRepresentable --> LineNumberingContainerView : creates
LineNumberingContainerView *-- LineNumberedTextView : creates
LineNumberingContainerView *-- GutterView : creates
LineNumberedTextView o-- LineNumberingContainerView : weak back-reference
SectionCollapsingContainerView *-- CollapsibleTextView : creates
SectionCollapsingContainerView *-- DisclosureGutterView : creates
CollapsibleTextView o-- DisclosureGutterView : weak
InlineAttachmentsContainerView *-- AnimatedAttachmentViewProvider : registers type
AnimatedAttachmentViewProvider --> AnimatedAttachmentView : creates
Ownership evidence
TextKitAndTextViewSampleApp/SectionCollapsingView.swift:11 — the container owns both views and uses a weak closure capture to avoid a container–gutter cycle.
class SectionCollapsingContainerView: PlatformContainerView {
// ...
}
| Owner |
State |
Relationship |
Mutation authority |
ContentView |
Selected sidebar item |
SwiftUI @State |
List selection binding |
| Each container view |
Text view plus companion gutter/control |
Creates and retains |
Initialization and feature callbacks |
| Specialized text view |
Paragraph cache, collapsed offsets, layout-pass entries |
Owns TextKit-derived state |
TextKit callbacks/toggle methods |
| Attachment view/provider |
Player and rendered attachment view |
Provider creates; text view manages reuse |
Attachment lifecycle |
Class and protocol design
| Type family |
Responsibility |
Design note |
| SwiftUI feature views/representables |
Select and construct a native feature view |
Thin adapters; no coordinators needed |
PlatformContainerView |
Normalize text-view embedding between AppKit and UIKit |
Small inheritance-based platform seam |
| Feature container views |
Load assets and assemble TextKit plus companion UI |
One owner per demonstration |
| Specialized text/gutter views |
Respond to viewport layout or content enumeration |
Private helpers protect feature invariants |
| Attachment subclasses/providers |
Supply interactive/animated inline views |
Framework extension points for attachment lifecycle |
No app-defined protocol exists. Protocol-oriented design is expressed through framework contracts such as UIViewRepresentable/NSViewRepresentable, NSTextContentStorageDelegate, and UIContentView-style TextKit provider APIs.
Access control
| Symbol |
Access |
Verified effect |
Rationale |
| Platform representables |
private |
Only public-facing SwiftUI wrappers in the same file construct them. |
Hides bridge mechanics from feature consumers. |
| Specialized text/gutter/player views |
private |
Helpers cannot be coupled from other files. |
Their state is meaningful only inside one demonstration. |
Container textView/gutterView pairs |
fileprivate in line/collapse samples |
Same-file helper classes can call back across type boundaries. |
Narrower than module access while supporting a file-scoped feature slice. |
| Platform typealiases and containers |
implicit internal |
All feature files share them inside the target. |
Cross-file platform vocabulary, not exported API. |
selection |
@State private |
Only ContentView controls navigation state. |
SwiftUI view-local source of truth. |
Reference code
TextKitAndTextViewSampleApp/LineNumberingView.swift:10 — fileprivate exposes only the two collaborators required by same-file layout helpers.
class LineNumberingContainerView: PlatformContainerView {
// ...
}
Logic ownership and placement
| Logic |
Owner or file |
Why it belongs there |
| Navigation/feature selection |
ContentView |
Pure SwiftUI presentation state. |
| UIKit/AppKit construction |
Representables.swift |
Boundary between value views and native reference views. |
| Platform layout differences |
Platform.swift |
Avoids scattering conditional aliases and scroll setup. |
| Visible line indexing/gutter drawing |
LineNumberingView.swift |
One file owns cache, viewport callback, and rendering. |
| Paragraph filtering/collapse state |
SectionCollapsingView.swift |
NSTextContentStorageDelegate needs direct access to collapsed offsets. |
| Attachment reuse/playback |
Attachment feature files |
Lifecycle follows TextKit attachment providers. |
Design patterns
| Pattern |
Source evidence |
Purpose or tradeoff |
| SwiftUI adapter layer |
TextKitAndTextViewSampleApp/SwiftUI/Representables.swift:12 |
Hosts imperative TextKit views without moving their state into SwiftUI. |
| Compile-time platform abstraction |
TextKitAndTextViewSampleApp/SwiftUI/Platform.swift:8 |
Shares feature code while retaining native UIKit/AppKit types. |
| File-scoped feature slice |
TextKitAndTextViewSampleApp/LineNumberingView.swift:10 |
Keeps owner and private collaborators together. |
| Delegate filtering |
TextKitAndTextViewSampleApp/SectionCollapsingView.swift:140 |
Hides body paragraphs at enumeration time rather than rewriting source text. |
| Provider/reuse policy |
TextKitAndTextViewSampleApp/InlineAttachmentsView.swift:22 |
Lets TextKit manage expensive attachment views across edits and scrolling. |
Naming conventions
- Public-facing feature names pair
...View with ...ContainerView and private ...Representable bridges.
PlatformView, PlatformTextView, and related aliases make cross-platform intent explicit.
- Helpers describe visible roles:
GutterView, DisclosureGutterView, and AnimatedAttachmentViewProvider.
- Mutating methods use concrete outcomes such as
toggleSection, rebuild, and setListMarkerStyle.
Architecture takeaways
- Keep SwiftUI representables thin when the native view has a rich callback-driven lifecycle.
- Use file-scoped feature slices to permit collaboration without widening helpers to the whole module.
- Centralize only real platform differences; leave feature logic shared and native.
- Model layout-derived UI, such as gutters, as a collaborator owned beside the text view.
Source map
| Source file |
Relevant symbols |
TextKitAndTextViewSampleApp/SwiftUI/ContentView.swift |
Navigation and selection |
TextKitAndTextViewSampleApp/SwiftUI/Representables.swift |
SwiftUI/native adapters |
TextKitAndTextViewSampleApp/SwiftUI/Platform.swift |
AppKit/UIKit abstraction |
TextKitAndTextViewSampleApp/LineNumberingView.swift |
Viewport cache and gutter |
TextKitAndTextViewSampleApp/SectionCollapsingView.swift |
Content filtering and collapse state |
TextKitAndTextViewSampleApp/InlineAttachmentsView.swift |
Animated attachment provider |
TextKitAndTextViewSampleApp/TextAttachmentView.swift |
Expandable attachment |
TextKitAndTextViewSampleApp/ExclusionPathView.swift |
Exclusion geometry |
TextKitAndTextViewSampleApp/TextListView.swift |
List marker editing |