1 – Start by adding doc comments to your types.
The starting point is adding a single short summary sentence as comment (using the
///) for each public type in your library or app. Feel free to add more: if you add a “blank line” (meaning include another line with
/// but nothing else in it) followed by additional content, that content appears as the “Discussion” or “Overview” for the type. This is a great place to drop in a code snippet showing how to use that type alongside the discussion of what it is and how to use it.
The reason I picked this as the starting point is that everything you add immediately becomes available through Xcode’s quick-help. You get maximum immediate benefit, as it provides content to the panel that opens when you option-click on a type.
The initial single line doc comment is referred to as the abstract. When a symbol (whether it’s a class, struct, protocol, property, or method) doesn’t have an abstract, DocC generates one for you. And that is where the much-commented-on
No Overview Available comes from.
2 – Add doc comments to your public methods and properties.
The important bit here isn’t a default initializers or functions that enable conformance to common standard library protocols. This is about the the elements on your type (struct, class, enum, or whatever) that make it unique and special. Aim to get to the core of what the symbol does – and most importantly, why you’d use it and what you need to use it. Sometimes its simple – and a single line abstract describing what it represents is more than sufficient. For those times, a single line summary (the first line with
///) does wonders. If it’s more complex, add that after a break (a line with
/// and nothing else as a doc comment), then talk to the details on how to use it, or what to expect.
If you’re writing the doc comments using Xcode, it provides a handy “generate a doc comment” command (
command-option-/ being the default key mapping to that Xcode gem) that creates a stub. If you do this for a function or method, it includes the parameters formatted correctly for DocC and placeholders for you to fill in.
If you stop right after doing this, you have the basic, effective documentation that is available to anyone loading your framework or package. But there is still a lot you can do to make it easier to use your library.
3 – Add a documentation catalog and a library overview.
Adding a documentation catalog is the 3rd thing on this list – that’s not a mistake. Everything above provides immediate benefit while you (and any collaborators) work on, or with, the source code. I think of the Documentation Catalog as being akin to the idea that you should include a README file with a project. It provides an introduction and framing to your library, and is the heart of where to learn more.
The documentation catalog is a directory with a collection of goodies. It houses the top-level overview of your library, and any assets (images, articles, and more — should you go that far). When you add a catalog and then use Product > Build Documentation within Xcode, those docs appear in the Xcode documentation window. You can export an archive for others to use from that window. There is also terminal commands to generate documentation that’s suitable for static hosting. I created an earlier post (Hosting Your Swift Library Docs on Github Pages) that goes into detail on the steps of doing just that.
When you create a documentation catalog using Xcode, its template includes a markdown file with a loose structure for what to add. It is intentionally, and specifically, structured for DocC. I recommend writing the content under the Overview heading first, and then going back and writing the summary later. The bottom section of the template (this text that starts with
## Topics and has a placeholder 3rd level heading with a generic symbol underneath) is the placeholder for you to organize the symbols of your project – the next tip.
4 – Add the top-level curation for your library.
In the world of DocC, curation is a term that means to create organization for a set of symbols. When I suggest adding top-level curation, I’m referring to adding organization to that top-page in your library, that includes the overview. This is the text beneath the overview that starts with
## Topics, followed by one or more 3rd level headers (
###) with various grouping names.
DocC started with collating only public types, methods, protocols, and so on for libraries and frameworks. As it’s evolving, the project is starting to add in the capability for documenting apps as well. In the latest Xcode 13.3 beta, when you add a catalog and build docs for an app, it includes internal types as well as public.
How you organize the symbols is subtle, but important. We have a tendency to scan down from the top – so organize the most important, or frequently used, elements near the top. Arrange symbols that have less importance beneath. DocC always builds a structure for you, even if you don’t provide one. When you provide some structure, it tries to take that into account first, and fills in any missing gaps. Docc places the symbols you don’t include in your structure beneath what you provide using a generic organization, grouped based on the type of symbol.
That generic structure isn’t too bad. It’s certainly understandable for many developers, so if you don’t get to this point, you still have useful docs due to the abstracts and overviews — that’s a win!
5 – Add a walk-through article.
Add an article that provides a walk-through of the most common, basic usage of library. The Documentation article template from Xcode looks suspiciously like the top-level organization page, but the intention of an article is quite different. It still has a structural pattern of a one-line abstract at the top (the placeholder that reads
summary in the template). Beneath that a
## Overview heading, and then a placeholder for a summary of the article. When I write an article, I leave that all alone and jump down into the content.
### heading after the overview section with content underneath it is where to start. Group what you’re showing, frequently I’ve seen this listed by task or step, by the 3rd level headings, and include the detail underneath. Intermix text and code snippets – and images if you can make the time and it makes sense. The point of what you’re making is to provide a quick overview of how to use the library, or at least one way of getting started with it. You may find there’s a lot you could say – too much for one article. That’s fine — in fact, that’s wonderful — just make more articles and add them in.
As you add an article, curate it into your documentation. By which I mean go back to that top-level document and add a link to your article. The article links look like
<doc:Your_Article's_File_Name>. When you build the documentation, the rendered content that’s dropped in place there is the top-level heading in your article, and it makes a link over to your content. An article is only useful when you get easily get to it.
I debated if this should be above the curation suggestion or not. It’s more work, certainly, but there is a ton of value. If you can’t, or don’t want, to spend a lot of time on this – focus on getting out something that shows someone wanting to use your library how to do it. There’s so much more that can be said about articles and organization, and opinions on what’s good and bad – but focus first on showing the key elements of your library, and you can build from there.
6 – Add strategic code snippets.
Hopefully your article had useful, direct code snippets in it. The next step to making that kind of directly usable content easier to find is to add code snippets into the overviews for some of your symbols. Take the time to pick the most important, or perhaps the most common, ways your library is used. I like to scribble that down on the side, and build my own little stack-rank of what I think would be most interesting to someone wanting to use my library. Then go through and add code snippets showing how to use that type, method, or function.
A pattern that I’m using at the moment is to create a unit test function that doesn’t actually test anything other than the code I just typed compiles correctly. When I’m making code snippets — both for articles and overviews in the reference section — I add a test case and put the code snippet in there. It keeps me honest with parameters and explicit formatting, and most importantly I’ve found – when the library evolves and something changes, you’ve got an immediate notice in the tests that something in the documentation needs an update there as well. I often put a comment in those “unit tests” that indicate where I used the snippet – for those times when a test fails and then I need to sort of what needs a tweak or update.
7 – Add curation to the rest of your types.
That same organization you provided for the top level of your library? Yeah – this is doing it for pieces below that top level. If you have a large class or struct, or types that include other types, this is hitting at the heart of making the list of symbols within them readily understandable.
While you can recursively do this through all your types, and a person with completion-is tendencies (which very much describes me) might want to, focus on the top level items and biggest collections. It is most useful to get all of your library’s types that include other symbols. When I’m doing this – I work down the types in the same order I added them into top-level structure. That’s assuming you ordered it most-important-first, so as long as you got that pretty close, you’re getting the most valuable pieces done first.
For types with a small number of sub-symbols (properties, enumeration cases, etc), I’ll include the organization of that symbol directly in the source code. But for a symbol with more than 4 or 5 symbols beneath it, I find it easier to manage by making an extension page in the Documentation catalog and including the curation/organization content there.
The process of adding an extension page is straightforward: in your documentation catalog (the directory ending in
.docc) add a directory named
Extensions, and then add markdown files into it. Xcode includes an extension template file type you can use. I name each of the files for the symbol that it provides organization for. Inside the extension file, include the curation. The content will look something like the following (example borrowed from in-the-works documentation updates for Nick Lockwood’s Euclid library):
# ``Euclid/Angle`` ## Topics ### Creating Angles - ``Angle/init(degrees:)`` - ``Angle/init(radians:)`` - ``Angle/degrees(_:)`` - ``Angle/radians(_:)`` ### Inspecting Angles - ``Angle/degrees`` - ``Angle/radians`` ### Common Angles - ``Angle/zero`` - ``Angle/halfPi`` - ``Angle/pi``
The reason I like using an extension file is that it doesn’t consume a lot of vertical space in the source code. I think it’s useful to have the abstract and overview as doc comments in the source, next to what they describe. I also think it’s less valuable for the organization to be there, and there is a cost to the readability of the source code. The organization isn’t the kind of thing you need to update when you’re tweaking a method’s signature or internals and how it works.
What are the symbols for my project?
When I’m tackling this curation process in adding documentation, I find it super-helpful to know the available symbols and their links. A symbol link is one of those items that represent a symbol that in text start and end with double back-ticks. For example, “
init(_:)“ is a symbol link that references to an initializer. When you have two functions that have the same signature, but different types, then DocC adds a little hash value on to the end to disambiguate them.
Unfortunately, getting a complete list is kind of pain right now. Xcode auto-completion does a pretty good job of auto-completing symbols, so you can explore with auto-completion, but I recently ran into some issues where it missed a few.
One way you can find the values is by running
xcrun docc preview on the command line, and navigating to through the rendered docs to the symbol you’re trying to find. The URL of that page has the symbol details at the end of the URL. If the symbol has a disambiguating hash code, it’s also is reflected in the URL. But that’s a lot of clicking around to get to a single value.
Another way to get the values is generating the documentation with the
--emit-digest option, and extracting what you want from there. I learned this from the DocC team members on the Swift Forums. The emit digest option adds a file to the generated content named
docs/linkable-entities.json. It’s a JSON file that includes a documentation reference for every symbol published in your docs as a documentation link. And a substring of the documentation link is what DocC uses as symbol links.
I knocked together a shell script snippet that uses the command line tools jq and other shell commands to generate a sorted list of all the possible symbols:
cat docs/linkable-entities.json \ | jq '..referenceURL' -r > all_identifiers.txt sort all_identifiers.txt \ | sed -e 's/doc:\/\/SceneKitDebugTools\/documentation\///g' \ | sed -e 's/^/- ``/g' \ | sed -e 's/$/``/g' > all_symbols.txt
The sed commands are regular-expression replacing the lines that convert the referenceURLs into symbols. It strips off the
doc://YourModuleName/documentation (the example above being pulled from a tiny library I have) from the list of URLs and adds into the double back-ticks.
I’ve started using the DocC plugin for generating documentation suitable for hosting on Github pages, but you don’t need to use that for getting this file generated. Running
docc convert with the
--emit-digest will get the same file generated for you. It’s placed in the output directory you specify for the conversion. If you’re lost on how to do all that, take look at one of the scripts that I’m using: docbuild.bash. I started doing this before the DocC plugin tool became available, so it includes all the steps from building the source, to converting the symbol graphs using DocC.