How to document wrapper types ("views")?

One thing I've been struggling with while rewriting my documentation for DocC is how to handle wrapper types/views.

These are special types, often used when there are alternative perspectives on a piece of data. There's usually a single property on the data's "main" type, which is the primary/only way to obtain an instance of the view. String is a good example of this pattern:

  • There's the regular String type, which is a collection of characters, but there's also
  • String.UTF8View (accessible via the .utf8 property),
  • String.UTF16View (via the .utf16 property), and
  • String.UnicodeScalarView (via .unicodeScalars).

Now, let's say I would like to introduce each of these views with a bit more detail; perhaps I'd like to explain a little bit about what a "unicode scalar" is and describe some unique characteristics of that view (e.g. it's a RangeReplaceableCollection, which is uncommon; the other views are not). Where do I put that documentation?

Option 1: Add the detail to the view type itself

This seems like the logical choice, but it comes with some drawbacks. In this situation, the detailed overview lives on String.UnicodeScalarView. However, that leaves the .unicodeScalars property itself with only a very basic description of why you'd actually want to access that property. In order to learn what that view offers, you need to click on the property's return type and examine the type-level documentation.

This is what String's current documentation does. The properties literally consist of one-line descriptions, like:

"A UTF-8 encoding of self."
"A UTF-16 encoding of self."

This is not really optimal. In other parts of the documentation, when I refer users to the unicode scalars view, I want to point them to the .unicodeScalars property (because that's how they actually obtain an instance of the view; that's where they should go). But without much explanation of what the view offers, it's not always obvious why I'm referring them there or how they should proceed.

Indeed, this is how String introduces these views. Notice that it talks about the "unicodeScalars property", not the "UnicodeScalarView type". It doesn't use DocC's links, but it should, and if it did, it would point to the (not very helpful) one-liner above.

This started off seeming like a good idea, but the results are sort of disappointing. So let's try the other option and see how we get on.

Option 2: Add the detail to the property.

Basically, we take all of the type's API and add it to topics on the property.

On the one hand, the overall navigation experience ends up being a lot better. I can refer to these views very naturally from other documentation, and the user gets a great experience, showing all of the methods and properties available on that view. In particular, because <some string>.unicodeScalars is the thing you actually write in code, Xcode's "Open in Developer Documentation" button can take you straight to the most relevant, most informative page, without extra clicks.

image

There are still problems though. One of the biggest is protocol conformances, which you can't organise as topics (the reference "Swift/String/UnicodeScalarView/Collection" won't resolve), and repeating that information in prose is kinda gross. But it's still important information, such as:

  • Which level of Collection conformance is actually offered (simple Collection? BidirectionalCollection? RandomAccessCollection? MutableCollection? RangeReplaceableCollection?), or
  • Whether the view is Equatable, Comparable, or Hashable, or
  • Whether it is Sendable and hence safe to use across concurrency boundaries.

Instead, this information still lives on the type's page, which has become a husk of its former self. It no longer even documents its own methods, and any users who stumble upon it might think it doesn't offer any APIs besides those conformances. We need to add a bunch links and try to make very obvious that this is not the page you're looking for.

Basically, it's the same problem that the property-level documentation had using Option 1. This way around is... better? I guess? but in truth, it's still leaving something to be desired.

Option 3: Something else?

I'm wondering if this actually warrants a new concept in DocC, which would merge a property and type and give them a single documentation page.

This isn't the same as a documentation collection, which simply groups distinct pages in a new level of hierarchy. In this model, String.UnicodeScalarView and String.unicodeScalars would share the same page, including the complete UnicodeScalarView API and all of its protocol conformances. I'm still not entirely sure how this would look, but I figured it would be worth floating the idea.

I think it makes a lot of sense, though. Unlike other properties, String.unicodeScalars or Dictionary.keys aren't really useful properties. They aren't like, say, Array.count, which returns some meaningful information; these properties truly are just gateways to the wrapper type. There is no reason for them to have independent documentation.

It might also be worth establishing some sort of authoring guidelines or best practices for how to structure common Swift patterns in documentation. These kinds of views are used throughout the standard library (Dictionary.Keys and Dictionary.Values come to mind as non-String examples), as well as in many popular first- and third-party packages.

2 Likes

Thanks for bringing this up, this is an interesting topic. The drawbacks you point out for options 1 and 2 make sense. I don't believe there is a silver bullet for this use case.

While these property/nested type pairs (e.g., String.unicodeScalars and String.UnicodeScalarView) are not associated at the language level (apart from the property being of the nested type), I do agree that organizing APIs in this way is quite common. This pattern extends to functions as well, for example zip(_:_:) and Zip2Sequence, repeatElement(_:count:) and Repeated, etc.

If this kind of relationship between members and types was formalized via some kind of language construct, DocC could use that relationship to link or share documentation across the member and the nested type. However, I'm not sure I'd see the value of such a language construct beyond the documentation use case.

Personally, what I've done in such cases has been to either duplicate parts of the prose documentation in the property and the nested type (which isn't maintainable), or curate the nested type in the member's Topics/See Also section. The latter has the benefit that via DocC's automatic curation, the nested type's page will link back to the property.

I'm wondering if DocC should provide abilities to either:

  • Include documentation from another page. You'd mark a chunk of prose in the docs for one symbol (e.g., the nested type), then add a reference to that chunk in another symbol's (e.g., the property) docs. This wouldn't help with sharing Topics/See Also sections, though.
  • Somehow mark that an API is conceptually strongly related to another API. How that is reflected in the rendered documentation would be TBD, but potentially it could result in DocC creating a standout link to the other API, including key parts of that API's documentation (e.g., its abstract), and maybe inheriting its Topics section.
1 Like

Yes, the latter option is essentially Option 2, which is what I think I'm going to do. Moving the entire nested type in to the member's documentation as you suggest feels a bit radical, but it's a good idea -- one of the things I've learnt using DocC over the past few weeks is that you shouldn't be afraid to organise the Topics hierarchy differently to the type hierarchy.

At least for me, I've found that I'm a bit reluctant to deviate too much from the type hierarchy (perhaps other developers feel the same?), but every time I have, the result is much better. It's a process. It takes time to get a feel for what's available and how best to use it.

I like this idea. A language-level concept is interesting, but it needs rigid definitions. From what I've learned using DocC so far, the documentation describes the product and the code, but it shouldn't necessarily be limited by what the language can or is interested in expressing.

We have Topics to express hierarchical relationships, but there are other kinds of relationships which can't be expressed right now. I could imagine some kind of documentation-level tag which groups two symbols together, perhaps with some kind of parameter which explains how they relate to one another.

1 Like