Deploying DocC with GitHub Actions

Max Cobb
8 min readOct 19, 2022

--

DocC with Swift Package Index

To save you reading through this post when all you really want to do is get DocC for your Swift Package, check out this post from Swift Package Index. The team at SPI literally build and host your documentation for your package if you add a short .spi.yml file to your project root.

What is DocC?

DocC is a documentation compiler for iOS applications or frameworks. It produces a rich API reference, as well as interactive tutorials, articles, and more.

Apple’s documentation does an excellent job of explaining DocC:

https://developer.apple.com/documentation/docc

Here’s an example of DocC builds from some repositories on GitHub:

DocC is not only the easiest way to create iOS docs; it’s best way to deliver your docs to iOS developers.

With the latest improvements from DocC we can now:

  • Build docs for Objective-C products as well as Swift
  • Toggle between Objective-C and Swift
  • Filter within the documentation
  • Publish docs directly on GitHub Pages (static website)

Build a DocC Archive

With a Swift Package open in Xcode, you can go straight to Product > Build Documentation and Xcode will generate and show your documentation in a separate window. From this window you can export the .doccarchive file.

This can be used to host on your domain, or imported by someone else to show your package’s documentation.

Alternatively, the same can be achieved from the command line. Go to the root directory of your swift package, and run the following:

xcodebuild docbuild -scheme MyPackage \
-derivedDataPath docc \
-destination 'generic/platform=iOS';

Swap out “MyPackage” with the name of your package or other scheme. For example, with the package maxxfrazer/RealityUI, you would use RealityUI. Similarly, swap out the platform if you are building for a different platform.

Dmitry pointed out to me in the comments, you may also need to add these parameters to the xcodebuild script, in the case you’re build a doccarchive for a project, rather than a package:

CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO

As the derivedDataPath is set to "docc", a new folder called docc will appear in your project root, which contains the doccarchive:

DocC Archive to Static Website

If you want to host this on a static hosting site, there’s one more step you’ll have to make, and that’s converting this archive to a static website.

The best way to make this conversion is directly with the docc command line tool built into Xcode. It's usually found at the following location within Xcode.app: Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/docc, but it's much simpler to just run xcrun --find docc to find the actual executable. docc has command process-archive and transform-for-static-hosting, which are the main things we need. Other than that, we need to add the path to the doccarchive, hosting base path, and output path.

$(xcrun --find docc) process-archive transform-for-static-hosting \
/path/to/MyPackage.doccarchive --output-path docs;

A new folder similar to this one will be created in the root of your project:

Now locally host the static site. To do this, go to the docs directory in Terminal and run python3 -m http.server.

Your documentation is now be visible at http://localhost:8000/documentation/PACKAGENAME. If you instead go to http://localhost:8000/documentation, it will list the package there for you to click through to.

If you are only hosting a single framework, it might make sense to replace index.html with a short script to redirect the user, such as:

echo "<script>window.location.href += \"/documentation/packagename\"</script>" > docs/index.html

Note that the package name must be lowercase here. If you’re not sure, check the contents of docs/documentation/. In my example I would use realityui.

This is an example of the resulting page that will be rendered:

Hosting Swift Package Docs on GitHub

For any repository on GitHub, you can create a static website that has the URL format:

https://<org-or-username>.github.io/<repo-name>

Hosting at this location works by telling GitHub where to fetch the website contents from, specifying the branch and directory. This gives a perfect place to host documentation for any Swift package.

One change we need to make before hosting these files, is adding a --hosting-base-path parameter to the Terminal command. The hosting base path needs to be the same as your repository name, as this is where GitHub hosts your static website.

$(xcrun --find docc) process-archive \
transform-for-static-hosting /path/to/MyPackage.doccarchive \
--hosting-base-path REPONAME \
--output-path docs;

Now all you need to do is push that docs directory to your repository on GitHub and head to the repository settings to tell GitHub where your static site is.

In many repositories, this is on an orphan branch called gh-pages where only the site exists at the root directory.

gh-pages branch:

