# A preview of DocC's support for combined documentation

**URL:** <https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373>\
**Category:** Swift-DocC\
**Created:** [September 4, 2024, 10:38am UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373 "2024-09-04T10:38:04Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ronnqvist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ronnqvist/32/20110_2.png) [@ronnqvist](https://forums.swift.org/u/ronnqvist)\
**Post date:** [September 4, 2024, 10:38am UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/1 "2024-09-04T10:38:04Z")

</div>

Hello,

I'm happy to let you know that there's now enough pieces of [combined documentation of multiple targets](https://forums.swift.org/t/combined-documentation-of-multiple-targets/59579/1) in place across Swift-DocC, Swift-DocC Render, and the [Swift-DocC Plugin](https://swiftlang.github.io/swift-docc-plugin/documentation/swiftdoccplugin) to enable you to try out an early, and still experimental, version of this feature for the use-case of hosting combined documentation for a Swift package together.

I've created [a tiny Swift package example](https://github.com/d-ronnqvist/combined-documentation-example) that hosts [its combined documentation](https://d-ronnqvist.github.io/combined-documentation-example/documentation) on GitHub Pages as a preview of what's supported today with the latests Swift-DocC Plugin version (1.4.2) and the Swift-DocC and Swift-DocC Render in the latest main [Swift development toolchain](https://www.swift.org/install/macos/#development-snapshots). Below is a screenshot of [one of those pages](https://d-ronnqvist.github.io/combined-documentation-example/documentation/outer/outerclass/):

 ![A screenshot of one of the pages from the combined documentation example with the left-hand side navigation expanded|100%](https://global.discourse-cdn.com/swift/original/3X/b/4/b49f8d1efc73ee837113606cb998a2abe4636bf6.png)

A few things you may notice on this page:

- The navigation hierarchy on the left-hand side displays the hierarchies for both targets in this package. This means that Quick Navigation—activated via the `/` key/button—can navigate to pages in both targets.
- The top of the navigation hierarchy, displays the name of this Swift package. Clicking on this element takes you to a minimal synthesized landing page for these targets. The same page can be reached at `/documentation`.
- The content of this page (from the "Outer" target) contains authored symbol links to pages from the "Inner" target. These symbol links are written with a leading slash followed by the module name and the rest of the path to the symbol. For example: `/Inner/InnerClass`.
- The "Inherits From" and "Conforms To" symbol relationships are automatically turned into links to those symbols (from the other target). If you navigate to `someProperty` or `someFunction()` you may also notice that the `InnerValue` elements in their declarations is a link to that page.

## Try this yourself

Building combined documentation for multiple targets with the Swift-DocC Plugin is easy. When you add the new `--enable-experimental-combined-documentation` flag to your existing `swift package generate-documentation` call and specify one or more targets using `--target <target-name>` or `--product <product-name>` , the plugin will build all the specified targets, configure them to support linking to their target dependencies' documentation, and output a documentation archive with their combined documentation.

If you're using a main Swift development toolchain you'll get a combined navigation hierarchy and a minimal synthesized landing page, like in the example above. You can [download a Swift toolchain](https://www.swift.org/install/macos/#development-snapshots) for your platform and follow the instructions on that page or use [the Swiftly CLI](https://github.com/swiftlang/swiftly?tab=readme-ov-file#command-interface-overview)to install a Swift development toolchain. You can use the development toolchain to build documentation with the Swift-DocC plugin by passing the toolchain path to the `swift package` CLI:

```shell
swift package \
  --toolchain /path/to/the-toolchain \
  generate-documentation \
  --enable-experimental-combined-documentation \
  ...

```

You can also use a Swift 6.0 toolchain but if you do, each target will get a separate navigation hierarchy and you won't have any landing page. You can still write links from targets to their dependencies and symbol relationships are still automatically turned into links. Note: the Swift 6.0 toolchain won't receive any further updates related to combined documentation.

We'd love for you to try this on your Swift packages and give us your feedback on what's already there, suggest enhancements, and help us find bugs. Note that this feature is experimental and may receive breaking changes.

One feature that's mentioned in [the combined documentation of multiple targets thread](https://forums.swift.org/t/combined-documentation-of-multiple-targets/59579/1) which isn't available yet is the ability to write your own landing page and package-level articles (that aren't about to any particular target). We have some ideas for how we want to do this but we want to discuss those ideas—in a separate, future Forums thread—and wanted to let you try out what's already available before that.

---

<div class="post-metadata">

**Author:** ![JaanusK](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jaanusk/32/18026_2.png) [@JaanusK](https://forums.swift.org/u/JaanusK)\
**Post date:** [September 9, 2024, 6:57am UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/2 "2024-09-09T06:57:42Z")

</div>

I have trouble getting some backreferences to work.

I made these changes to this example project, and then compiled the web documentation.

```swift
jaanus@jk-mbp ~/D/combined-documentation-example (main)> git diff
diff --git a/Sources/Inner/Inner.swift b/Sources/Inner/Inner.swift
index 2741ffe..4724b64 100644
--- a/Sources/Inner/Inner.swift
+++ b/Sources/Inner/Inner.swift
@@ -23,6 +23,8 @@
 public protocol InnerProtocol {}
 
 /// A class in the inner target.
+///
+/// See also OuterClass: ``/Outer/OuterClass``
 open class InnerClass {}
 
 /// A public value in inner target.
diff --git a/Sources/Outer/Outer.docc/Outer.md b/Sources/Outer/Outer.docc/Outer.md
index 765cd01..9eb594b 100644
--- a/Sources/Outer/Outer.docc/Outer.md
+++ b/Sources/Outer/Outer.docc/Outer.md
@@ -1,3 +1,5 @@
 # ``Outer``
 
 The outer module of this combined-documentation example.
+
+See also the Inner class: ``/Inner/InnerClass``

```

The change to `Outer` is rendered as expected:

 ![Screenshot 2024-09-09 at 09.54.29](https://global.discourse-cdn.com/swift/original/3X/9/0/90709a636370df6664cd469da1dce1f76d190c0f.png)

However, the change to `InnerClass` is not rendered as expected:

 ![Screenshot 2024-09-09 at 09.54.40](https://global.discourse-cdn.com/swift/original/3X/4/f/4f971f25d0ee97a3196c3c01a65a241208c4e5af.png)

Could this be because `Outer` has `Inner` defined as a dependency in `Package.swift`, but not vice versa? So the `Inner` documentation generation does not have access to `Outer` symbols, and thus won’t render links to those.

---

<div class="post-metadata">

**Author:** ![ronnqvist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ronnqvist/32/20110_2.png) [@ronnqvist](https://forums.swift.org/u/ronnqvist)\
**Post date:** [September 10, 2024, 9:17am UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/3 "2024-09-10T09:17:56Z")

</div>

> [@JaanusK](#):
>
> Could this be because `Outer` has `Inner` defined as a dependency in `Package.swift`, but not vice versa? So the `Inner` documentation generation does not have access to `Outer` symbols, and thus won’t render links to those.

Correct. Each target's documentation can only link to its direct dependencies' documentation. This is described in the original pitch post and there's also [a section that talks about what some of the tradeoffs would be](https://forums.swift.org/t/combined-documentation-of-multiple-targets/59579#bidirectional-links-9) of allowing bidirectional links and how that creates an inconsistent experience if one of the targets can be imported individually.

Note that you wouldn't be able to configure the "Inner" target to depend on the "Outer" target because the code doesn't allow for cyclic dependencies (not documentation specific).

---

<div class="post-metadata">

**Author:** ![ronnqvist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ronnqvist/32/20110_2.png) [@ronnqvist](https://forums.swift.org/u/ronnqvist)\
**Post date:** [September 10, 2024, 9:34am UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/4 "2024-09-10T09:34:17Z")

</div>

We've yet to see many practical use cases for linking to specific symbols "up" the documentation hierarchy (for example inferred from the target's source dependency hierarchy). A few examples have been brought up but we felt that there were better ways to write that documentation which didn't involve bidirectional links. For example:

- The inner target that wants to point to an example implementation for conforming to a protocol.

- A package's top-level documentation wants to point to a list of consumers.

- Documentation about how to use an outer symbol and an inner symbol together.

- Packages with one or more example projects

---

<div class="post-metadata">

**Author:** ![mackoj](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mackoj/32/42395_2.png) [@mackoj](https://forums.swift.org/u/mackoj)\
**Post date:** [October 1, 2024, 8:56am UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/5 "2024-10-01T08:56:55Z")

</div>

Hello @ronnqvist I did tried it and it's crashing with the toolchain (swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain).

```bash
swift package \
  --toolchain "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain" \
  --allow-writing-to-directory "../docs/develop" \
  generate-documentation \
  --target VitaminPlay \
  --target AccentColor \
  --target Accordion \
  --target ArticleCard \
  --target Assets \
  --target Button \
  --target Carousel \
  --target Checkbox \
  --target Chip \
  --target DesignTokens \
  --target Divider \
  --target Fonts \
  --target IconButton \
  --target Link \
  --target Loader \
  --target Price \
  --target ProductCard \
  --target ScoreRating \
  --target Shared \
  --target StarRating \
  --target Sticker \
  --target TabView \
  --target TextEditor \
  --target TextField \
  --target VitaminToggle \
  --output-path "../docs/develop" \
  --transform-for-static-hosting \
  --enable-experimental-combined-documentation

```

```swift
Generic signature: <τ_0_0, τ_0_1 where τ_0_0 : View, τ_0_1 : Downloader, τ_0_1.Id == URL, τ_0_1.Value == Image>
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Pass '-Xfrontend -disable-round-trip-debug-types' to disable this assertion.
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
0.	Program arguments: /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend -frontend -c -primary-file /Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Carousel+CardContent.swift /Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Carousel.swift /Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Debug/CardContentDebugOptions.swift /Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Models/CardContentViewModel.swift /Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Models/CardModel.swift /Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Models/CardViewModel.swift /Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Models/Progress.swift -emit-dependencies-path /Users/mac-JMACKO01/Developer/generate_doc/code/.build/arm64-apple-macosx/debug/Carousel.build/Carousel+CardContent.d -emit-reference-dependencies-path /Users/mac-JMACKO01/Developer/generate_doc/code/.build/arm64-apple-macosx/debug/Carousel.build/Carousel+CardContent.swiftdeps -target arm64-apple-macosx11.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk -I /Users/mac-JMACKO01/Developer/generate_doc/code/.build/arm64-apple-macosx/debug/Modules -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib -F /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -color-diagnostics -enable-testing -g -debug-info-format=dwarf -dwarf-version=4 -module-cache-path /Users/mac-JMACKO01/Developer/generate_doc/code/.build/arm64-apple-macosx/debug/ModuleCache -swift-version 5 -Onone -D SWIFT_PACKAGE -D DEBUG -empty-abi-descriptor -Xcc -working-directory -Xcc /Users/mac-JMACKO01/Developer/generate_doc/code -resource-dir /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/lib/swift -enable-anonymous-context-mangled-names -file-compilation-dir /Users/mac-JMACKO01/Developer/generate_doc/code -Xcc -isysroot -Xcc /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk -Xcc -F -Xcc /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -Xcc -fPIC -Xcc -g -module-name Carousel -package-name code -in-process-plugin-server-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/lib/swift/host/libSwiftInProcPluginServer.dylib -plugin-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/local/lib/swift/host/plugins -target-sdk-version 15.0 -target-sdk-name macosx15.0 -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/local/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -parse-as-library -o /Users/mac-JMACKO01/Developer/generate_doc/code/.build/arm64-apple-macosx/debug/Carousel.build/Carousel+CardContent.swift.o -index-store-path /Users/mac-JMACKO01/Developer/generate_doc/code/.build/arm64-apple-macosx/debug/index/store -index-system-modules
1.	Apple Swift version 6.1-dev (LLVM 89ccf4b8a46135a, Swift 6a5ae8d5df144dd)
2.	Compiling with effective version 5.10
3.	While evaluating request IRGenRequest(IR Generation for file "/Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Carousel+CardContent.swift")
4.	While emitting IR SIL function "@$s8Carousel11CardContentV4body7contentQr7SwiftUI014_ViewModifier_C0VyACyxq_GG_tFAF0H0PAFE8onChange2of7performQrqd ___yqd__ ctSQRd __lFQOyAF08ModifiedC0VyAlFEAmnOQrqd___ yqd __ctSQRd__ lFQOyAlFEAmnOQrqd ___yqd__ ctSQRd __lFQOyAlFEAmnOQrqd___ yqd __ctSQRd__ lFQOyAlFE0J6AppearAOQryycSg_tFQOyAlFE7overlay_9alignmentQrqd ___AF9AlignmentVtAfKRd__ lFQOyAlFEAT_AUQrqd ___AWtAfKRd__ lFQOyAL6SharedE2if_9transformQrSbyXK_qd __xXEtAfKRd__ lFQOyAlFE5frame5width6heightAUQr12CoreGraphics7CGFloatVSg_A5_AWtFQOyAlFE7gesture_9includingQrqd ___AF11GestureMaskVtAF7GestureRd__ lFQOyAlFE10preference3key5valueQrqd __m_5ValueQyd__ tAF13PreferenceKeyRd __lFQOyAlFE8position1x1yQrA4__ A4_tFQOyAlFEA_A0_A1_AUQrA5 __A5_AWtFQOyAlFE0U6EffectyQrSo17CGAffineTransformVFQOyAF6ZStackVyAF05TupleH0VyAF7ForEachVySaySiGSiAlFE0J9DisappearAOQrAS_tFQOyAlFEArOQrAS_tFQOyAlFE0J10TapGesture5countAOQrSi_yyctFQOyAlFEA17_A18_A19_QrA4__ A4_tFQOyAlFEA_A0_A1_AUQrA5 __A5_AWtFQOyAlXEAY_AZQrSbyXK_qd__ xXEtAfKRd __lFQOyAlFEAT_AUQrqd___ AWtAfKRd __lFQOyAC0bH033_52A3C2B362B4C7D5ECC1F72FBFD8C439LLVyxq__ G_AF5GroupVyAF012_ConditionalC0VyAlFE6border_A0_Qrqd ___A4_tAF10ShapeStyleRd__ lFQOyAlFEA_A0_A1_AUQrA5 __A5_AWtFQOyAlFE4fontyQrAF4FontVSgFQOyAlFE10background_AUQrqd___ AWtAfKRd __lFQOyAF4TextV_AF5ColorVQo__ Qo __Qo__ A51_Qo_AF05EmptyH0VGGQo __AlFE6zIndexyQrSdFQOyAlFE11scaleEffect_6anchorQrA4__ AF9UnitPointVtFQOyA60 __Qo__ Qo_Qo __Qo__ Qo __Qo__ Qo __Qo_SgGSg_A28_ySnySiGSiAlFEA17_A18_A19_QrA4__ A4_tFQOyAlFEA_A0_A1_AUQrA5 __A5_AWtFQOyAF5ShapePAFE6stroke9lineWidthQrA4__ tFQOyAF9RectangleV_Qo __Qo__ Qo_GSgA28_yA77_SiAlFE16allowsHitTestingyQrSbFQOyAlFEA61_yQrSdFQOyAlFEA17_A18_A19_QrA4 __A4_tFQOyAlFEA_A0_A1_AUQrA5__ A5_AWtFQOyAF6VStackVyA26_yAlFEA_A0_A1_AUQrA5 __A5_AWtFQOyA79_AFE4fill_5styleQrqd___ AF9FillStyleVtAFA42_Rd __lFQOyA83__ A51_Qo __Qo__ A49_tGG_Qo __Qo__ Qo __Qo_GSgtGG_Qo__ Qo __Qo__ AX21ReadSizePreferenceKeyVQo __AF13_EndedGestureVyAF15_ChangedGestureVyAF19GestureStateGestureVyAF11DragGestureVAC15GestureProgressA34_LLOyxq__ GGGGQo __Qo__ AlFE7clipped11antialiasedQrSb_tFQOyAlFE0E5Shape_6eoFillQrqd ___SbtAFA78_Rd__ lFQOyA129 __A83_Qo__ Qo_Qo __AlFEA89_yQrSbFQOyA38_yA40_yAlFEA47__ AUQrqd ___AWtAfKRd__ lFQOyA91_yA26_yA49 __A49_A49_tGG_A51_Qo_A57_GG_Qo_Qo__ A38_yA40_yAlFEA89_yQrSbFQOyA97 __Qo_A57_GGQo__ Qo __SiQo__ SiQo__A4_Qo_AX17AnimationCompleteVyA4_GG_A29_Qo_AF13GeometryProxyVcfU_".
 for expression at [/Users/mac-JMACKO01/Developer/generate_doc/code/Sources/Components/Carousel/Carousel+CardContent.swift:204:20 - line:410:5] RangeText="{ proxy in
      // Carousel is represented here with e `ZStack` containing all the cards that is moved
      // horizontally with drag gestures or animated to a particular card.
      ZStack {
        /// Ignore view if there are no items.
        if numberOfItems > 0 {
          /// The `wideIndex` refers to the index when loop is enabled for wrapped around cards
          /// that are "out of rage" e.g. in the negative or grater than the card count.
          ForEach(visibleIndexes, id: \.self) { wideIndex in
            let index = convertLoopIndexToInRange(wideIndex, count: numberOfItems)

            // This check is for when changing the `numberOfItems` when things go our of sync for a
            // short while.
            if index < cardContentViewModel.cardViewModels.count {
              // This is the individual card view.
              CardView(cardViewModel: cardContentViewModel.cardViewModels[index],
                       placeholderView: placeholderView,
                       debugOptions: debugOptions)
              .overlay(
                Group {
                  if debugOptions.showWideIndex {
                    Text("\(wideIndex)")
                      .foregroundColor(Color.black) // Use `.foregroundStyle` in iOS 15.
                      .background(Color.white.opacity(0.5))
                      .font(.body)
                      .frame(height: .infinity)
                      .border(.orange)
                  } else {
                    EmptyView()
                  }
                }
                , alignment: .top)
              .if(debugOptions.disableScale == false) {
                // Scaling effects for selected index.
                $0
                  .scaleEffect(selectedIndex == wideIndex ? 2 : 1)
                  .zIndex(zIndex(forCardIndex: wideIndex))
              }
              .frame(width: cardLength, height: cardLength)
              // position uses the top leading position.
              .position(
                x: CGFloat(wideIndex) * cardLength + cardLength/2,
                y: cardLength/2
              )
              .onTapGesture {
                // Tap on a visible card to jump (animate) to it.
                jump(toIndex: wideIndex)
              }
              .onAppear { progress?(Progress(index: index, state: .onAppear)) }
              .onDisappear { progress?(Progress(index: index, state: .onDisappear)) }
            }
          } // ForEach
        }

        if debugOptions.showDebugBorders {
          ForEach(0..<numberOfItems, id: \.self) { index in
            Rectangle()
              .stroke(lineWidth: 2)
              .frame(width: cardLength, height: cardLength)
              .position(
                x: CGFloat(index) * cardLength + cardLength/2,
                y: cardLength/2
              )
          }
        }

        if debugOptions.showRulers {
          // This debug option shows a ruler with offset values for each card.
          ForEach(0..<numberOfItems, id: \.self) { index in
            VStack {
              Rectangle()
                .fill(.blue)
                .frame(width: 2, height: cardLength * 1.5)

              Text(String(format: "%.02f", -1 * CGFloat(index) * cardLength))
                .font(.caption)
            }
            .frame(width: 50, height: cardLength * 2)
            .position(
              x: CGFloat(index) * cardLength + cardLength/2,
              y: cardLength/2
            )
            .zIndex(10)
            .allowsHitTesting(false)
          }
        }
      } // ZStack
      // Groups the geometry so all the positions are moved at once. Use `.geometryGroup()` when
      // target is in iOS 17.
      .transformEffect(.identity)
      // The size of the `ZStack` is the with of all the cards.
      .frame(width: cardLength * CGFloat(numberOfItems), height: cardLength, alignment: .leading)
      .position(
        // Position is based around the center of the view, move it to be the offset minus half the
        // width plus half the view the ZStack sits inside plus half a card width.
        x: offset + centerOffset
           + (cardLength * CGFloat(numberOfItems))/2
           + proxy.size.width/2 - cardLength/2,
        y: proxy.size.height/2
      )
      // So the proxy size is stored.
      .preference(key: ReadSizePreferenceKey.self, value: proxy.size)
      .gesture(DragGesture()
        // Using `updating` to get the "gesture started" events.
        .updating($gestureState) { value, state, transaction in
          switch state {
            case .inactive:
              state = .started

              let startingOffset = offset(fromIndex: selectedIndex, withOffset: value.translation.width)
              // TODO: Modifying state during view update, this will cause undefined behavior.
              offsetAtDragStart = startingOffset
              print("drag started offset: trans \(value.translation.width), "
                    + "index \(selectedIndex), "
                    + "offset \(startingOffset) == \(offsetAtDragStart). thread: \(Thread.current)")
            case .started:
              state = .changed
            case .changed:
              break
          }
        }
        .onChanged { dragGestureChanged(translationX: $0.translation.width) }
        .onEnded { dragGestureEnded(translationX: $0.translation.width)}
      ) // gesture
      // Use the zoomed card height for the width.
      .frame(width: proxy.size.width, height: cardLength * 2)
      .if(debugOptions.clipViews) {
        $0
          .contentShape(Rectangle()) // Blocks hit testing
          .clipped() // Clips the view
      }
      .overlay(
        Group {
          // Debug view to show the state of the visible and pre-fetched cards.
          if debugOptions.showFetchDebugView {
            VStack {
              Text(.init(debugIndexesString()))
              Text("Length: \(Int(cardLength))")
              Text("offset: \(Int(offset))")
            }
            .background(Color.white.opacity(0.85))
          } else {
            EmptyView()
          }
        }
        .allowsHitTesting(false)
        , alignment: .top)
      .overlay(
        Group {
          // Line for the debug ruler option.
          if debugOptions.showRulers {
            Rectangle()
              .fill(.black)
              .frame(width: 1, height: cardLength * 3)
              .allowsHitTesting(false)
          } else {
            EmptyView()
          }
        }
      )
      .onAppear {
        print("updateVisibleIndexesAtRest - onAppear")
        // Update the visible cards when the view first appears.
        updateVisibleIndexesAtRest()
      }
      // TODO: iOS 17 use `.onChange(of: selectedIndex) { oldValue, _ in }`
      .onChange(of: numberOfItems) { newValue in
        // If the number of items changes we need to update the collection of Card View Models.
        updateCardViewModels(numberOfItems: newValue)
      }
      .onChange(of: selectedIndex) { [selectedIndex] newValue in
        // Keep the old index up to date.
        oldIndex = selectedIndex
      }
      .onChange(of: offset) { [offsetAtDragStart] newValue in
        // Update on change of `offset` so new visible cards are shown when dragging.
        if debugOptions.updateOnOffset && updateVisible {
          let range = min(offsetAtDragStart, newValue)...max(offsetAtDragStart, newValue)
          print("updateVisibleIndexesAtRest - onChange(of: offset) range: \(range) "
                + "... offsetAtDragStart: \(offsetAtDragStart)")
          visibleIndexes = calculateVisibleIndexes(selectedIndex: selectedIndex,
                                                   offsetRange: range,
                                                   numberOfItems: numberOfItems,
                                                   itemWidth: cardLength,
                                                   viewWidth: viewSize.width,
                                                   centerOffset: centerOffset)
        }
      }
      .onAnimationCompleted(for: offset) {
        // Update the visible cards at the end of the animation for `offset`. This isn't always at
        // the end of a gesture.
        if debugOptions.updateOnAnimationComplete {
          print("updateVisibleIndexesAtRest - onAnimationCompleted")
          // Updates on drag (instead of `.onChange(of: dragOffset)`), as well as when a on tap
          // animation has finished.
          offsetAtDragStart = offset(fromIndex: selectedIndex, withOffset: 0)
          updateVisibleIndexesAtRest()
        }
      }
      .onChange(of: visibleIndexes) { [visibleIndexes] newVisibleIndexes in
        print("\(visibleIndexes) => \(newVisibleIndexes)")
        print("fetching: \(fetchableIndexes)")
        // When the visible cards change, fetch content for all the necessary cards.
        cardContentViewModel.fetchCardModels(fetchableIndexes: fetchableIndexes)
      }

    "
 #0 0x0000000105e1782c (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x10572382c)
 #1 0x0000000105e15ff8 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x105721ff8)
 #2 0x0000000105e17e74 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x105723e74)
 #3 0x0000000195da6584 (/usr/lib/system/libsystem_platform.dylib+0x18047a584)
 #4 0x0000000195d75c20 (/usr/lib/system/libsystem_pthread.dylib+0x180449c20)
 #5 0x0000000195c82a30 (/usr/lib/system/libsystem_c.dylib+0x180356a30)
 #6 0x0000000105f56408 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x105862408)
 #7 0x0000000100e59820 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x100765820)
 #8 0x0000000100e59b6c (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x100765b6c)
 #9 0x0000000100e523bc (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x10075e3bc)
#10 0x0000000100e94718 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x1007a0718)
#11 0x0000000100e93e2c (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x10079fe2c)
#12 0x0000000100d2dcc0 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x100639cc0)
#13 0x0000000100e44aec (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x100750aec)
#14 0x0000000100e93390 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x10079f390)
#15 0x0000000100e4d45c (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x10075945c)
#16 0x0000000100e469ec (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x1007529ec)
#17 0x000000010093f2a4 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x10024b2a4)
#18 0x000000010093bc2c (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x100247c2c)
#19 0x000000010093b08c (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x10024708c)
#20 0x000000010094749c (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x10025349c)
#21 0x000000010093cf58 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x100248f58)
#22 0x000000010093c624 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x100248624)
#23 0x000000010072ac50 (/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-09-25-a.xctoolchain/usr/bin/swift-frontend+0x100036c50)
#24 0x00000001959eb154

```

I did fill a feedback FB15344007 if you want to test this on your side.

Hope it helps.

---

<div class="post-metadata">

**Author:** ![ronnqvist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ronnqvist/32/20110_2.png) [@ronnqvist](https://forums.swift.org/u/ronnqvist)\
**Post date:** [October 3, 2024, 8:33am UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/6 "2024-10-03T08:33:53Z")

</div>

With the information that's available to me this I'm not sure if this is a documentation specific error or not.

Does a regular build of those same targets with that toolchain succeed or does it encounter the same error?

---

<div class="post-metadata">

**Author:** ![colbrew](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/colbrew/32/37627_2.png) [@colbrew](https://forums.swift.org/u/colbrew)\
**Post date:** [October 4, 2024, 8:46pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/7 "2024-10-04T20:46:34Z")

</div>

Am I right that this won't work for swift packages for iOS? I'm getting the error `- error: no such module 'UIKit'` because the code includes UIKit imports. If so, will there be support for combined documentation using `xcodebuild docbuild` at some point?

---

<div class="post-metadata">

**Author:** ![Joseph\_Heck](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joseph_heck/32/22369_2.png) [@Joseph\_Heck](https://forums.swift.org/u/Joseph_Heck)\
**Post date:** [October 6, 2024, 8:01pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/8 "2024-10-06T20:01:13Z")

</div>

> [@colbrew](#):
>
> will there be support for combined documentation using `xcodebuild docbuild` at some point?

While it's a completely reasonable question, `xcodebuild docbuild` isn't something that the working group can (or will) answer - since it's entirely dependent on internal Apple development priorities, which aren't shared.

Some features in DocC (snippets, in particular) are missing support in docbuild, even though they've been available in the public OSS toolchains for over three years. That's a fairly complete feature, where mixed synthesis of iOS, macOS, and other platform symbols is something technically possible, but essentially hand crafted today and not something that's "easily" supported in even the open-source available workflows (docc-plugin, etc).

---

<div class="post-metadata">

**Author:** ![colbrew](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/colbrew/32/37627_2.png) [@colbrew](https://forums.swift.org/u/colbrew)\
**Post date:** [October 7, 2024, 12:13pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/9 "2024-10-07T12:13:42Z")

</div>

Thanks @Joseph_Heck for the context on `xcodebuild docbuild` vs. `swift package`. Yeah, our large enterprise iOS app environment has been migrating our internal docs to DocC, and combined documentation/top-level landing page would be a huge benefit. Unfortunately, many of our libraries depend on `UIKit` so looks like a non-starter at the moment. Appreciate the work you and @ronnqvist are doing on this - I tried building the docs for the tiny example linked above and it worked great!

---

<div class="post-metadata">

**Author:** ![creativecat](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/creativecat/32/36665_2.png) [@creativecat](https://forums.swift.org/u/creativecat)\
**Post date:** [November 20, 2024, 5:39pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/10 "2024-11-20T17:39:25Z")

</div>

> [@Joseph\_Heck](#):
>
> While it's a completely reasonable question, `xcodebuild docbuild` isn't something that the working group can (or will) answer - since it's entirely dependent on internal Apple development priorities, which aren't shared.

I'm also interesting in `xcodebuild docbuild` support given that current `swift package` seems to only build macos documentation. We're currently unable to build our documentation using it `swift package` due to error: [Several Errors related to macos version · Issue #100 · swiftlang/swift-docc-plugin · GitHub](https://github.com/swiftlang/swift-docc-plugin/issues/100) so looking for an alternative solution to combine documentation from multiple targets/modules.

---

<div class="post-metadata">

**Author:** ![Joseph\_Heck](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joseph_heck/32/22369_2.png) [@Joseph\_Heck](https://forums.swift.org/u/Joseph_Heck)\
**Post date:** [November 20, 2024, 5:51pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/11 "2024-11-20T17:51:29Z")

</div>

> [@creativecat](#):
>
> I'm also interesting in `xcodebuild docbuild` support given that current `swift package` seems to only build macos documentation.

More specifically, `swift build` always builds for the host operating system on which it's running, with the exception of the cross-compilation targets. The DocC system works from whatever swift build generates. Apple doesn't provide an easy path to build for iOS (or its other platforms) without using Xcode.

The key here is getting the symbol graphs from the compilations to other targets (for example, iOS), after which you can merge/mix/etc to get the various graphs together. Apple's doing that for their internal documentation, but it's not a capability of the docc-plugin, at least at this point. `docc-plugin` is more or less a coordinator over the builds, extracting the relevant symbol graphs from the compiler, and then hands them to the raw DocC executable, which is the bit that's responsible for actually mixing those together with a documentation catalog to generate the completed archive.

---

<div class="post-metadata">

**Author:** ![creativecat](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/creativecat/32/36665_2.png) [@creativecat](https://forums.swift.org/u/creativecat)\
**Post date:** [November 22, 2024, 8:10pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/13 "2024-11-22T20:10:54Z")

</div>

Thanks! Our issue is the same as @colbrew. Our large app includes several modules, but also depends on UIKit for some of them. If the swift package plugin doesn't support this, then we have to resort to `docbuild` but `docbuild` doesn't support the combined documentation being discussed here.

It sounds like it is possible to still get what we want using `docbuild`, but it's unclear what all the pieces are and if there are any examples of the right approach for bringing all the pieces together.

---

<div class="post-metadata">

**Author:** ![Joseph\_Heck](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joseph_heck/32/22369_2.png) [@Joseph\_Heck](https://forums.swift.org/u/Joseph_Heck)\
**Post date:** [December 30, 2024, 5:22pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/14 "2024-12-30T17:22:41Z")

</div>

I was recently poking at this new feature ([Using DocC for package with multiple modules - #7 by wigging](https://forums.swift.org/t/using-docc-for-package-with-multiple-modules/76699/7)), and found that preview-documentation doesn't appear to work with this option, only generate-documentation.

Does anyone have a handy one-liner (that uses python or such) to run a quick local server to emulate what's happening with preview documentation to visually see the results?

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [December 30, 2024, 5:26pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/15 "2024-12-30T17:26:40Z")

</div>

Did you try `$ python -m http.server`? This will start a HTTP server on port 8000.

---

<div class="post-metadata">

**Author:** ![Joseph\_Heck](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joseph_heck/32/22369_2.png) [@Joseph\_Heck](https://forums.swift.org/u/Joseph_Heck)\
**Post date:** [December 30, 2024, 5:46pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/16 "2024-12-30T17:46:51Z")

</div>

> [@wigging](#):
>
> python -m http.server

Does that always serve up whatever is in the current working directory? Or how do you "aim it" at a documentation archive to get the content visible? (I'm expecting to want to serve and display all content from the root of a `doccarchive`

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [December 30, 2024, 5:53pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/17 "2024-12-30T17:53:02Z")

</div>

By default it serves the files in the current working directory where you executed the command. You can specify a different directory with the optional `-d` parameter.

---

<div class="post-metadata">

**Author:** ![creativecat](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/creativecat/32/36665_2.png) [@creativecat](https://forums.swift.org/u/creativecat)\
**Post date:** [January 6, 2025, 9:58pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/19 "2025-01-06T21:58:33Z")

</div>

> [@ronnqvist](#):
>
> We've yet to see many practical use cases for linking to specific symbols "up" the documentation hierarchy (for example inferred from the target's source dependency hierarchy). A few examples have been brought up but we felt that there were better ways to write that documentation which didn't involve bidirectional links. For example:

The output of this feature is exactly what our team is looking for (a single site with all modules/targets listed and searchable). However, we can't use the package plugin given it doesn't support building UIKit.

The original proposal thread [Combined documentation of multiple targets - #24 by ronnqvist](https://forums.swift.org/t/combined-documentation-of-multiple-targets/59579/24) mentions the use of `docc` `convert` combined with `merge`.

I see that `merge` command was added to the docc subcommand, but I have yet to find any examples of how to use it, how it should be combined with other commands, the sequence of commands, and how it relates to what's being discussed here.

Essentially, is there a way to use this experimental feature outside of the swift package plugin or a way to accomplish the same output using `docc` and `docbuild`?

I posted a separate thread here in case it needs a deeper dive. [Docbuild and Merge Multiple Docc Catalogs](https://forums.swift.org/t/docbuild-and-merge-multiple-docc-catalogs/77013)

---

<div class="post-metadata">

**Author:** ![coenttb](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/coenttb/32/44217_2.png) [@coenttb](https://forums.swift.org/u/coenttb)\
**Post date:** [February 20, 2025, 7:58am UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/20 "2025-02-20T07:58:03Z")

</div>

```swift
swift package \
    generate-documentation \
    --enable-experimental-combined-documentation \
    --target Outer \
    --target Inner

```

Using this command, it results in a `CombinedDocumentationExample.doccarchive`. I double-click it, and it shows Imported documentation: Inner. But no Outer.

I'd appreciate some further guidance, as I can't see the cross-target references working.

---

<div class="post-metadata">

**Author:** ![ronnqvist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ronnqvist/32/20110_2.png) [@ronnqvist](https://forums.swift.org/u/ronnqvist)\
**Post date:** [February 20, 2025, 1:00pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/21 "2025-02-20T13:00:25Z")

</div>

> [@coenttb](#):
>
> I double-click it, and it shows Imported documentation: Inner. But no Outer.
> 
> I'd appreciate some further guidance, as I can't see the cross-target references working.

To preview this feature, please view the hosted documentation in a web browser. Any functionally specific to Xcode is out of scope for this thread.

---

<div class="post-metadata">

**Author:** ![mbrandonw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mbrandonw/32/349_2.png) [@mbrandonw](https://forums.swift.org/u/mbrandonw)\
**Post date:** [April 22, 2025, 5:06pm UTC](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373/22 "2025-04-22T17:06:34Z")

</div>

> [@ronnqvist](#):
>
> We've yet to see many practical use cases for linking to specific symbols "up" the documentation hierarchy (for example inferred from the target's source dependency hierarchy).

Hi @ronnqvist, thanks for providing a run down of all the ways you've seen inner-to-outer linking come up, and how one can avoid the situation. I've run into a situation where I need inner-to-outer linking, and while it seems related to some of the situations you've outlined, I still don't know a path forward.

Many of our libraries need to be split into two modules: a "core" one, and then one that depends on the core to provide a bit more functionality. I believe even a number of the official Swift packages do this too.

The most common reason for this is due to macros and their high cost of compiling SwiftSyntax. For example, our newly released StructuredQueries library has a [StructuredQueriesCore and StructuredQueries](https://github.com/pointfreeco/swift-structured-queries/blob/f157e6036aaf29e80771485e3efcf1177bfb76ae/Package.swift#L41-L54) target where core does not have any macros. That way our other libraries (and 3rd parties) can depend on StructuredQueriesCore to get the vast majority of functionality of the library without incurring the cost of macros.

And macros are not the only way this comes up. We recently had to do this in our [SnapshotTesting](https://github.com/pointfreeco/swift-snapshot-testing/blob/70575026488894b948e97204582ceea748c5fb3c/Package%40swift-6.0.swift#L14-L21) library too because we provided Swift Testing support to the library and unfortunately Swift Testing cannot be imported into UI test targets. This forced us to split the functionality into a "core" module that did not access any Swift Testing symbols, and then another that does access those symbols.

So, all of that is to say, many of our libraries naturally split into two modules, and that can complicate documentation. So far we've treated the core modules as the primary place to document since that is where 99% of the symbols exist, and this includes all of our articles too. Then in the non-core module we just provide a basic landing page that tells people that the majority of the documentation lives elsewhere and we link them to the core module.

It works, but it's not ideal. There are two main problems. First, when a user is reading the documentation, they see that they are reading the docs for "XYZCore" rather than just "XYZ". That can be confusing because they never actually do `import XYZCore`. And second, the core module can't link out to any of the symbols in the non-core module, such as the macros.

So, this does seem like a situation where linking from inner-to-outer is useful. It's needed anytime we are required to split up a module into two, not because they contain different functionality, but merely because it provides a better experience for our users. We'd prefer to keep everything in just one module, but that often is just not possible (as is the case with macros and Swift Testing problems).

Do you have any advice for this situation?

[Next page](https://forums.swift.org/t/a-preview-of-doccs-support-for-combined-documentation/74373.md?page=2)
