DocC: Documenting Swift Code with Interactive Tutorials and Rich References
You wrote a beautifully modular Swift package, gave every type a clear name, and shipped it to your team. Two weeks
later, your Slack is full of questions: “What does RenderPolicy do?” “Which initializer should I use?” “Does this
method throw?” The code was clear to you, but without documentation, the API surface might as well be a locked door.
DocC turns your inline comments into a fully navigable, searchable, and hostable documentation website — and it ships
with Xcode, no third-party tooling required.
This post covers doc comment syntax, documentation catalogs, article pages, extension files, tutorial pages, and publishing to GitHub Pages. We will not cover third-party documentation generators like Jazzy or SwiftDoc — DocC is Apple’s first-party answer and the one that integrates directly into Xcode’s Quick Help, Developer Documentation window, and Swift Package Index.
Contents
- The Problem
- Doc Comment Fundamentals
- Documentation Catalogs
- Extension Files and Article Pages
- DocC Tutorial Pages
- Advanced Usage
- Performance Considerations
- When to Use (and When Not To)
- Summary
The Problem
Consider a production Swift package that models a Pixar rendering pipeline. The types are well-named, the access control is thoughtful, but the public API is still opaque to consumers:
public struct RenderJob {
public let sceneName: String
public let frameRange: ClosedRange<Int>
public let outputFormat: OutputFormat
public let priority: Priority
public init(
sceneName: String,
frameRange: ClosedRange<Int>,
outputFormat: OutputFormat = .exr,
priority: Priority = .normal
) {
self.sceneName = sceneName
self.frameRange = frameRange
self.outputFormat = outputFormat
self.priority = priority
}
public func submit(
to farm: RenderFarm
) async throws -> RenderReceipt {
try await farm.enqueue(self)
}
}
A teammate staring at this in Xcode’s Quick Help sees nothing. No summary, no parameter descriptions, no guidance on
which OutputFormat to choose or what errors submit(to:) can throw. They have to read the source, which defeats the
purpose of distributing a package. DocC solves this by transforming structured comments into rich, linked documentation
that lives alongside the code.
Doc Comment Fundamentals
DocC builds documentation from triple-slash (///) comments placed directly above declarations. The first paragraph
becomes the summary shown in Quick Help and code completion. Everything after the first blank line becomes the
Discussion section.
/// A single unit of work submitted to the render farm.
///
/// A `RenderJob` encapsulates the scene, frame range, and
/// output configuration needed to produce a sequence of
/// rendered frames. Jobs are immutable once created -- modify
/// the configuration by creating a new job.
///
/// ```swift
/// let job = RenderJob(
/// sceneName: "ToyStory4_Opening",
/// frameRange: 1...240
/// )
/// try await job.submit(to: mainFarm)
/// ```
public struct RenderJob { /* ... */ }
The embedded code fence inside the doc comment becomes a runnable example in the rendered documentation. DocC
syntax-highlights it and validates it during xcodebuild docbuild.
Parameter, Returns, and Throws
Document each parameter, the return value, and thrown errors using DocC’s dash-prefixed keywords:
/// Submits this job to the specified render farm for processing.
///
/// The farm validates the scene name against its asset catalog
/// before enqueuing. If the scene is not found, the method
/// throws ``RenderFarmError/sceneNotFound``.
///
/// - Parameters:
/// - farm: The render farm that will process this job.
/// - Returns: A ``RenderReceipt`` to track progress.
/// - Throws: ``RenderFarmError/sceneNotFound`` if the scene
/// name does not match any registered scene.
public func submit(
to farm: RenderFarm
) async throws -> RenderReceipt {
try await farm.enqueue(self)
}
Notice the double-backtick syntax ( RenderReceipt ). This creates a symbol link — DocC resolves it to the
actual type’s documentation page and renders it as a navigable hyperlink. Use double backticks for every reference to a
type, method, or property in your own module.
Tip: Xcode’s Editor > Structure > Add Documentation (Cmd+Option+/) generates a doc comment skeleton with parameters pre-filled. Use it as a starting point, then replace the placeholders with real descriptions.
Documenting Enums and Protocols
Every public case and protocol requirement should have its own doc comment. Consumers rarely read the source for an
enum — they rely on code completion, where the summary for each case is the only guidance they get:
/// The image format used for rendered output frames.
public enum OutputFormat {
/// OpenEXR format with 16-bit half-float channels.
///
/// Preferred for compositing pipelines that need HDR data.
case exr
/// PNG format with 8-bit RGBA channels.
///
/// Use for previews and web-resolution deliverables.
case png
/// JPEG format with configurable compression quality.
///
/// - Parameter quality: A value between `0.0` (max
/// compression) and `1.0` (lossless). Defaults to `0.85`.
case jpeg(quality: Double = 0.85)
}
Apple Docs: Writing Symbol Documentation in Your Source Files — Xcode
Documentation Catalogs
Doc comments get you summaries and parameter docs, but a real documentation site needs landing pages, conceptual articles, and curated organization. That is what a Documentation Catalog provides.
A documentation catalog is a folder with the .docc extension added to your Swift package or Xcode target. For a
package called PixarRenderKit, the structure looks like this:
Sources/
PixarRenderKit/
PixarRenderKit.docc/
PixarRenderKit.md // Top-level landing page
GettingStarted.md // Article page
Resources/ // Images, videos
render-pipeline.png
RenderJob.swift
RenderFarm.swift
The top-level Markdown file inside the catalog must have the same name as the module. It serves as the landing page and uses DocC directives to organize the documentation into groups:
# `PixarRenderKit`
A framework for submitting and managing render jobs across distributed render farms.
## Overview
PixarRenderKit provides a type-safe API for creating render jobs, submitting them to render farms, and tracking
progress.
## Topics
### Essentials
- <doc:GettingStarted>
- `RenderJob`
- `RenderFarm`
### Configuration
- `OutputFormat`
- `Priority`
- `RenderPolicy`
### Tracking and Results
- `RenderReceipt`
- `RenderProgress`
- `RenderFarmError`
The ## Topics section is where DocC diverges from standard Markdown. Each group heading under ### becomes a section
on the landing page. Symbol references use double backticks, and article references use the <doc:ArticleName> syntax.
Note: If you do not provide a
## Topicssection, DocC auto-generates one by grouping symbols alphabetically. This is rarely what you want — curate the groups manually for a coherent reading order.
Extension Files and Article Pages
Extension files let you add long-form documentation to a symbol without cluttering the source file. Article pages provide conceptual content that is not tied to any single symbol.
Extension Files
Create a Markdown file inside your .docc catalog whose title matches the symbol’s full path:
# `PixarRenderKit/RenderFarm`
@Metadata { @DocumentationExtension(mergeBehavior: append) }
## Managing Farm Capacity
A render farm distributes jobs across available nodes. When all nodes are occupied, new jobs enter a priority queue. The
farm automatically rebalances when nodes become available.
### Scaling Strategies
Use `RenderFarm/setNodeLimit(_:)` to cap the number of concurrent nodes. This is useful during off-peak hours when you
want to reserve capacity for interactive work.
The @DocumentationExtension(mergeBehavior: append) directive tells DocC to append this content to whatever doc
comments already exist on RenderFarm. Use mergeBehavior: override if you want the extension file to completely
replace the in-source documentation.
Article Pages
Articles are standalone Markdown files that explain concepts, workflows, or architectural decisions. They are not tied to a symbol:
# Getting Started with PixarRenderKit
Learn how to create your first render job and submit it to a local render farm.
## Overview
This article walks you through the three steps needed to render your first frame: creating a `RenderJob`, connecting to
a `RenderFarm`, and submitting the job.
## Creating Your First Job
Start by defining the scene you want to render and the frame range.
After the article’s prose sections, you would include code examples using standard DocC Markdown fences. Reference
articles from your landing page with <doc:GettingStarted>. DocC resolves the filename (minus the .md extension) as
the article identifier.
Apple Docs: Adding Structure to Your Documentation Pages — Xcode
DocC Tutorial Pages
DocC supports interactive, step-by-step tutorials similar to Apple’s own SwiftUI tutorials. These are authored as
Markdown files with special directives inside the .docc catalog.
A tutorial table of contents file uses the @Tutorials directive at the top level:
@Tutorials(name: "PixarRenderKit") { @Intro(title: "Meet PixarRenderKit") { Learn how to create, submit, and track
render jobs using PixarRenderKit.
@Image(source: "render-hero.png",
alt: "A rendered frame from Toy Story")
}
@Chapter(name: "Your First Render") { Create a render job and submit it to a local farm.
@TutorialReference(
tutorial: "doc:Creating-Your-First-Job"
)
} }
Each referenced tutorial is a separate file that uses @Tutorial, @Section, and @Step directives:
@Tutorial(time: 10) { @Intro(title: "Creating Your First Render Job") { Build a simple command-line tool that renders a
single frame from the Monsters Inc. scare floor. }
@Section(title: "Setting Up the Project") { @ContentAndMedia { Create a new Swift package and add PixarRenderKit as a
dependency.
@Image(source: "xcode-new-package.png",
alt: "Xcode new package dialog")
}
@Steps {
@Step {
Create a new Swift package in Xcode.
@Code(name: "Package.swift",
file: "tutorial-step-01.swift")
}
@Step {
Add PixarRenderKit as a dependency.
@Code(name: "Package.swift",
file: "tutorial-step-02.swift")
}
}
} }
The @Code directive references Swift files stored in the Resources folder of your catalog. DocC renders them with
syntax highlighting and shows diffs between sequential steps automatically.
Tip: Tutorials are excellent for onboarding new team members to internal frameworks. The effort is significant, but the payoff is a self-service learning path that replaces hours of pair programming.
Advanced Usage
Building Documentation from the Command Line
You do not need Xcode open to build DocC. The Swift Package Manager has built-in support:
# Build documentation for a Swift package
swift package generate-documentation \
--target PixarRenderKit \
--output-path ./docs
# Preview locally with a built-in web server
swift package preview-documentation \
--target PixarRenderKit
For Xcode targets, use xcodebuild:
xcodebuild docbuild \
-scheme PixarRenderKit \
-derivedDataPath ./build \
-destination 'generic/platform=iOS'
The generated .doccarchive is a self-contained bundle you can host on any static file server.
Publishing to GitHub Pages
DocC archives need a post-processing step to become hostable as static HTML. The swift-docc-plugin handles this
automatically:
// Package.swift
dependencies: [
.package(
url: "https://github.com/swiftlang/swift-docc-plugin",
from: "1.4.0"
)
]
Then generate static HTML with the --transform-for-static-hosting flag:
swift package generate-documentation \
--target PixarRenderKit \
--transform-for-static-hosting \
--hosting-base-path PixarRenderKit \
--output-path ./docs
The --hosting-base-path should match your GitHub repository name so that relative paths resolve correctly under
https://your-org.github.io/PixarRenderKit/. Push the docs folder to your gh-pages branch, enable GitHub Pages in
your repository settings, and your documentation is live.
Warning: If you forget
--hosting-base-path, all internal links will break when hosted under a subdirectory. Always set it to match your repository name.
Linking Across Modules
When your project contains multiple packages, use external symbol links to cross-reference types across module boundaries:
/// Submits this job using the shared farm from
/// ``PixarFarmManager/shared``.
///
/// This convenience method depends on
/// [PixarFarmManager](doc:PixarFarmManager) being
/// initialized before use.
public func submitToSharedFarm(
) async throws -> RenderReceipt {
try await submit(
to: PixarFarmManager.shared.activeFarm
)
}
DocC resolves cross-module links at build time as long as both modules are built in the same documentation pass. Use the
--additional-symbol-graph-dir flag to include pre-built symbol graphs from dependencies.
Conditional Documentation with @available
When APIs span multiple OS versions, document availability explicitly:
/// Renders frames using the Metal 3 mesh shader pipeline.
///
/// This method leverages hardware-accelerated mesh shading
/// for significantly faster geometry processing.
///
/// - Important: Requires Apple A17 Pro or later.
@available(iOS 17, macOS 14, *)
public func renderWithMeshShaders(
job: RenderJob
) async throws -> [RenderedFrame] {
// ...
}
DocC picks up @available attributes and renders them as availability badges on the symbol’s documentation page.
Consumers immediately see which platforms support the API.
Performance Considerations
DocC itself is a build-time tool, so it has no runtime impact on your app. However, documentation builds can affect your development workflow:
- Incremental builds: DocC supports incremental documentation builds. Only symbols whose doc comments changed are re-processed. For a package with hundreds of public symbols, this keeps rebuild times under a few seconds.
- Large catalogs: Tutorial pages with many image assets and code files increase the
.doccarchivesize. A catalog with 50+ images can produce archives exceeding 100 MB. Use compressed formats (WebP, HEIC) for screenshots and keep tutorial code files minimal. - CI integration: Documentation builds add 30-90 seconds to a typical CI pipeline depending on module size. Run them in a separate CI job to avoid blocking your main build-and-test pipeline.
For packages with extensive documentation, consider building docs only on release branches or tag pushes rather than on every commit:
# GitHub Actions: build docs only on release tags
on:
push:
tags: ['v*']
Apple Docs: Distributing Documentation to Other Developers — Xcode
When to Use (and When Not To)
| Scenario | Recommendation |
|---|---|
| Public package consumed by other teams | Use DocC with curated landing pages and full symbol docs. |
| Internal framework shared across modules | Use doc comments and a minimal landing page. Skip tutorials unless onboarding is a bottleneck. |
| App-level code not shared as a library | Doc comments for complex logic only. A full catalog is overkill. |
| Rapidly prototyping, API still unstable | Skip catalogs. Write doc comments on public API only. Invest once the API stabilizes. |
| Team currently uses Jazzy | Migrate to DocC. Jazzy is unmaintained and DocC integrates with Xcode natively. |
The decision framework is straightforward: if other developers consume your API and you want them to succeed without reading source code, DocC earns its keep. If the audience is only you and the code changes daily, inline comments suffice.
Summary
- Triple-slash (
///) doc comments are the foundation — every public symbol should have a summary, parameter docs, and return/throws documentation. - Documentation catalogs (
.doccfolders) let you add landing pages, curated topic groups, conceptual articles, and extension files that keep source files clean. - Tutorial pages provide interactive, step-by-step onboarding using
@Tutorial,@Section, and@Stepdirectives. - The
swift-docc-plugintransforms DocC archives into static HTML suitable for GitHub Pages or any static hosting provider. - DocC has zero runtime cost — it is purely a build-time and developer-experience tool.
If your package is already well-documented but you want to enforce structure at the package boundary, explore Modular App Architecture for strategies on designing clean public interfaces that make documentation natural.