Repository navigation
Performance and resource-usage improvements for both libraries, plus a move to Xcode 27.
MarkdownUI
Behavior changes
DefaultImageProviderandDefaultInlineImageProvidernow default to.downsampled(maximumPixelDimension: 2048). Large network images decode with at most 2048 pixels on their longest side and keep their original intrinsic size. Passresolution: .originalto keep the previous behavior.- The new
Resolution.downsampled(maximumPixelDimension:)case may require updating exhaustiveswitchstatements overResolution. - Image responses are capped at 50 MB and 32 megapixels decoded.
Additions
AssetInlineImageProvider.init(bundle:)looks up images by the URL's last path component.
Performance
- Streamed sources reparse only the changed tail instead of the whole document.
- Builder Markdown strings are cached, and nested image scans are deferred.
- Parsing switches on integer cmark node types.
- Lists use fewer layers per item and scope marker widths to their own list.
FlowLayoutkeeps several measurements per layout pass.- Inline text fragments with matching attributes are coalesced.
- Heading slugs are computed in one pass.
- Network images:
- Load through a dedicated
URLSessionwith separate network and decode concurrency limits. - Revalidate with ETag/Last-Modified.
- Are held in a decoded-image cache with count-bounded LRU eviction.
- Load through a dedicated
- Loaded block images are kept when their views reappear.
- The macOS asset image provider caches bundle images.
- Hashable inline image providers are identified by value.
- The syntax highlighting cache uses LRU eviction and hashes keys outside its lock.
- Long or streaming documents are now documented to use
.lazyrendering.
MarkdownUIEditor
Images
- Images decode off the main actor at display resolution, with size and pixel limits.
- Cached images revalidate with conditional requests.
- Images load through a dedicated URL session.
- Default image providers share one decoded-image cache whose cost is measured from the retained bitmap.
Editing
- After structural edits, only the top-level blocks that changed are replaced.
- Edited blocks are written back only when an edit actually changes them.
- Source binding echoes are recognized without reparsing the document.
- Toolbar command state is evaluated once per update.
- Projection lookups, inline attribute resolution, and text-run merging no longer scale with document size per edit.
- Task checkbox placement is skipped when its layout inputs are unchanged.
Tables
- Unchanged tables keep their attachments and grids when text is rendered again.
- Changed tables update their grid in place for row, column, and cell changes, including after table commands and undo.
- Each distinct table cell's text is projected once.
- A growing UIKit table relayouts only itself.
Tooling
- Builds and tests use Xcode 27 with the 27.0 SDKs (Swift 6.4).
- Snapshot baselines are recorded on macOS 27 and iOS 27.
- CI runs on GitHub-hosted runners with formatting, lint, and per-platform build checks.
- Documentation builds and publishing are centralized.
Full Changelog: 3.1.0...3.2.0