Table of Contents
- The problem
- Design decisions and alternatives
- A real bug: the rendering test that could not fail
- Usage
- Limits
The problem
iOS 26 and macOS 26 brought a long list of new SwiftUI APIs: Liquid Glass, tab bar minimizing,
toolbar spacers, scroll edge effects, a rich text TextEditor, a native WebView, Chart3D,
SF Symbol draw effects, the @Animatable macro and the on-device Foundation Models framework.
Reading the documentation tells you the signatures. It does not show you what Glass.clear looks
like next to Glass.regular, or what a tab bar accessory does when the bar minimizes.
SwiftUI iOS 26 Showcase is a Swift
package with sixteen small screens, each built around one group of these APIs. Every screen has
controls to try the API's options and lists the SDK symbols it uses. You can run the whole
gallery, or put a single screen into your own app.
The package did not start in this shape. The repository was opened in February 2026. The 2.0.0
changelog says what it looked like before the rebuild: from June 2026 it held only
placeholders — "enterprise" helpers (a circuit breaker, a retry policy, an event bus) unrelated
to SwiftUI, a gallery whose only behaviour was a print call, and a test file that referenced
types that did not exist, so the tests never compiled. The old CI workflow ignored test
failures, so it still showed green. Version 2.0.0, released on 14 October 2026 through
PR #3, replaced all of it.
Because the earlier public types were removed, it is a new major version.
That history set the main goal for the rebuild: a showcase is only useful if what it claims is
true, and the claims have to be checked by something other than the author's memory.
Design decisions and alternatives
One enum as the single source of truth. Every demo is a case of ShowcaseDemo with its
metadata: title, summary, SF Symbol, category, the APIs it shows and the platforms it runs on.
The gallery rows, search and the demo table in the README all come from this metadata.
ShowcaseDemoView switches over the enum to pick the view, so adding a case without a view
breaks the build. The alternative was a Demo protocol with a registry that each view adds
itself to. That puts metadata next to the view, but a forgotten registration fails silently at
run time; with the enum, the compiler finds it.
A catalog that does not import SwiftUI. ShowcaseCatalog filters demos by platform,
searches, groups by category and looks a demo up by identifier. It only imports Foundation, and
the gallery is a thin view over it, so the behaviour is tested with plain unit tests. The search
rule is simple on purpose: a query matches a demo when every word appears in its title, summary
or API list, ignoring case and diacritics. Fuzzy matching was the obvious alternative, but for
sixteen items a rule that you can state in one sentence is easier to test and to predict.
Platform differences in two layers. Two demos use APIs that do not exist on macOS:
tabBarMinimizeBehavior(_:) and listSectionIndexVisibility(_:). Their views are wrapped in
#if os(iOS), and on macOS ShowcaseDemoView shows a ContentUnavailableView instead. The
metadata marks them as iOS-only, so the macOS catalog lists the other fourteen, and
demo(withID:) returns nil for a demo that is not available on the catalog's platform. A
deep link cannot open a screen that would not work.
A small public surface. The public API is the gallery, ShowcaseDemoView, the catalog types
and three helpers that the README documents: WebAddress, RichTextFormatter and
SurfaceFunction. The sixteen demo views are internal, so they can change without a major
version. Logic that does not need a view lives in those helper types so that tests can reach it.
WebAddress, for example, accepts only http and https addresses with a host and no user
name. A comment in the source explains why the user name check matters: without it,
mailto:a@b.com would parse as user mailto on host b.com once https:// is added.
A real minimum toolchain. tabViewBottomAccessory(isEnabled:content:) is marked
@available(iOS 26.1). The tab bar demo checks #available(iOS 26.1, *) at run time, but
#available does not help when the symbol is missing from the SDK, so Xcode 26.0 cannot build
the package. The Getting Started article was changed to name Xcode 26.1 as the minimum
(commit 7ec13b4), and the README and the changelog say the same.
A real bug: the rendering test that could not fail
The most useful lesson in this repository came from a test that passed for the wrong reason.
The first rendering test, everyAvailableDemoRenders, drew each demo with ImageRenderer and
checked the image size. The size came from the frame the test set itself, so the check could not
fail. Commit `b5d727a`
records what a closer look found: 11 of 14 demo images were blank. ImageRenderer does not draw
the contents of a ScrollView, and the demo pages were laid out inside one.
The fix had two parts. First, commit ba4c342 added a seam: an environment value that turns
scrolling off, and a small view that respects it.
1/// A vertical scroll view, or just its content when `demoPagesScroll` is off.2struct DemoScrollView<Content: View>: View {3 @Environment(\.demoPagesScroll) private var scrolls4 @ViewBuilder let content: Content5 6 var body: some View {7 if scrolls {8 ScrollView { content }9 } else {10 content11 }12 }13}The default stays true, so the app behaves as before. It is a test-only concern in production
code, and the source says so in a one-line comment. The alternatives were a snapshot testing
library or UI tests, both with more dependencies and slower runs.
Second, the test was rewritten. Each demo is now rendered at 390 points wide and its natural
height, with scrolling off, and compared with a bare DemoPage that has only the same summary
and API list. The demo must be taller and must draw more non-transparent pixels. A control test
checks the measurement itself: Color.clear draws zero pixels and a line of text draws more
than zero. The commit message notes that removing the content from DemoPage makes the test
fail for every page-based demo.
Then the same question was asked of the new test, and it found a second gap. The scroll edge
demo still used a plain ScrollView, and its segmented picker sits in a safe area bar outside
the page content. The test only ever saw the picker: removing all 30 rows left the pixel count
unchanged (2b30b15). The demo moved to DemoScrollView, the row count became a static
constant, and a dedicated test now requires the page to be at least rowCount × 44 points
taller than the bare page. With the rows replaced by ForEach(0..<0) the page is 186 points
tall against a minimum of 1,482, and the test fails; with all 30 rows it is 1,866 points
(d35fd7f).
The last step was to write down what the test cannot see. ImageRenderer does not draw a List
or a WebView. The two List-based demos are left out of the rendering test, and removing the
WebView from the web view demo leaves the rendered height and pixel count unchanged (205
points, 17,890 pixels), because the address and status bars alone make the page larger
(c713b8c). That note sits next to the test, and the README states the same limits.
The lesson is plain: a green check is only worth what it can detect. The way to find out is to
break the code on purpose and watch the test fail.
Usage
Add the package in Xcode with File › Add Package Dependencies… and the URL
https://github.com/muhittincamdali/SwiftUI-iOS26-Showcase.git, keeping the default rule from
2.0.0. Then replace your app's main file with:
1import SwiftUI2import SwiftUIiOS26Showcase3 4@main5struct DemoApp: App {6 var body: some Scene {7 WindowGroup {8 ShowcaseGallery()9 }10 }11}To show a single demo inside your own navigation:
1struct ChartScreen: View {2 var body: some View {3 NavigationStack {4 ShowcaseDemoView(.chart3D)5 }6 }7}The catalog behind the gallery can be used directly, for example to build your own demo browser:
1let catalog = ShowcaseCatalog(platform: .iOS)2let matches = catalog.demos(matching: "glass button") // [.glassButtons]3let sections = catalog.sections(matching: "symbol") // one section: Charts, Symbols & Animation4let demo = catalog.demo(withID: "webView") // .webViewThese blocks are copied verbatim into ReadmeExamplesTests.swift, where they compile and their
results are checked with #expect. A separate parameterized test reads the README and fails when
a row of the demo table does not match the metadata. At the 2.0.0 tag, swift test on macOS
runs 39 tests in 9 suites; CI also runs the tests in the iOS Simulator and builds the example
app for iOS and macOS.
Limits
- There are no snapshot or UI tests. The rendering test answers only "did this demo draw its
own content", and the two List demos and the WebView content are outside it.
- The scroll edge test checks height only, so rows that draw nothing but keep their 44-point
height would still pass.
- The Foundation Models demo needs a device with Apple Intelligence turned on to send prompts,
and the web view demo needs a network connection. Neither the model call nor the page load is
tested automatically.
- The minimum toolchain is stated as Xcode 26.1, but CI runs on the
macos-26runner image
without a version matrix, so that lower bound is not tested on its own.
- The demo texts are English only. The README has nine translations; the app does not.
- At the 2.0.0 tag, the
Package.swiftsnippet in the README's Installation section still uses
branch: "main", while the quick start and the Getting Started article use 2.0.0.
- The README roadmap has one item: demos for SwiftUI APIs that later iOS 26 updates introduce.
The code, the changelog and the tests are in the
repository, and the API reference
is on GitHub Pages.
Tags
iOS Development News
Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.
Your subscription starts when you open the link in the confirmation email and press “Confirm my subscription”. The newsletter keeps open/click statistics; you can unsubscribe anytime with one click. Privacy

