DocC sans Xcode

Swift-DocC is a CLI tool to build HTML rendered documentation. It's goal is more than just reference documentation, but also long-form subjective content: tutorials, overviews, books - indeed, the Swift book itself is built with DocC, and that would be the workflow closest to what I have in mind currently (I want to use DocC to build the HTML for a standalone (to start with) Markdown file).

docc comes with Xcode. To run, I can just use:

xcrun docc

Links:

Coming back to the workflow. Apple's documentation for Writing Documentation covers all sorts of things, but it talks of a "documentation catalog". The docc CLI tool also has a lot of juicy commands, however they seem to operate on this documentation catalog, but how do we create one?

Looking at the source for the Swift Book, we can see that a documentation catalog is just (I think) a folder with a .docc extension. It also seems to have an Info.plist file (because of course it does, this is Apple). So maybe it is easier if we create this catalog using Xcode itself.

In our Xcode project, use the create new file dialog ⌘ N, press tab to focus the search field, type "doc", and select the "Documentation Catalog".

It generates a new folder for us, with a single markdown file and an empty folder. No Info.plist! So that's not needed for the minimal setup. Maybe we could've just done mkdir -p My.docc && touch My.docc/Hello.md too.

So now that we have a documentation catalog, let us preview it:

xcrun docc preview Documentation.docc

Nice, it runs a preview server for us! We can open it at http://localhost:8080/. Right now it displays "An unknown error has occurred", but that's fine, that's better than those Unexpected Exceptions at least.

Now let us add some content. I'll create a minimal markdown, replacing the template because I think the double backticked symbol reference in the autogenerated markdown is making docc unhappy.

With a flourish, if we reopen our preview window in the browser, we see, um, the same error still. So that's a bit of a let down.

Coming back to the Terminal where we ran the docc preview, we see indeed that DocC is displeased at us, and is

warning: An article is expected to start with a top-level heading title

Let's add a title then. "My Dear Pony". Hmm, still nothing.

Maybe it doesn't like my juvenile kindergarten, sense of humor. Let me give it a better title. I'll use the name of the project. But still I see the same error. Worse, docc has even stopped complaining about anything in the Terminal, it is just silently ignoring us. Maybe we do need that Info.plist after all?

Let's copy paste the Info.plist from the Swift Book into Documentation/Info.plist. Still nothing.

Let's see if there's a way to get docc to talk to us about what's wrong.

xcrun docc help preview

There's --diagnostic-level info, but let's try the --analyze first:

.../Documentation.docc/Documentation.md: note: You haven't curated \
  'doc://org.swift.tspl/documentation/The-Swift-Programming-Language/Documentation'

Maybe it's complaining because we're using the bundle identifier of the Swift book in the Info.plist. Let's remove that and try again.

Ha! docc crashes.

Maybe there isn't a way to build standalone articles using docc? Or maybe the way is there, but it is mentioned in some of the WWDC videos I haven't watched? Think, Manav, think.

If I go back to Xcode, and Product > Build Documentation action, it builds documentation for the symbols in my actual code and shows them nicely. So it is just misconfiguration somewhere.

From the Swift-DocC announcement post on Swift Forums :

At its core, the Swift-DocC compiler accepts two kinds of inputs:

  1. A folder of additional content called the Documentation Catalog, which can include:

    • Article files containing free-form Markdown describing a topic conceptually
    • ...
    • An Info.plist file containing metadata such as the name of the documented module. This file is optional and the information it contains can be passed via the command line.

So I think I need to figure out what is the Info.plist file for me, instead of just copy pasting randomly.

Let's clone the Swift book, and try to run the preview there:

git clone https://github.com/apple/swift-book.git
cd swift-book
xcrun docc preview TSPL.docc

Oops, this also gives the same error. The README mentions this should work:

Run docc preview TSPL.docc in this repository's root directory.

What am I doing wrong?

I think I need to give in trying to follow the documentation for using DocC, an irony I was trying to avoid, and instead search around on the internet.

Sure enough, I get this result in the Swift forums, someone hitting the similar roadblocks, and this encouraging reply:

Swift-DocC does indeed support building standalone documentation catalogs without an associated Swift module.

And they (the same person it seems who announced Swift-DocC) give a link to an example in the swift-docc repo, under Test fixtures. Let's give it a shot:

git clone https://github.com/apple/swift-docc.git
cd "swift-docc/Tests/SwiftDocCTests/Test Bundles"
xcrun docc preview BundleWithTechnologyRoot.docc

Nope, still the unknown error.

Finally, let me do what I should've done what as the first step - go to StackOverflow. And, indeed, there we find the solution :

DocC does not provide a "template" on its own. It expects it installed at a certain path that you don't have. You need to fetch the template and provide a path to it.

Let's follow the helpful instructions from the rest of the answer:

cd -
git clone https://github.com/apple/swift-docc-render-artifact
export DOCC_HTML_DIR=$(pwd)/swift-docc-render-artifact/dist

cd -
xcrun docc preview BundleWithTechnologyRoot.docc

It didn't work, but the error changed

The page you're looking for can't be found.

When I look more closely, the preview URL that docc printed on the Terminal now changed, and now has an additional path component. If I open http://localhost:8080/documentation/technologyx instead, it works!

Let's try this on our original file too. Let's go back to our project, delete the Documentation.docc that we were tinkering with, and recreate it from scratch.

mkdir Documentation.docc
echo World > Documentation.docc/Hello.md
xcrun docc preview Documentation.docc

Still doesn't work. Looks like we need something in there. After comparing with BundleWithTechnologyRoot.docc, I can find that if we create this minimal Markdown:

# Hello

@Metadata {
  @TechnologyRoot
}

World

paste it into a file, and run (remember, we have DOCC_HTML_DIR defined from above already):

pbpaste > Documentation.docc/Hello.md
xcrun docc preview Documentation.docc

It works indeed!

Reading around a bit more in the links I gave above, this time a bit more calmly since I've already gotten it working, I realize it wasn't the DOCC_HTML_DIR that was giving me trouble – that issue is for folks who're directly running docc. I'm running it via xcrun, so that's not the problem. The problem was that I was missing the @TechnologyRoot.

Further, it looks like this problem (requiring @TechnologyRoot) has already been fixed on GitHub, just 4 days ago! But that's on Swift-DocC mainline, and I'm using the docc that comes with Xcode, so the fix will take a while to get around to me. The good part is that future people will not to go about this whole roundabout.

So this post is mostly pointless for posterity. It just stands testament to yet another hour that I just let be.

Manav Rathi
Jan 2024