GitHub repository settings:

Deploying with GitHub Actions

Now that you understand how to deploy documentation in Apple’s DocC format on GitHub pages, you won’t exactly want to re-create this manually and launch your static website every time you make a new commit or merge in a pull request.

This is why it’s highly recommended that you turn your deployment into a GitHub Action!

You can make your GitHub Action directly upload and host your static website with a feature that’s currently in beta:

GitHub Actions?

To quickly summarise, GitHub Actions is an amazing CI/CD tool from GitHub. These can be used to run tests on your code. Actions can be executed base on pull requests, every time you push to a specific branch, create a new release, or even a cron job that executes periodically. Actions can be run on a number of different runner images, including macos, which we will use for this project. Actions are free for public repositories, and have very generous usage for private repositories.

If you haven’t used GitHub actions before, I’d highly recommend taking a deeper look into it on GitHub.

Creating The Action

First, head back to the settings of your repository and set the build and deployment source as “GitHub Actions”:

This is a feature that’s currently in beta, but that doesn’t mean we can’t use it!

As with every GitHub Action, create a YAML file inside the following repository at the root of your repo: .github/workflows. For example, .github/workflows/publish-pages.yml.

For most of the action, the setting will be fairly trivial and can be modified as you see fit. Here’s an example setup, up to where the jobs are defined:

# Name your workflow.
name: Deploy DocC
on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true

From there we need to create a job that does several things:

  • Checkout the repository.
  • Build DocC documentation, as above.
  • Upload the website as an artifact.
  • Publish that artifact got GitHub Pages.

As this is building DocC, we will run this job on the macos-12 image that is provided. This is what the resulting job will look like:

jobs:
# Single deploy job since we're just deploying
deploy:
environment:
# Must be set to this for deploying to GitHub Pages
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: macos-12
steps:
- name: Checkout 🛎️
uses: actions/checkout@v3
- name: Build DocC
run: |
xcodebuild docbuild -scheme MyPackage \
-derivedDataPath /tmp/docbuild \
-destination 'generic/platform=iOS';
$(xcrun --find docc) process-archive \
transform-for-static-hosting /tmp/docbuild/Build/Products/Debug-iphoneos/MyPackage.doccarchive \
--hosting-base-path MyPackage \
--output-path docs;
echo "<script>window.location.href += \"/documentation/mypackage\"</script>" > docs/index.html;
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload only docs directory
path: 'docs'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

Remember to replace MyPackage and mypackage with your Swift Package name. And be sure to set the derivedDataPath as above, otherwise there may be some caching issues on the deployment.

That all looks quite big, but only really because of the multiple commands inside the Build DocC step. Since the end of September, the macos-12 image uses Xcode 14 as default, giving us the new DocC look.

The action upload-pages-artifact uploads the artifact with the name "github-pages", and the action deploy-pages looks for an artifact of the same name to then deploy.

If you head to the Actions tab of your repository and select the action that was just deployed, you should see that the action has succeeded like this:

Upon clicking the displayed URL you will be redirected to your documentation page!

From now on, the documentation will automatically deploy whenever you push to the main branch of your repository; or upon another action, depending on how you’ve set it up.

See an Example

For a full example of this, check out VideoUIKit-iOS repo, complete with the workflow file deploy_docs.yml, and published documentation below. This package has a DocC documentation deployed in the same method as above, and also includes an article within DocC, with more updates to come.

Current Drawbacks of DocC

Currently, if your class has some helper methods extending classes found in other frameworks, such as Foundation, those methods will not be defined within the documentation.

This is being actively worked on by the community, so expect it to be included in future versions of Swift and Xcode:

Conclusion

By following these steps, you should now be able to have your own deployment of DocC docs on your own Swift Package, or other project automatically on GitHub. Please leave a comment below with your package!

If you found this helpful, please feel free to drop some claps and a follow. 👏👏👏

--

--

Max Cobb
Max Cobb

Written by Max Cobb

spatial computing at apple. won’t be posting new content for now.

Responses (2)