RepoPilotOpen in app →

siteline/swiftui-introspect

Introspect underlying UIKit/AppKit components from SwiftUI

Healthy

Healthy across all four use cases

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 5w ago
  • 9 active contributors
  • MIT licensed
Show 3 more →
  • CI configured
  • Tests present
  • Single-maintainer risk — top contributor 92% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/siteline/swiftui-introspect)](https://repopilot.app/r/siteline/swiftui-introspect)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/siteline/swiftui-introspect on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: siteline/swiftui-introspect

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/siteline/swiftui-introspect shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 5w ago
  • 9 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Single-maintainer risk — top contributor 92% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live siteline/swiftui-introspect repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/siteline/swiftui-introspect.

What it runs against: a local clone of siteline/swiftui-introspect — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in siteline/swiftui-introspect | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 63 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>siteline/swiftui-introspect</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of siteline/swiftui-introspect. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/siteline/swiftui-introspect.git
#   cd swiftui-introspect
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of siteline/swiftui-introspect and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "siteline/swiftui-introspect(\\.git)?\\b" \\
  && ok "origin remote is siteline/swiftui-introspect" \\
  || miss "origin remote is not siteline/swiftui-introspect (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "Sources/Introspect.swift" \\
  && ok "Sources/Introspect.swift" \\
  || miss "missing critical file: Sources/Introspect.swift"
test -f "Sources/IntrospectableViewType.swift" \\
  && ok "Sources/IntrospectableViewType.swift" \\
  || miss "missing critical file: Sources/IntrospectableViewType.swift"
test -f "Sources/IntrospectionView.swift" \\
  && ok "Sources/IntrospectionView.swift" \\
  || miss "missing critical file: Sources/IntrospectionView.swift"
test -f "Sources/IntrospectionSelector.swift" \\
  && ok "Sources/IntrospectionSelector.swift" \\
  || miss "missing critical file: Sources/IntrospectionSelector.swift"
test -f "Sources/PlatformView.swift" \\
  && ok "Sources/PlatformView.swift" \\
  || miss "missing critical file: Sources/PlatformView.swift"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 63 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~33d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/siteline/swiftui-introspect"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

SwiftUI Introspect is a Swift package that provides a bridge from SwiftUI's declarative layer down to the underlying UIKit/AppKit view hierarchy, allowing developers to access and customize native UIView/NSView instances that SwiftUI wraps. It solves the problem of needing fine-grained control over platform-specific behaviors (gestures, scrolling, styling) that aren't exposed through SwiftUI's public API by using view hierarchy traversal with marker views (IntrospectionView) as anchors to locate the target UIKit/AppKit component. Single-package structure: Sources/ contains the core introspection engine (Introspect.swift, IntrospectionView.swift, IntrospectionSelector.swift) with a ViewTypes/ subdirectory defining introspection handlers for each SwiftUI control (Button.swift, DatePicker.swift, Form.swift, etc.). Examples/Showcase/ is a full Xcode project demonstrating introspection across controls, navigation, presentation, and custom UIViewRepresentable scenarios. Package.swift defines the SPM manifest; no git submodules or monorepo complexity.

👥Who it's for

iOS/macOS developers building SwiftUI apps who need to access UIKit/AppKit implementation details for advanced customization (e.g., adjusting UIScrollView deceleration, accessing UITextView internals, tuning gesture recognizers) without dropping to pure UIViewRepresentable code.

🌱Maturity & risk

Production-ready and actively maintained. The codebase is 315KB of Swift with established CI/CD (GitHub Actions in .github/workflows/ci.yml), broad platform support badges indicating multi-version testing, and a solid example app (Examples/Showcase) demonstrating real usage. The architecture suggests thoughtful design with version-specific introspection handlers (PlatformVersion.swift, ViewTypes/ per-control approach).

Low to moderate risk. The library uses only public APIs (no private UIKit selectors), mitigating the risk of App Store rejection. However, it's tightly coupled to UIKit/AppKit internals, so major OS releases can break introspection—this is why it requires explicit opt-in per-version (e.g., .iOS(.v13, .v14, ...)). Single maintainer risk exists (Siteline org); check recent commit dates and open issue backlog for current health.

Active areas of work

The repo is actively supporting new iOS versions (see support for .v18, .v26 in the README snippet), implying ongoing maintenance for each new platform release. The Examples/Showcase app exercises multiple control types (List.swift, ScrollView.swift, Presentation.swift, Controls.swift), suggesting the maintainers are actively testing coverage across the SwiftUI API surface.

🚀Get running

git clone https://github.com/siteline/swiftui-introspect.git
cd swiftui-introspect
# View package structure
cat Package.swift
# Open the example app
open Examples/Showcase/Showcase.xcodeproj
# Or integrate into your own project via SPM

Daily commands: This is a library, not an app. To run the example showcase: ```bash cd Examples/Showcase open Showcase.xcodeproj

Build and run in Xcode (Cmd+R)

Or integrate into your own SwiftUI project by adding this package dependency via Xcode's 'Add Package Dependency' dialog or by editing Package.swift.

🗺️Map of the codebase

  • Sources/Introspect.swift — Core API entry point providing the .introspect() modifier that all SwiftUI views use to access underlying UIKit/AppKit components.
  • Sources/IntrospectableViewType.swift — Protocol defining the contract for making SwiftUI view types introspectable; every view type must conform to this.
  • Sources/IntrospectionView.swift — Internal infrastructure that performs the recursive tree traversal to find UIKit/AppKit views in the SwiftUI hierarchy.
  • Sources/IntrospectionSelector.swift — Selection logic that matches target platform views against introspection criteria; defines how to traverse and identify the correct underlying component.
  • Sources/PlatformView.swift — Platform abstraction layer (iOS/macOS) that encapsulates UIView/NSView differences across SwiftUI versions.
  • Sources/ViewTypes/View.swift — Generic introspectable wrapper for arbitrary SwiftUI Views; provides fallback introspection mechanism for unlisted view types.

🧩Components & responsibilities

  • Introspect.swift (Public API) (SwiftUI modifier system, ViewModifier protocol) — Extends SwiftUI View with .introspect() modifier; configures IntrospectionView and chains closures
    • Failure mode: If modifier not applied or view type not supported, closure never invoked; silently fails
  • IntrospectionView (Tree Traversal) (UIView/NSView reflection, SwiftUI view identity tracking) — Walks SwiftUI view hierarchy recursively; applies selector to each node; extracts target platform view
    • Failure mode: If hierarchy structure changes or selector logic is incorrect

🛠️How to make changes

Add support for a new SwiftUI view type

  1. Create new file in Sources/ViewTypes/ named after the view type (e.g., YourCustomView.swift) (Sources/ViewTypes/YourCustomView.swift)
  2. Implement the IntrospectableViewType protocol, specifying the UIKit/AppKit counterpart (e.g., UIYourCustomView) (Sources/ViewTypes/YourCustomView.swift)
  3. Define the introspectionSelector to identify the target platform view in the hierarchy using IntrospectionSelector pattern (Sources/ViewTypes/YourCustomView.swift)
  4. Add concrete extension conformances for iOS/macOS if platform-specific behavior is needed; otherwise use generic View fallback (Sources/ViewTypes/YourCustomView.swift)
  5. Add test case in Examples/Showcase to demonstrate the new type works correctly (Examples/Showcase/Showcase/AppView.swift)

Add a view type variant for a different style modifier

  1. Create new file in Sources/ViewTypes/ for the styled variant (e.g., ListWithCustomStyle.swift based on ListWithGroupedStyle.swift pattern) (Sources/ViewTypes/ListWithCustomStyle.swift)
  2. Copy and adapt the base view type implementation, changing the target UIView class or selection criteria to match the styled variant's underlying component (Sources/ViewTypes/ListWithCustomStyle.swift)
  3. Register the new type in the introspection selector so SwiftUI can route styled variants to their correct implementations (Sources/IntrospectionSelector.swift)

Debug why introspection fails for a specific view

  1. Check PlatformVersion.swift to confirm your iOS/macOS version is supported and view type is available (Sources/PlatformVersion.swift)
  2. Review the target view type definition in Sources/ViewTypes/ to verify the UIKit/AppKit class name and introspectionSelector match actual runtime hierarchy (Sources/ViewTypes/YourViewType.swift)
  3. Use IntrospectionView.swift traversal logic mentally or add logging to diagnose whether the view matcher is correctly identifying the underlying component (Sources/IntrospectionView.swift)
  4. Verify the callback closure in your .introspect() modifier is actually being invoked; if not, the selector criteria may be incorrect (Sources/Introspect.swift)

🔧Why these technologies

  • SwiftUI + UIKit/AppKit interop — Provides fine-grained control over native components while keeping SwiftUI syntax for layout; only way to access platform-specific APIs (e.g., UITextFieldDelegate, NSScrollView properties)
  • Recursive view hierarchy traversal — SwiftUI hides intermediate view wrappers; must traverse the entire tree to locate the target UIKit/AppKit component buried under layers of SwiftUI view containers
  • Protocol-based introspectable types — Decouples view type definitions from core introspection engine; allows community extensions and compile-time type safety vs. string-based lookup

⚖️Trade-offs already made

  • Introspect via view tree traversal rather than official SwiftUI APIs

    • Why: SwiftUI does not provide public APIs to access underlying UIKit/AppKit components; undocumented traversal is the only mechanism
    • Consequence: Fragile across iOS/macOS versions; view hierarchy structure may change without warning, breaking selectors
  • Separate view type files for each SwiftUI view (40+ files in ViewTypes/)

    • Why: Each view may map to different UIKit classes or require different selector logic; makes patterns explicit and version-specific logic localized
    • Consequence: Code duplication; adding a new view requires understanding the pattern and creating a new file rather than centralized config
  • Generic fallback View type that works on any SwiftUI view

    • Why: Covers unmapped views and enables rapid prototyping without adding to the codebase
    • Consequence: Generic selector may match incorrect intermediate views; less reliable than explicit type definitions
  • No caching of introspection results

    • Why: SwiftUI view identity and hierarchy can change across renders; cached results may become stale
    • Consequence: Overhead on every render; introspection closure runs even if underlying component hasn't changed

🚫Non-goals (don't propose these)

  • Does not support SwiftUI on non-Apple platforms (e.g., Linux, Windows)
  • Does not provide write-back synchronization from UIKit/AppKit state changes back to SwiftUI @State
  • Does not guarantee API stability across minor iOS/macOS version updates
  • Does not provide real-time reactive bindings between SwiftUI and platform view properties

🪤Traps & gotchas

  1. Version mismatch requirement: You must explicitly specify which OS versions your code targets in the .introspect() call (e.g., .iOS(.v15, .v16, .v17))—omitting a version means introspection won't run on that OS, even if the code would work. 2. Scope confusion: By default, .introspect targets the view it's attached to; if that view doesn't have a UIKit equivalent (e.g., pure SwiftUI container), you must use scope: .ancestor to climb the hierarchy. 3. Closure capture gotcha: The README warns against storing UIView/NSView references outside the customize closure—they can be deallocated during SwiftUI redraws, causing crashes. 4. Xcode version lock: See .swift-version for the minimum Swift toolchain; building with mismatched Xcode may fail silently.

🏗️Architecture

💡Concepts to learn

  • View hierarchy traversal — Core to this library—understanding how UIView.subviews works and how to safely traverse it without assuming view structure is essential to using and extending introspection
  • Marker-based anchor pattern — Introspect uses invisible marker views to delimit a search range in the hierarchy; this prevents false matches when the same UIView type appears multiple times in a screen
  • UIViewRepresentable bridge protocol — SwiftUI's standard mechanism for wrapping UIKit views; introspection is an alternative approach that works without explicitly writing UIViewRepresentable boilerplate
  • Platform-specific view type mapping — Different iOS versions may have different underlying UIView structures for the same SwiftUI control; this library solves that via PlatformVersion and version-specific handlers
  • Defensive view casting and type safety — Introspect uses protocols and optional binding rather than forced casts to ensure graceful degradation if UIKit internals change—critical for production stability
  • Scope-aware ancestor walking — The .ancestor scope option lets you introspect parent containers (e.g., accessing a ScrollView from a child Text); requires careful hierarchy logic to avoid unintended matches
  • Closure-based customization with lifetime management — Introspect passes UIView references in closures that may outlive the view instance; the library documents why holding references outside the closure is unsafe
  • airbnb/lottie-ios — Also bridges SwiftUI to UIKit by embedding UIView/NSView in SwiftUI containers; demonstrates alternative UIViewRepresentable patterns for native integration
  • pointfreeco/swift-composable-architecture — Popular state management library for SwiftUI that often pairs with introspection when fine-grained UIKit customization is needed alongside Redux-like state flow
  • realm/realm-swift — Database library heavily used in SwiftUI apps that need introspection to access underlying UIKit scroll/table behaviors for performance tuning
  • kickstarter/ios-oss — Large open-source SwiftUI/UIKit hybrid app that likely uses introspection patterns; reference for production SwiftUI + native bridge best practices
  • apple/swift-coredata — Apple's official CoreData+SwiftUI integration; complements introspection when you need both reactive data binding and UIKit customization

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add unit tests for ViewType introspection selectors in Sources/ViewTypes/

The repo has 20+ ViewType files (Button.swift, DatePicker variants, List variants, etc.) but no visible test directory structure. Each ViewType defines IntrospectionSelector logic that maps SwiftUI views to their underlying UIKit/AppKit components. Adding comprehensive tests would ensure selectors work correctly across iOS/macOS versions and prevent regressions when new ViewTypes are added.

  • [ ] Create Tests/ directory with a structure mirroring Sources/ViewTypes/
  • [ ] Add tests for core selector logic in Tests/ViewTypes/ButtonTests.swift, DatePickerTests.swift, etc.
  • [ ] Test introspection selectors for platform-specific variants (e.g., DatePickerWithWheelStyle.swift, ListWithInsetGroupedStyle.swift)
  • [ ] Add tests validating PlatformVersion compatibility logic in Sources/PlatformVersion.swift
  • [ ] Integrate test target into Package.swift with .testTarget definitions

Add macOS/AppKit-specific integration tests in Examples/Showcase

The Examples/Showcase currently focuses on iOS (UIKit). The library supports both UIKit and AppKit, but there's no dedicated macOS example app demonstrating AppKit introspection. This would help contributors understand AppKit component mapping and catch platform-specific bugs early.

  • [ ] Create Examples/Showcase/ShowcaseMacOS/ directory with separate macOS app target
  • [ ] Port key showcase views (Controls.swift, List.swift, Navigation.swift) to AppKit equivalents
  • [ ] Add macOS-specific introspection examples (NSButton, NSPopUpButton, NSSplitViewController, etc.)
  • [ ] Update Examples/Package.swift to include macOS executable target
  • [ ] Document AppKit differences in Examples/Showcase/README.md

Add GitHub Action workflow to validate ViewType selector coverage across iOS/macOS versions

The .github/workflows/ci.yml exists but there's no automated validation that all supported ViewTypes are actually introspectable on all declared platform versions. This could catch missed version compatibility issues. The repo declares support for iOS 13+ and macOS 10.15+, but selectors may not work on all versions.

  • [ ] Add new workflow file: .github/workflows/introspect-coverage.yml
  • [ ] Create a Sources/TestHelpers/IntrospectionValidator.swift that programmatically checks each ViewType in Sources/ViewTypes/ has valid selectors
  • [ ] Run validator against multiple iOS/macOS simulator versions in the workflow using matrix strategy
  • [ ] Generate coverage report showing which ViewTypes are unavailable on which versions
  • [ ] Fail CI if a ViewType lacks version guards or documentation for unsupported platforms

🌿Good first issues

  • Add introspection support for Stepper control: Create Sources/ViewTypes/Stepper.swift following the pattern of Button.swift and DatePicker.swift, handling UIStepper on iOS and NSStepper on macOS, then add an example in Examples/Showcase/Showcase/Controls.swift: medium
  • Expand PlatformVersion.swift with iOS 19 support (following existing .v18 pattern) to unlock introspection for next-gen platform features; update .github/workflows/ci.yml to test the new version: low
  • Document the marker-based search algorithm in Sources/IntrospectionView.swift with inline code comments explaining how the invisible anchor views delimit the traversal range; add a diagram comment at the top of the file: low
  • Add unit tests for IntrospectionSelector.swift to verify view hierarchy traversal logic works correctly with deeply nested views and multiple candidates of the same UIView type: medium

Top contributors

Click to expand

📝Recent commits

Click to expand
  • b789ee1 — Gitignore .build/ at any depth (davdroman)
  • 9731abe — CI: update iPadOS to 26.2, fix NavigationSplitView test sidebar visibility (#513) (davdroman)
  • 26986a5 — Fix memory leak in IntrospectionStore and dangling pointer in introspectionController (#512) (davdroman)
  • 30cf2a9 — Simplify CI (#511) (davdroman)
  • fe75854 — Update .editorconfig (#509) (davdroman)
  • 50d54bc — Update open source libraries list in README [ci skip] (davdroman)
  • 379cbeb — CI: bump runner to macos-26 (#508) (davdroman)
  • 69d97e5 — CI: disable linting (davdroman)
  • 6ba9bd2 — CI: SwiftFormat linting (#507) (davdroman)
  • f54a840 — SwiftFormat: disable wrapSingleLineBodies rule (davdroman)

🔒Security observations

The SwiftUI Introspect codebase demonstrates solid security posture as a library for accessing underlying UIKit/AppKit components. No critical vulnerabilities were identified. The primary concerns are organizational: lack of security policy documentation, missing responsible disclosure guidelines, and no visible automated dependency scanning. Since this is a developer utility library with no network I/O, database access, or user data handling visible in the file structure, injection attack risks are minimal. The library's introspection capabilities are inherently low-risk as they operate at the view layer. Recommendations focus on process improvements rather than code-level fixes.

  • Low · Missing CHANGELOG and Security Policy — Repository root. The repository lacks a SECURITY.md file or security policy documentation. This makes it difficult for security researchers to report vulnerabilities responsibly. Additionally, no CHANGELOG is evident, which could obscure security-related updates. Fix: Add a SECURITY.md file with responsible disclosure guidelines and maintain a CHANGELOG documenting security patches and updates.
  • Low · Insufficient Code Review Guidelines — Repository root. No visible CONTRIBUTING.md or code review guidelines that enforce security best practices for contributors. This could allow insecure code patterns to be introduced. Fix: Create a CONTRIBUTING.md file that includes security review requirements, code standards, and testing procedures before accepting pull requests.
  • Low · No Dependency Vulnerability Scanning — Package.swift. The Package.swift file content was not provided, making it impossible to verify if dependency scanning tools are configured. Swift Package Manager dependencies should be regularly audited. Fix: Implement automated dependency scanning using tools like Dependabot or similar. Pin exact versions for production releases and regularly update dependencies.
  • Low · Xcode Project Files Not Version Controlled Properly — Examples/Showcase/Showcase.xcodeproj/. The presence of .pbxproj and workspace files in git history can lead to merge conflicts and potential injection vulnerabilities if not properly managed. Fix: Consider using .gitignore to exclude generated Xcode files, or use a tool like xcodeproj to manage project files programmatically to reduce manual edits.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · siteline/swiftui-introspect — RepoPilot