Hidden Gems in the Swift Argument Parser - Part II

Published: September 19, 2025
Written by:
Natan Rolnik
Natan Rolnik

The first part of this series covered generating completion scripts for Swift binaries built with the argument parser. Moreover, it explored how to improve the completion suggestions with file or directory completion, and also how to install them.

In this second part, we’ll explore other great features the argument parser provides: manual page generation, DocC generation, and the (experimental) JSON dump feature, which can be very powerful when combined with the ArgumentParserInfo library.

Manual Page

Manual pages are a form of documentation for Unix-style commands, which can be read by running the man command. Many system tools already come with manual pages installed, such as ping, mkdir, or ls. They have a very specific format and structure, and are very detailed, documenting available options and flags.

For example, the manual page for the dig command looks like this:

The manual page for the dig command
The manual page for the dig command

To quit the manual page, you can press q.

Manual Page Generation

The argument parser provides a built-in package plugin that allows generating manual pages for an executable built with it. For an existing executable tool, in the root of the package, you can run the generate-manual plugin to generate the manual page:

swift package generate-manual

By default, the manual page will be saved to the .build/plugins/GenerateManual/outputs/<executable-name> directory, and the manual page will be named <executable-name>.1. You can also specify the output directory with the -o option. To see the generate-manual plugin help, run the same command appending the --help option.

Installing the Manual Page

Manual pages can be installed in different locations, but the convention for tools not part of the system is to put them under the /usr/local/share/man directory, which is already in the default manpath. Therefore, to install the manual page, you can run the following two commands, replacing <executable-name> with the actual name of your executable:

swift package generate-manual
sudo mv .build/plugins/GenerateManual/outputs/<executable-name>/<executable-name>.1 /usr/local/share/man/man1

DocC Generation

Another type of documentation the argument parser provides (since version 1.6.0) is DocC, which you might already be familiar with from the Apple ecosystem.

To generate the DocC documentation for an executable, you also use a package plugin, but this time it’s the generate-docc-reference plugin:

swift package generate-docc-reference

This plugin will build the executable in release mode, and then generate a .docc directory with the documentation - a Markdown file in it. It will also print the location of the docc directory, next to the sources of the executable. For example, for SwiftLint, you can generate the DocC reference and find it in the following location:

The DocC directory for SwiftLint The DocC directory for SwiftLint
The DocC directory for SwiftLint

Previewing the DocC Documentation

If you have the Xcode command line tools installed, you can preview the documentation in the browser with the following command:

xcrun docc preview <path-to-docc-directory>

This will print the URL of the local server, which you can open in the browser to preview the documentation:

The DocC documentation for SwiftLint, in the browser
The DocC documentation for SwiftLint, in the browser

Serving the DocC Reference as a Static Website

Finally, you can convert the DocC reference into a static website with the following command:

xcrun docc convert <path-to-docc-directory> --output-path ./static-docc-website --transform-for-static-hosting

This will create a directory called static-docc-website with the website contents, which you can serve with any static server:

The contents for the static website with SwiftLint's DocC reference The contents for the static website with SwiftLint's DocC reference
The contents for the static website with SwiftLint's DocC reference

Important: When deploying a static website, there are two important options you need to take into account:

  • --hosting-base-path: The base path your documentation website will be hosted at. For example, if you deploy your site to example.com/my_name/my_project/documentation instead of example.com/documentation, pass /my_name/my_project as the base path.
  • When serving the website from GitHub Pages, make sure to pass the --source-service and --source-service-base-url options. To read more about these options, run xcrun docc convert --help.

The ArgumentParserInfo Library

If you want even more control over the information about your tool, such as commands, subcommands, and arguments, you can make use of the ArgumentParserInfo library.

JSON Dump

Every tool built with the argument parser has a command to generate a JSON file with all the tool information: --experimental-dump-help. The best part is that the same Codable models used to generate the JSON are available in the ArgumentParserInfo library, so you can use them to parse the JSON and get the information you need.

For example, if we run swiftlint --experimental-dump-help, we get the following JSON:

The JSON dump for SwiftLint
The JSON dump for SwiftLint

Parsing the JSON

With the JSON dump you can read all the information about a specific tool and, for example, generate a Markdown file with the information about any argument-parser-based Swift tool you have in your system.

For example, using the Command package to spin up other tools, you can do something along the lines below.

Start by declaring an AsyncParsableCommand that will be used to dump the other tools’ JSON information:

import ArgumentParser
import ArgumentParserToolInfo
import Command
import Foundation

@main
struct DocTool: AsyncParsableCommand {}

Then, add to it an argument to specify the tool name:

@Argument(help: "The name of the tool to generate documentation for")
var toolName: String

Then, implement the run method:

func run() async throws {
    // 1
    let commandRun = Command.run(arguments: [toolName, "--experimental-dump-help"])
    let result = try await commandRun.concatenatedString(including: [.standardOutput])

    guard let resultData = result.data(using: .utf8) else {
        return
    }

    // 2
    let decoder = JSONDecoder()
    let version = try decoder.decode(ToolInfoHeader.self, from: resultData).serializationVersion

    guard version == 0 else {
        // Unsupported version
        return
    }

    // 3
    let rootCommand = try decoder.decode(ToolInfoV0.self, from: resultData)

    // 4
    let outputString = rootCommand.command.generateMarkdown()
    print(outputString)
}

Although this code is a bit long, you can understand it by breaking it down into the following steps:

  1. Spin up a new process using the Command package, by calling the tool and passing the --experimental-dump-help argument. The tool must be installed in the system.
  2. Decode the result into a ToolInfoHeader model, and verify that the version of the JSON matches the version you support.
  3. Decode the result into a ToolInfoV0 model, which contains the root command information, and all children data.
  4. Generate, in another function, a Markdown string with the information about the root command, by iterating over the children. Then, print it to the standard output.

Instead of printing the Markdown string, you can allow the user to save it to a file, or use it to generate an HTML file, or any other format you want! Also, this sample code doesn’t handle errors, so take into account that you would need to think of better error messages.

Explore Further

With that, another series comes to an end!

See you at the next post. Have a good one!
Swift, Xcode, the Swift Package Manager, iOS, macOS, watchOS and Mac are trademarks of Apple Inc., registered in the U.S. and other countries.

© Swift Toolkit, 2024-2025