This document explains how OpenSwiftUI's documentation is built and hosted.
OpenSwiftUI uses Swift-DocC to generate API documentation, which is hosted on GitHub Pages at:
https://openswiftuiproject.github.io/OpenSwiftUI/documentation/openswiftui/
The documentation is built from the OpenSwiftUI target and includes symbols from OpenSwiftUICore via @_exported import. System framework symbols (CoreFoundation, CoreGraphics, etc.) are filtered out to keep the documentation focused on OpenSwiftUI's own APIs.
Documentation is built and published by .github/workflows/documentation.yml.
The workflow deploys to GitHub Pages when a version tag like 0.18.3 is pushed,
or when the workflow is run manually from the Actions tab.
The workflow:
Scripts/CI/darwin_setup_build.sh so local package dependencies match
the rest of the macOS CI environment.OpenSwiftUIProject/swift-docc-render-artifact at
release/6.3-colorful and exports its dist/ directory as DOCC_HTML_DIR.Scripts/build-documentation.sh --clean --hosting-base-path /OpenSwiftUI,
with source links pointing at the triggering tag or manual workflow ref..docs/build/docc-output with actions/upload-pages-artifact.actions/deploy-pages.GitHub Pages settings:
github-pagesGitHub repository setup:
[0-9]*.[0-9]*.[0-9]*main if you want manual workflow_dispatch deployments
from the default branchTwo scripts are provided for documentation management:
Scripts/build-documentation.shBuilds documentation locally with optional preview server.
Usage:
# Build and preview documentation locally
./Scripts/build-documentation.sh --preview
# Build with internal symbols visible
./Scripts/build-documentation.sh --minimum-access-level internal
# Preview on a different port
./Scripts/build-documentation.sh --preview --port 8080
# Clean build (force regenerate symbol graphs)
./Scripts/build-documentation.sh --clean
Options:
--preview - Start a local HTTP server to preview documentation--port PORT - Port for preview server (default: 8000)--minimum-access-level LEVEL - Symbol visibility: public, internal, private, fileprivate (default: public)--target TARGET - Documentation target (default: OpenSwiftUI)--hosting-base-path PATH - Base path for hosting (e.g., /OpenSwiftUI)--source-service SERVICE - Source service (github, gitlab, bitbucket)--source-service-base-url URL - Base URL for source links--clean - Clean build artifacts and force rebuildSet DOCC_HTML_DIR to use a custom swift-docc-render dist/ directory. CI
uses the renderer from
OpenSwiftUIProject/swift-docc-render-artifact@release/6.3-colorful. For local
preview with the same renderer, use a local checkout:
DOCC_HTML_DIR=/path/to/swift-docc-render-artifact/dist \
./Scripts/build-documentation.sh --preview
For local preview, omit --hosting-base-path; that option is only applied to
static builds for GitHub Pages.
Example workflow:
# Preview documentation locally before deploying
./Scripts/build-documentation.sh --preview
# Open http://localhost:8000/documentation/openswiftui in your browser
# Make changes to documentation comments in source code
# Re-run the script to see updates (uses incremental builds)
Scripts/update-gh-pages-documentation.shLegacy manual deployment script. Prefer the GitHub Actions workflow for normal publishing; this script is kept as an emergency/manual fallback.
Usage:
# Build and deploy documentation to the legacy gh-pages branch
./Scripts/update-gh-pages-documentation.sh
# Test deployment without pushing (dry-run)
./Scripts/update-gh-pages-documentation.sh --echo-without-push
# Deploy using existing build (skip building)
./Scripts/update-gh-pages-documentation.sh --no-build
# Deploy with internal symbols
./Scripts/update-gh-pages-documentation.sh --minimum-access-level internal
The script defaults to --hosting-base-path /OpenSwiftUI because the site is served from a subdirectory. Without this setting, CSS/JS resources will fail to load.
Options:
--hosting-base-path PATH - Base path for GitHub Pages deployment (default: /OpenSwiftUI)--no-build - Skip building, use existing documentation output--echo-without-push - Show push command without executing (for testing)--minimum-access-level LEVEL - Symbol visibility (default: public)--clean - Clean build artifacts and force rebuild--no-force - Don't force push (preserves gh-pages history, increases repo size)How it works:
build-documentation.shgh-pages branch as a git worktree at gh-pages/gh-pages/docs/origin/gh-pagesNote: Force push is used by default to keep the repository size small by avoiding accumulation of large binary files (CSS, JS, images) in git history. Each deployment completely replaces the previous one.
The legacy manual script deploys to the gh-pages branch with the following structure:
gh-pages/
├── .nojekyll # Prevents Jekyll processing
└── docs/ # Documentation root (configured in GitHub Pages settings)
├── index.html
├── css/
├── js/
├── data/
├── documentation/
└── ...
We initially used Swift Package Index (SPI) for documentation hosting, which worked well and provided excellent features like multi-version documentation picker. However, we encountered several limitations that led us to switch to self-hosted GitHub Pages:
SPI's documentation system is built on swift-docc-plugin and SwiftPM, which currently has some constraints:
Binary Target Limitations - SwiftPM has issues with binary targets in documentation builds (swiftlang/swift-package-manager#7580). We had to add isSPIDocGenerationBuild workarounds in Package.swift to exclude certain dependencies during SPI builds.
Exported Symbol Handling - SwiftPM's symbol graph generation doesn't properly handle @_exported import declarations (swiftlang/swift-package-manager#9101), which is essential for OpenSwiftUI's re-export architecture where OpenSwiftUI re-exports OpenSwiftUICore.
Limited Customization - The plugin-based approach doesn't provide fine-grained control over symbol filtering, documentation generation parameters, or output customization that we need for a complex project like OpenSwiftUI.
By self-hosting, we gain:
We appreciate the Swift Package Index team's efforts in providing documentation hosting for the Swift community. The decision to self-host is purely technical, driven by OpenSwiftUI's specific requirements and architectural constraints rather than any shortcomings of SPI itself.
# 1. Make changes to documentation comments in source code
# 2. Preview locally
./Scripts/build-documentation.sh --preview
# 3. Verify changes at http://localhost:8000/documentation/openswiftui
# 4. Merge the documentation changes
# 5. Push a version tag such as 0.18.3, or run the Documentation workflow manually
# 6. Verify at https://openswiftuiproject.github.io/OpenSwiftUI/documentation/openswiftui/
The CI workflow already uses --clean to force symbol graph regeneration. For
local verification after adding new public APIs or changing module structure,
run:
./Scripts/build-documentation.sh --clean --preview
Symptom: Documentation page loads but appears unstyled, browser console shows 404 errors for CSS/JS files.
Cause: Documentation was built without --hosting-base-path /OpenSwiftUI flag.
Solution: The CI workflow already passes the correct base path. For manual local checks, rebuild with:
./Scripts/build-documentation.sh --hosting-base-path /OpenSwiftUI
If you use the legacy manual deployment script, keep its default
/OpenSwiftUI base path or pass an explicit equivalent.
Symptom: New APIs don't appear in documentation.
Cause: Symbol graphs weren't regenerated.
Solution: Use --clean flag to force regeneration:
./Scripts/build-documentation.sh --clean
Symptom: Documentation shows CoreFoundation, CoreGraphics symbols.
Cause: Symbol filtering failed or was disabled.
Solution: The filtering is automatic. If you see system symbols, check the build output for filtering errors and ensure the Python JSON filtering step completed successfully.
Symptom: Address already in use error when running preview.
Solution: The script will detect this and prompt you to kill the existing process, or use a different port:
./Scripts/build-documentation.sh --preview --port 8001
./Scripts/build-documentation.sh --target OpenSwiftUICore --preview
./Scripts/build-documentation.sh \
--minimum-access-level internal \
--preview
./Scripts/update-gh-pages-documentation.sh \
--echo-without-push
This creates the gh-pages branch locally and shows what would be pushed without actually pushing to the remote.
The build process filters symbol graphs to remove re-exported system framework symbols:
-emit-symbol-graphs:MODULE_LEN+MODULE_NAME...) to extract module namesOpenSwiftUI and OpenSwiftUICore modules are keptThe deployment script uses git worktree to manage the gh-pages branch:
gh-pages/: checked out to gh-pages branchBy default, deployments use git push --force to prevent repository size growth:
To preserve history: ./Scripts/update-gh-pages-documentation.sh --no-force
The following improvements are planned for the documentation system:
[ ] Remove default implementation - Currently, the documentation includes default implementations from protocol extensions. These can clutter the documentation and make it harder to find the primary API declarations. Future work will add filtering to hide default implementations while keeping protocol requirements visible.
[x] Migrate to GitHub Actions - Documentation deployment now runs through
.github/workflows/documentation.yml, with publication from version tags and
manual workflow runs.