Named groups in "See Also"

I've been really enjoying Swift-DocC so far, and one thing I particularly like are topics. The ability to group a set of related properties and methods in a logical group makes it a lot easier to organise larger APIs.

For example, in WebURL, I've grouped the members of the main WebURL type: initialisers which parse URL strings are in one topic, URL components (.hostname, .path and the like) in another topic, and more detailed information about network hosts (such as .host and .origin) in another topic. This works really well:

Also really useful are the "see also" sections. They're great for referring to related functionality in a more prominent way than links in the body text. Declaring them is really easy; I just added the following to the documentation comment for the username property to add its throwing setter equivalent, setUsername, to the end of the document:

  /// ## See Also
  ///
  /// - ``WebURL/setUsername(_:)``
  ///

And it shows up with a nice, big reference that you'll notice even if you skim most of the body text :sweat_smile:. The rest of the section is filled with other items from the property's topic.

But they have a problem: they're not named, and I can't find any syntax which allows me to group these references in to named sections (I tried the regular ### Header sections; didn't work). DocC's guide doesn't mention "See Also" sections at all. Ideally, I'd have that section say "Responding to Setter Failures" rather than "Related Documentation", to really grab people's attention as they're taking a glance.

Now, I did find one workaround, but I'm unsure how wise it is. I can add these references as topics, and get the interface I wanted. For example, adding this to the hostname documentation comment:

  /// ## Topics
  /// ### Network Hosts and Origins
  ///
  /// - ``WebURL/host-swift.property``
  /// - ``WebURL/Host-swift.enum``
  ///
  /// ### Responding to Setter Failures
  ///
  /// - ``WebURL/setHostname(_:)``
  ///

Produces exactly what I want. Users who view the hostname property, will see in a really visible way that there's also this host property which gives them more direct information, better suited to establishing a network connection. This is actually a snippet from the sibling topic "Network Hosts and Origins", filtered to only the declarations that are relevant here.

Unfortunately, things start to break when you build webs of topics in this way. It seems that arranging declarations in to topics establishes a level of hierarchy that's important to DocC's internal model, and cyclic topic references mean some symbols just don't show up.

The SwiftC guide mentions collections as a possible solution: by grouping large topics and having a way to refer to them from elsewhere, but it also warns against creating too much hierarchy.

So I have a few questions:

  1. Is this an unwise use of Topics? The documentation describes them only as "groups with meaningful names", but it seems that DocC uses them for something else internally. Why can't I create cyclic topic references?

  2. Is it possible to name groups in "See Also" so I don't need a full topic declaration?

  3. Is it possible for a "See Also" section to refer to a topic other than splitting it out as a collection? In this case, I'd like the WebURL.hostname property, in the "Reading and Writing a URL's Components" topic, to have a "See Also" reference to the "Network Hosts and Origins" topic (also on the WebURL type). Ideally I'd also like to filter that reference to the most salient declarations.

3 Likes

Hey Karl,

I don't have a full answer for you, but in general the See Also segments are generated from how you've organized those symbols within whatever grouping they're in, and the name of the section under See Also comes from there. It's a tad difficult to talk about in the abstract.

As an example, I'm cobbling some documentation for Automerge (current work in progress in a branch: https://github.com/heckj/automerge-swift/tree/docc-basics if you're so inclined - it's public).

If you grab that branch and build the docs, you'll see a top-level grouping that looks like the following:

Then if you click through into the symbol Document, you'll see the following "See Also" section at the bottom:

The title and what's included comes from the symbols that were included in the top level grouping parallel to it.

The same mechanism can be use for symbols within a class, struct, etc if you group those together (what the documentation coverage metrics that I've just uncovered describe as 'curation' or 'curated symbols'). That's something I've only recently clued in needs to be done with an extension markdown document that appends data (the 'curation' or organization content) onto a symbol that's documented within the source.

That said, I'm uncertain if you can add your own "See Also" section, and add additional symbols, or manually generate that section if you want to. I'll need to defer to Ethan, Franklin, or whomever knows the original source to answer that.

Hi Karl,

Excited to hear you've been adopting Swift-DocC!

I also don't have a definitive answer here, but I can chime in on a few points. And I think @BoisyPitre may be able to provide some more informed answers from an authoring perspective on how to best use See Also and Topics sections.

The reason cyclic topic references are discouraged is because they're used to create the navigation hierarchy for Swift-DocC's generated navigator index that can be used by IDEs and other documentation viewers. For example, it's your Topic sections that determine how your framework's symbols are laid out in the Xcode documentation window's navigation sidebar.

In some cases having cyclic references there is actually okay and if there's a specific use case you'd like to enable please let us know. But in most scenarios it's better to have every symbol appear once in the navigator.

Since "See Also" sections are not used for navigator index creation they are better for creating those cyclic references or generally referencing symbols that are already organized into the navigator index via a Topics section on a different page.

From my perspective, this is a bug that we should fix. You should be allowed to have named groups in the "See Also" section. If you'd like to file a bug on bugs.swift.org, that would be much appreciated :slight_smile:

There are authoring reasons you may want to keep See Also sections small enough that you don't need headings (and @BoisyPitre can likely speak to these) but I don't think we want Swift-DocC to enforce that as an arbitrary restriction if you have a use case.

Oh this is interesting. This isn't possible today but would be a good feature request. So essentially you'd want to define a topic group by referencing one authored elsewhere? Something like:

/// My docs.
///
/// ## Topics
///
/// ### My Topic Heading
/// Description of my topic group.
///
/// - ``Other Symbol``
public struct Foo {}

/// My other docs.
///
/// ## Topics
/// - <doc:Foo#My-Topic-Heading>
public struct Bar {}

Let me know if I'm understanding that correctly :slight_smile:

It would also be interesting to use a reference to an authored topic group as a "hint" to DocC's automatic topic group generation. (And maybe this is closer to what you're requesting.)

1 Like

@Joseph_Heck you're correct here on how the automation See Also section generation works. I just wanted to chime in on a couple points.

I may be misunderstanding you here, but while we encourage you to use documentation extensions to organize your symbols into Topics sections, this shouldn't be a requirement.

For example, I would expect the following to work just as well in a documentation comment or in a documentation extension.

## Topics

### My Topic Heading

- ``MySymbol``

Extensions are just encouraged if you want to keep your documentation comments more focused for developers reading them in your codebase directly.

See Also sections can be manually created as well. "See Also" is a reserved second-level heading just like "Topics". So the following should work today:

## See Also

- ``MySymbol``
1 Like

Thanks @ethankusters - I was trying that over the weekend, but the content wasn't reflecting in my documentation build, so I thought it had to be in an extension. Thank you for correcting me - as I'd much prefer to keep the organization for the methods, properties, etc. in a single document as comments in the original source file.

1 Like

Hi @Karl,

As @ethankusters indicated, DocC automatically populates the See Also section with the "peer" symbols grouped one level above with the article you're working on.

To give you my perspective as a writer, I almost always (~99% of the time) let DocC do its thing with the See Also section and don't add to it. The idea is that since I've curated the grouping where the article resides in a logical way with related symbols, the See Also section should reflect that.

There are those few times when an article or symbol in another area of the documentation may be relevant. In that instance, I would call out that article or symbol in the See Also section.

But in my experience, those times are rare.

DocC's use of peer symbols in the group one level up from the article you're working on means that for n symbols in a group, each symbol will have n-1 references in its See Also section. In practice, I work to keep the number of symbols in a group down to 8 items max. That means the See Also section would have a maximum of 7 references. That's not the norm, however. Typically a group has 2-4 symbols, meaning the See Also for each symbol will have 1-3 references.

My suggestion is to not overthink or overuse the See Also section, and let your grouping decisions in the level above dictate how the See Also section plays out. If you find that there's a specific symbol or article. you want to call out, consider putting it in the peer group above the article you're working on and let DocC handle it automatically.

Just my .02 :)

