Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
429a582
Add core infrastructure for SwiftUI renderer
RoyalPineapple Feb 2, 2026
f8e7491
Add SwiftUI_Experimental renderer module
RoyalPineapple Feb 2, 2026
c0b4b13
Add renderer selection to test framework APIs
RoyalPineapple Feb 2, 2026
5d95394
Add SwiftUI_Experimental framework target and project configuration
RoyalPineapple Feb 2, 2026
dd1fd5b
Add SwiftUI demo apps and test infrastructure
RoyalPineapple Feb 2, 2026
e777936
Add SwiftUI renderer reference images
RoyalPineapple Feb 2, 2026
acdf7ea
Update CI configuration
RoyalPineapple Feb 2, 2026
c1daa19
Rename AccessibilityRenderer to LayoutEngine and clean up API
RoyalPineapple Feb 2, 2026
e297b55
Add SwiftUI layout engine support to SnapshotTesting
RoyalPineapple Feb 5, 2026
f2f92fc
Cherry-pick badges (effd1dc4) with iOS 16 availability
RoyalPineapple Feb 5, 2026
62c07c8
Rename SwiftUI_Experimental to AccessibilitySnapshotPreviews
RoyalPineapple Feb 5, 2026
a48b897
Regenerate reference images for AccessibilitySnapshotPreviews tests
RoyalPineapple Feb 5, 2026
f406530
rename renderSize to size, just for the public preview API
soroushsq Feb 9, 2026
93caf20
tweaking the sf symbols used for unspoken traits
RoyalPineapple Feb 10, 2026
fa7a86e
new color pallete that is slightly more subtle
RoyalPineapple Feb 10, 2026
1105873
fixing 26 tests
RoyalPineapple Feb 10, 2026
5e9ebd4
snapshots after rebasing from main to pick up custom content fix
RoyalPineapple Feb 10, 2026
27b4b87
fixing failure results upload for swiftui tests
RoyalPineapple Feb 10, 2026
3571c5d
adjusting custom content demo
RoyalPineapple Feb 10, 2026
64fa8e2
clip legend
soroushsq Feb 11, 2026
f0cea48
add another clipped
soroushsq Feb 11, 2026
70902c2
modifying test view to hide the activation points as they arent relev…
RoyalPineapple Feb 11, 2026
325184c
fix merge
soroushsq Mar 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,21 @@ jobs:
run: tuist build --path Example
- name: Tuist Test - en
run: tuist test --path Example -d '${{ matrix.device }}' -o ${{ matrix.os }} --platform iOS --result-bundle-path TestResults-${{ matrix.os }}.xcresult "AccessibilitySnapshotDemo (en)"
- name: Tuist Test - AccessibilitySnapshotPreviews
if: matrix.platform != 'iOS_17'
run: xcodebuild test -workspace Example/AccessibilitySnapshot.xcworkspace -scheme AccessibilitySnapshotPreviewsDemo -destination 'platform=iOS Simulator,name=${{ matrix.device }},OS=${{ matrix.os }}' -resultBundlePath TestResults-SwiftUI-${{ matrix.os }}.xcresult
- name: Zip Test Results
if: failure()
run: zip -r TestResults-${{ matrix.platform }}.zip TestResults-*.xcresult
- name: Upload Results
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4
if: failure()
with:
name: Tuist Test Results (${{ matrix.platform }})
path: TestResults-${{ matrix.os }}.xcresult
name: Test Results-(${{ matrix.platform }})
path: TestResults-${{ matrix.platform }}.zip
- name: Upload Reference Images
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4
if: failure()
with:
name: Reference Images (${{ matrix.platform }})
name: Reference Images-(${{ matrix.platform }})
path: Example/SnapshotTests/ReferenceImages
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import AccessibilitySnapshotPreviews
import SwiftUI

@main
struct AccessibilitySnapshotPreviewsDemoApp: App {
var body: some Scene {
WindowGroup {
NavigationStack {
DemoListView()
}
}
}
}

struct DemoListView: View {
var body: some View {
List {
Section("Basics") {
NavigationLink("Basic Accessibility") {
BasicAccessibilityDemo()
}
}

Section("Advanced") {
NavigationLink("Custom Actions") {
CustomActionsDemo()
}
NavigationLink("Custom Rotors") {
CustomRotorsDemo()
}
NavigationLink("Custom Content") {
CustomContentDemo()
}
NavigationLink("Path Shapes") {
PathShapesDemo()
}
NavigationLink("Unspoken Traits") {
UnspokenTraitsDemoView()
}
}
}
.navigationTitle("Accessibility Previews")
}
}

#Preview {
NavigationStack {
DemoListView()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import SwiftUI
import AccessibilitySnapshotPreviews

struct BasicAccessibilityDemo: View {
var body: some View {
VStack(alignment: .leading, spacing: 12) {
// MARK: - Label

DemoSection(title: "Label", description: "Text description for an element") {
Image(systemName: "star.fill")
.font(.title2)
.foregroundStyle(.yellow)
.accessibilityLabel("Favorite")
}

// MARK: - Value

DemoSection(title: "Value", description: "Current state or content") {
HStack {
Text("Progress")
Spacer()
Text("75%")
.foregroundStyle(.secondary)
}
.accessibilityElement(children: .combine)
.accessibilityValue("75 percent complete")
}

// MARK: - Hint

DemoSection(title: "Hint", description: "What happens when activated") {
Button("Delete") {}
.buttonStyle(.bordered)
.tint(.red)
.accessibilityHint("Removes this item permanently")
}

// MARK: - Traits

DemoSection(title: "Traits", description: "Element type and behavior") {
Text("Section Header")
.font(.headline)
.accessibilityAddTraits(.isHeader)
}

// MARK: - Input Labels

DemoSection(title: "Input Labels", description: "Voice Control phrases") {
Button {} label: {
Image(systemName: "mic.fill")
}
.buttonStyle(.bordered)
.accessibilityLabel("Microphone")
.accessibilityInputLabels(["Microphone", "Mic", "Record", "Voice"])
}

// MARK: - Activation Point

DemoSection(title: "Activation Point", description: "Custom tap target") {
RoundedRectangle(cornerRadius: 8)
.fill(Color.blue)
.frame(width: 60, height: 40)
.overlay(
Circle()
.fill(Color.white)
.frame(width: 8, height: 8)
.offset(x: 20, y: 10)
)
.accessibilityLabel("Custom tap target")
.accessibilityActivationPoint(CGPoint(x: 50, y: 30))
}

// MARK: - Element Grouping

DemoSection(title: "Element Grouping", description: "Combine children") {
HStack {
Image(systemName: "person.circle.fill")
.font(.title2)
VStack(alignment: .leading, spacing: 2) {
Text("John Doe")
.font(.subheadline)
.fontWeight(.medium)
Text("Online")
.font(.caption2)
.foregroundStyle(.green)
}
}
.padding(8)
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.accessibilityElement(children: .combine)
}

Spacer()
}
.padding()
.navigationTitle("Basic Accessibility")
}
}

#Preview {
BasicAccessibilityDemo()
.accessibilityPreview()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import SwiftUI
import AccessibilitySnapshotPreviews

struct CustomActionsDemo: View {
@State private var isFavorite = false
@State private var isArchived = false

var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 32) {
DemoSection(
title: "Custom Actions",
description: "VoiceOver users swipe up/down to access these actions"
) {
emailRowView
}
}
.padding()
}
.navigationTitle("Custom Actions")
}

private var emailRowView: some View {
HStack {
VStack(alignment: .leading) {
Text("Important Email")
.font(.headline)
Text("Preview of email content...")
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
if isFavorite {
Image(systemName: "star.fill")
.foregroundStyle(.yellow)
}
}
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(12)
.accessibilityElement(children: .combine)
.accessibilityLabel("Important Email. Preview of email content")
.accessibilityActions {
Button(isFavorite ? "Remove from favorites" : "Add to favorites") {
isFavorite.toggle()
}
Button("Reply") {}
Button("Forward") {}
Button(isArchived ? "Unarchive" : "Archive") {
isArchived.toggle()
}
Button("Delete", role: .destructive) {}
}
}
}

#Preview {
CustomActionsDemo()
.accessibilityPreview()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import AccessibilitySnapshotPreviews
import SwiftUI

struct CustomContentDemo: View {
var body: some View {
VStack(alignment: .leading, spacing: 16) {
DemoSection(
title: "Custom Content",
description: "Additional details via 'More Content' rotor"
) {
VStack(spacing: 12) {
productCard(
name: "Wireless Headphones",
price: "$149.99",
rating: 4.5,
reviews: 2847,
sku: "WH-1000XM5"
)

productCard(
name: "Bluetooth Speaker",
price: "$79.99",
rating: 4.2,
reviews: 1523,
sku: "BS-MINI-BLK"
)
}
}

Spacer()
}
.padding()
.navigationTitle("Custom Content")
}

private func productCard(
name: String,
price: String,
rating: Double,
reviews: Int,
sku: String
) -> some View {
VStack(alignment: .leading, spacing: 4) {
Text(name)
.font(.subheadline)
.fontWeight(.medium)

HStack {
Text(price)
.font(.caption)
.fontWeight(.semibold)

Spacer()

Text("★ \(String(format: "%.1f", rating))")
.font(.caption2)
}
}
.accessibilityElement(children: .combine)
.accessibilityLabel("\(name), \(price)")
.accessibilityCustomContent("Rating", "\(String(format: "%.1f", rating)) stars from \(reviews) reviews")
.accessibilityCustomContent("SKU", sku, importance: .high)
}
}

#Preview {
CustomContentDemo()
.accessibilityPreview()
}
Loading
Loading