The state of Swift documentation

I'm not really convinced we need to coordinate with the closed source docs to make progress in this area. Most of what's been described should be possible using swift symbolgraph-extract, which is available in toolchains from 5.3 onwards and seems designed for this use case. Its output includes doc comments, type relationships, and guidance on how to format headings/subheadings/etc. of documentation pages without needing to make many individual SourceKit requests.

For example, this command can be used to generate a symbol graph for the macOS standard library, and includes all the information needed to generate documentation pages or an index:
swift symbolgraph-extract --module-name=Swift --pretty-print --output-dir /some/output/dir --target=x86_64-apple-macosx --sdk=/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.16.sdk. I don't think there's anything stopping us from integrating this into the build process and installing symbol graphs for the standard library alongside the other documentation in /usr/share/doc/swift for use by toolchain or external tools.

Partial symbolgraph output (it's very long)
{
  "metadata": {
    "formatVersion": {
      "major": 0,
      "minor": 5,
      "patch": 0
    },
    "generator": "Apple Swift version 5.3 (swiftlang-1200.0.16.9 clang-1200.0.22.5)"
  },
  "module": {
    "name": "Swift",
    "platform": {
      "architecture": "x86_64",
      "vendor": "apple",
      "operatingSystem": {
        "name": "macosx",
        "minimumVersion": {
          "major": 10,
          "minor": 15,
          "patch": 0
        }
      }
    }
  },
  "symbols": [
    {
      "kind": {
        "identifier": "swift.method",
        "displayName": "Instance Method"
      },
      "identifier": {
        "precise": "s:STsE10compactMapySayqd__Gqd__Sg7ElementQzKXEKlF::SYNTHESIZED::s:s5Int16V5WordsV",
        "interfaceLanguage": "swift"
      },
      "pathComponents": [
        "Int16",
        "Words",
        "compactMap(_:)"
      ],
      "names": {
        "title": "compactMap(_:)",
        "navigator": [
          {
            "kind": "keyword",
            "spelling": "func"
          },
          {
            "kind": "text",
            "spelling": " "
          },
          {
            "kind": "identifier",
            "spelling": "compactMap"
          },
          {
            "kind": "text",
            "spelling": "<ElementOfResult"
          },
          {
            "kind": "text",
            "spelling": ">(("
          },
          {
            "kind": "typeIdentifier",
            "spelling": "UInt",
            "preciseIdentifier": "s:Su"
          },
          {
            "kind": "text",
            "spelling": ") "
          },
          {
            "kind": "keyword",
            "spelling": "throws"
          },
          {
            "kind": "text",
            "spelling": " -> ElementOfResult"
          },
          {
            "kind": "text",
            "spelling": "?) "
          },
          {
            "kind": "keyword",
            "spelling": "rethrows"
          },
          {
            "kind": "text",
            "spelling": " -> [ElementOfResult"
          },
          {
            "kind": "text",
            "spelling": "]"
          }
        ],
        "subHeading": [
          {
            "kind": "keyword",
            "spelling": "func"
          },
          {
            "kind": "text",
            "spelling": " "
          },
          {
            "kind": "identifier",
            "spelling": "compactMap"
          },
          {
            "kind": "text",
            "spelling": "<ElementOfResult"
          },
          {
            "kind": "text",
            "spelling": ">(("
          },
          {
            "kind": "typeIdentifier",
            "spelling": "UInt",
            "preciseIdentifier": "s:Su"
          },
          {
            "kind": "text",
            "spelling": ") "
          },
          {
            "kind": "keyword",
            "spelling": "throws"
          },
          {
            "kind": "text",
            "spelling": " -> ElementOfResult"
          },
          {
            "kind": "text",
            "spelling": "?) "
          },
          {
            "kind": "keyword",
            "spelling": "rethrows"
          },
          {
            "kind": "text",
            "spelling": " -> [ElementOfResult"
          },
          {
            "kind": "text",
            "spelling": "]"
          }
        ]
      },
      "docComment": {
        "lines": [
          {
            "text": "Returns an array containing the non-`nil` results of calling the given"
          },
          {
            "text": "transformation with each element of this sequence."
          },
          {
            "text": ""
          },
          {
            "text": "Use this method to receive an array of non-optional values when your"
          },
          {
            "text": "transformation produces an optional value."
          },
          {
            "text": ""
          },
          {
            "text": "In this example, note the difference in the result of using `map` and"
          },
          {
            "text": "`compactMap` with a transformation that returns an optional `Int` value."
          },
          {
            "text": ""
          },
          {
            "text": "    let possibleNumbers = [\"1\", \"2\", \"three\", \"///4///\", \"5\"]"
          },
          {
            "text": ""
          },
          {
            "text": "    let mapped: [Int?] = possibleNumbers.map { str in Int(str) }"
          },
          {
            "text": "    // [1, 2, nil, nil, 5]"
          },
          {
            "text": ""
          },
          {
            "text": "    let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }"
          },
          {
            "text": "    // [1, 2, 5]"
          },
          {
            "text": ""
          },
          {
            "text": "- Parameter transform: A closure that accepts an element of this"
          },
          {
            "text": "  sequence as its argument and returns an optional value."
          },
          {
            "text": "- Returns: An array of the non-`nil` results of calling `transform`"
          },
          {
            "text": "  with each element of the sequence."
          },
          {
            "text": ""
          },
          {
            "text": "- Complexity: O(*m* + *n*), where *n* is the length of this sequence"
          },
          {
            "text": "  and *m* is the length of the result."
          }
        ]
      },
      "functionSignature": {
        "parameters": [
          {
            "name": "transform",
            "declarationFragments": [
              {
                "kind": "identifier",
                "spelling": "transform"
              },
              {
                "kind": "text",
                "spelling": ": ("
              },
              {
                "kind": "typeIdentifier",
                "spelling": "Self",
                "preciseIdentifier": "s:STsE4Selfxmfp"
              },
              {
                "kind": "text",
                "spelling": ".Element"
              },
              {
                "kind": "text",
                "spelling": ") "
              },
              {
                "kind": "keyword",
                "spelling": "throws"
              },
              {
                "kind": "text",
                "spelling": " -> ElementOfResult"
              },
              {
                "kind": "text",
                "spelling": "?"
              }
            ]
          }
        ],
        "returns": [
          {
            "kind": "text",
            "spelling": "[ElementOfResult"
          },
          {
            "kind": "text",
            "spelling": "]"
          }
        ]
      },
      "swiftGenerics": {
        "parameters": [
          {
            "name": "ElementOfResult",
            "index": 0,
            "depth": 1
          }
        ]
      },
      "swiftExtension": {
        "extendedModule": "Swift",
        "constraints": [
          {
            "kind": "conformance",
            "lhs": "Self",
            "rhs": "Sequence"
          }
        ]
      },
      "declarationFragments": [
        {
          "kind": "keyword",
          "spelling": "func"
        },
        {
          "kind": "text",
          "spelling": " "
        },
        {
          "kind": "identifier",
          "spelling": "compactMap"
        },
        {
          "kind": "text",
          "spelling": "<ElementOfResult"
        },
        {
          "kind": "text",
          "spelling": ">("
        },
        {
          "kind": "externalParam",
          "spelling": "_"
        },
        {
          "kind": "text",
          "spelling": " "
        },
        {
          "kind": "internalParam",
          "spelling": "transform"
        },
        {
          "kind": "text",
          "spelling": ": ("
        },
        {
          "kind": "typeIdentifier",
          "spelling": "UInt",
          "preciseIdentifier": "s:Su"
        },
        {
          "kind": "text",
          "spelling": ") "
        },
        {
          "kind": "keyword",
          "spelling": "throws"
        },
        {
          "kind": "text",
          "spelling": " -> ElementOfResult"
        },
        {
          "kind": "text",
          "spelling": "?) "
        },
        {
          "kind": "keyword",
          "spelling": "rethrows"
        },
        {
          "kind": "text",
          "spelling": " -> [ElementOfResult"
        },
        {
          "kind": "text",
          "spelling": "]"
        }
      ],
      "accessLevel": "public"
    },
8 Likes

Is swift symbolgraph-extract itself documented anywhere? It's the first time I'm hearing of this tool, maybe it's just me.

7 Likes

@Daniel_Martin This looks awesome. Thanks for making this and sharing it here.

I'm not an Emacs user, but I'd love to have keyword/attribute documentation integrated into SourceKit-LSP. I also love your idea of linking directly into the standard library source code.

@owenv Thank you for mentioning this, I had also not heard about swift symbolgraph-extract before.

If others want to try this, I had to change MacOSX10.16.sdk to MacOSX11.0.sdk for the command to work on my machine (macOS 10.15.6 with Xcode 12.0 beta 4).

Not that I know of, but the output format is described by the code here, and there's an example here of how SPM uses it.

1 Like

Thank you for the link!

The fact that it's undocumented I think is very indicative of the problem described in the original post. The amount of these small tools and infrastructure is growing, but it's also clear that documenting any of that is not a priority. The development barrier for contributing to the toolchain is as hard as it gets, where you're expected to own very powerful hardware to endure the multi-hour builds, be proficient in Python, Bash, CMake and C++ in addition to the knowledge of compiler engineering, and none of these tools and technologies intersect with what Swift developers use day to day. That could be ok and we may say that this is caused by historical reasons, and Swift developers usually aren't compiler engineers anyway etc. But the fact that the documentation in those areas is so poor exacerbates this problem to a dangerous level.

I don't want to say that this looks like gatekeeping, and I don't think that anyone made a conscious effort to make the toolchain contribution process harder. But the reality of the situation is that for anyone new who wants to contribute the only thing we can say is "go read the source code". And the more complex the toolchain and the infrastructure around it become, the harder it will be to catch up with the documentation coverage with time. We'll just end up with a small homogenous community of people who know the source code and quirks well enough and are motivated enough (or paid by the limited amount of companies who can afford it) to contribute. If we can't call it gatekeeping, it could feel like gatekeeping to new contributors.

23 Likes

I can only confirm that. I always wanted to contribute to Swift, but the source code is not exactly easy to understand for a newcomer, especially if you also aren't that experienced in the LLVM infrastructure either, which is massively used throughout the whole Swift compiler.

Instead I'm now trying to make my own little programming language (I have not very much free time, so I'm making slow progress), which also uses LLVM as the backend, so that I can learn that as well.
I sometimes use the Swift source code as an inspiration and to simplify my process of understanding the code I wanted a Xcode-project, where I can just jump around the classes and functions. Maybe there is another easy way to achieve that, but I just found in the documentation, that I have to build the entire compiler using utils/build-script --xcode, which is an extremely long process (on my recently bought computer about an hour, but on my old little MacBook it was maybe 10 hours or something crazy like that). If there is a faster way, maybe it should be documented as well.

I know that proper documenting such a massive project takes a long time and that there are not that many people who understand the source code so well, that they can properly document every function, class, etc., but I think that there is quite a big barrier for newcomers to join the development process, that could definitely be lowered by that.

4 Likes

This is exactly the reason to make the documentation efforts the top priority and to start it now. We need to make sure that the documentation improves as the project grows, right now there's an impression that the documentation coverage and quality don't keep up with all the changes. If we dismiss this as a priority, the state of things will only become worse in the next five to ten years. If the declared goal is to make Swift available for everyone on all platforms, the community should be able to participate in that instead of being excluded.

By "being excluded" I mean only the impression that people get when they are introduced to the project, just as @Zollerboy1 highlighted above. One obviously could answer "everything is open source, what else do you need? We should be happy with what we've got, it could have been closed source", but that won't help us attract more people to the community, maybe even the opposite. There's still the impression that Swift is "an Apple language" and I find it hard convincing people otherwise for exactly these reasons. A lot of it is more similar to "source code dumps" that Apple have been doing before they started using GitHub. I appreciate they started accepting pull requests, but this is only the first step and it's clearly not enough.

5 Likes

Are you talking about documenting the compiler itself?

I agree that some of that could be made clearer. For example, I remember when I first saw the project - there were lots of comments talking about "tail allocations", but I'd never heard that term before and Googling wasn't very fruitful.

That said, there will always be some required knowledge when contributing to the compiler.

  • Firstly, you need to know how compilers work - sounds obvious, but I think that is the biggest hurdle we have today. Even if the entire project was written in Swift rather than C++ and fastidiously documented, that barrier is always going to be there.

  • Secondly, you need to know how LLVM works. It's the framework upon which the compiler is built. To put it another way - if you took a complex iOS application, and you knew nothing about UIKit, you would look at the code and be just as confused.

It might be worth getting feedback from the Google folks about this, as I think many of them were already familiar with other LLVM-based compilers. How easy is it for somebody with that background knowledge to get started with the Swift codebase? I think that's probably the most relevant question to ask when it comes to the compiler.

3 Likes

I don't think any of this is stated clearly anywhere in the Swift compiler documentation. There are no links to relevant LLVM documentation there, and it's hard to say that LLVM documentation is great either. You will find a couple of introductory guides, but most of it redirects to the doxygen-generated reference, which a lot of times has either empty "No overview available" style comments, or comments that duplicate declaration names in plain English, but don't explain the purpose or give at least some examples of API usage. To exacerbate things the doxygen reference is also not searchable.

The point here is not eradicating the barrier completely, as you said it will always be there. The goal should be in lowering the barrier as much as possible and make that a priority. This will help us attract more contributors and broaden the community, which will have its own network effects as we'll have more eyes on the code, fixing bugs, adding features, and making this infrastructure more portable, or adding support for other platforms directly. This in turn will attract even more people, making the community stronger and more vibrant, ensuring that Swift as an ecosystem is viable and has wide use cases.

The spectrum of developer experience when onboarding to new complex projects is very wide, and very strongly depends on how project documentation is structured. If this hypothetical complex iOS application is poorly documented, has no references to existing resources, and partial existing documentation is not searchable and is not maintained, obviously the amount of time to get yourself comfortable with it will be very considerable.

On the other hand, a few complex iOS apps I've worked on previously had thorough description of the architecture with strict requirements on documentation coverage, and deployed documentation itself was rendered in a searchable form on CI for every commit. Even though architecture decisions made in those apps were somewhat sophisticated, I was able to make my first contribution in the matter of hours, not days as opposed to undocumented app codebases. Similarly, I witnessed that junior developers, who had very little experience with UIKit, enjoyed the onboarding process and learned a lot or got references about UIKit thanks to the detailed app documentation itself.

6 Likes

@Max_Desiatov These are all good points, but I think ideas for improving compiler documentation are straying a little from what this thread is about: which is the experience for developers writing Swift code on non-Apple platforms.

Perhaps it is worth moving the compiler discussion to a different thread.

5 Likes

I don't think that these topics are currently divorced enough from each other to discuss them separately. It would be great if they were, but one of the points in the original post was about documentation generated for the standard library and Foundation. The former uses GYB, while the latter is mostly built with the build script stored in the toolchain repository. Neither of those are well documented, and if they were, it would be related more to the compiler and the toolchain/SDK infrastructure in general.

Or, similarly, there was a suggestion above to use swift symbolgraph-extract to improve documentation for user libraries, but that tool itself is a part of the toolchain and is not documented.

3 Likes

In addition to what others have posted, here is something from my observation, along with a suggestion for improvements:

  1. Documentation of Swift Package Manager is scattered in 2 parts on swift.org: its API documentation is grouped in and accessible through the "Documentation" page, and the more tutorial-like part is grouped in the "Projects" section. Neither one provides links or references to the other.

  2. I think the Language Guide part of The Swift Programming Language is a fantastic resource for first-time learners. My only problem with it is that it omits some important information. For example:


I suggest that going forward, we should start requiring all new evolution proposals to include an evaluation on how documentation should be updated (when the documentation is closed-source like The Swift Programming Language book), and/or actually updating the documentation (when the documentation is open-source like the "docs" directory in Swift's repository)

14 Likes

Definite +1 on this. I feel like documentation, especially for a new language is going to be a must, and if everyone just documents their own stuff, it'll be no work at all. It's the existing things that are going to be hard to document. The sooner we can get change to happen, the easier our work will be.

Also, I feel that the documentation of an open source language is just as important as the language itself. We all want the language to be easy to use for both beginners and power users, and you can only get so far with the language syntax. We need official language documentation that is reliable and detailed for both beginners and experts alike.

2 Likes

@Karl is correct that my intent for this thread was to talk about improving the documentation for users of Swift. While I'd also appreciate more documentation for the compiler and related tools, I think having better language and API documentation is much more important for the Swift project.

That being said, I don't necessarily have a problem with discussing ideas for improving the compiler documentation in this thread, too. So far, the volume of posts has been manageable.

I strongly agree with this sentiment, and I think the state of the API documentation is an indicator that the impression is not unfounded.

9 Likes

Imma ditto that. I've long been impressed by Swift as a language for its versatility and relative ease-of-use, and have said as much to my coding friends. Unfortunately, most of these friends still confuse Swift with SwiftUI (and by extension UIKit), mostly riding on the impression of Swift as "an Apple thing." This perception ties naturally with the squeamishness of some around the subject of vendor lock-in, for which Apple is famous.

I believe that Swift has a place on just about every platform, but when folks need to dig around on Apple sites through Apple-esque documentation that really isn't all that great just to get started using Swift on their platform of choice, it's difficult for me to ease their worries about being "locked in to Apple's way of doing things" while showing them how great Swift can really be.


So... who do we talk to to get this rolling? What direction should we attack first? Should we work to get existing docs open-sourced? Do one of us know somebody at Apple who's in charge of their Swift documentation? Do we have a public repo already? Should we work on updating and improving Swift's existing inline docs first? I'm excited to see this rolling and to help where I can, but I just don't know where first to aim.

8 Likes

I also experience this from time to time; just linking to documentation about the Swift standard library points to the apple.com domain, enforcing the idea that Swift is an Apple language, which means that a lot of mindshare is lost.

5 Likes

This page on Swift.org was recently shown to me by someone evaluating the feasibility of Swift on Linux: https://swift.org/core-libraries/

The page ends with:

As stated above, this project is in its early days. We look forward to working together with the community to create a great set of libraries that enable Swift to produce powerful software across platforms.

That does not instill much confidence, and is it even true anymore?

7 Likes

I think what makes great a programming language are not only its features but the developer experience. Doesn't matter if your language has the coolest features and is the fastest in the world but if your are lacking of a good dependency manager, editor, and documentation, the language feels incomplete and kind of unusable, even if the language itself is very robust. Sadly for Swift historically if we talk only from the documentation perspective (as a cross platform language), feels very incomplete, and incomplete not because doesn't exists at all, incomplete because the documentation is only for apple platforms. When in the current documentation site we just see in which Xcode/AppleOS version a feature lands and not in what Swift's version, makes you think that the feature only is officially supported and only works in apple platforms. I think this one of too many reason that prevents Swift to get adopted from others when they see the language is very apple oriented.

5 Likes

Preventing Timing Problems When Using Closures is absolutely not enough in the Docs. It must has a lot of concrete instruction how to use Dispatch

Terms of Service

Privacy Policy

Cookie Policy