4 Likes

Thanks, that's very helpful. I did notice the issues with Xcode, and switched some of my method and property-level Topics back to "See Also" sections (despite the lack of named groups :sob:).

Filed! [SR-15543] Support named sections in "See Also" ยท Issue #203 ยท apple/swift-docc ยท GitHub

Sort of, but I'd actually like to include some "See Also" elements by referencing a topic authored elsewhere. So what I have is:

/// _The_ Type. You know the one.
/// 
/// ## Topics
///
/// ### This is how we Foo it
/// 
/// - ``TheType/FooType``
/// - etc...
///
/// ### Then we hit the Bar
///
/// - ``TheType/BarType``
/// - etc...
///
struct TheType {

   /// It's a Foo.
   ///
   /// ## See Also
   /// 
   /// ### <doc:TheType/Topics/Then we hit the Bar>
   /// - ``TheType/BarType``
   ///
   struct FooType {
   }
}

So when I go to TheType's documentation, I see these groups with FooType and methods related to foo-ing it, and there's another group with BarType and methods related to that. That's a reasonable, logical grouping when viewing at the level of TheType.

But when I'm looking at FooType's documentation, it might have a need to remind me that BarType will be happening later (via an entry in the "See Also" section), and that it's part of the "Then we hit the Bar" Topic. But rather than including that entire topic, it can select a few particular entries that are relevant to the thing you're looking at.

Basically: it's named "See Also" sections, but the names are links to an inline (non-collection) Topic defined elsewhere, and the DocC compiler will tell me if the Topic name changes, or the linked APIs are ever moved from that Topic.

Thanks very much for the advice! I definitely appreciate a writer's take on things. With all of the capabilities DocC provides, the challenge is actually writing good documentation and structuring things appropriately, and that is something I (and probably many developers) need to learn to do better.

The challenge in this case is that for some large projects, there are multiple logical groupings. For example, this project is a URL library. On one level, URLs are textual strings. But they also contain components - a network address, path, etc.

  • People coming from other libraries or doing specialised tasks will expect these components to also be available as strings (network host name string, path string, etc - and there are 8 of these!), so it makes sense to have those string interfaces be grouped in a single Topic.

  • But - some of these components have internal structure; a path like "/foo/bar/baz" is actually a list of segments, network addresses have a known structure, etc. The URL type can provide an additional level of information, beyond plain strings, and each of these is a separate Topic.

This is a conflict of meaning vs. form: on one level, it makes complete sense that the path string should live next to information about its own segments in a single Topic, because they have overlapping meaning. On the other hand, there is an even stronger attachment to members which actually have quite distinct meanings, but similar form. Part of this attachment is based on familiarity and what readers will expect.

DocC's generated references for these members will then also show related APIs with similar form, which is not incorrect. But I think it justifies augmenting that with a reference to APIs with an exceptionally strong attachment in terms of meaning.

It is a delicate balance, though. Too much prodding and "Did You Mean?"s can be counterproductive
:paperclip::eyes: