惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
IT之家
IT之家
小众软件
小众软件
T
Tailwind CSS Blog
博客园 - 聂微东
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
Hugging Face - Blog
Hugging Face - Blog
D
Docker
爱范儿
爱范儿
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News
MongoDB | Blog
MongoDB | Blog
N
News and Events Feed by Topic
U
Unit 42
The Register - Security
The Register - Security
宝玉的分享
宝玉的分享
J
Java Code Geeks
H
Heimdal Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
酷 壳 – CoolShell
酷 壳 – CoolShell
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
博客园 - Franky
A
About on SuperTechFans
Webroot Blog
Webroot Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Recent Commits to openclaw:main
Recent Commits to openclaw:main
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
V
V2EX
博客园 - 司徒正美
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
The GitHub Blog
The GitHub Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
A
Arctic Wolf
罗磊的独立博客
Forbes - Security
Forbes - Security
PCI Perspectives
PCI Perspectives
SecWiki News
SecWiki News
大猫的无限游戏
大猫的无限游戏
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
F
Fortinet All Blogs
博客园 - 【当耐特】
Microsoft Azure Blog
Microsoft Azure Blog

Inside Nutrient

A guide to the invisible work behind documents Introducing Nutrient Documents for Salesforce: Native document generation and signing Document AI vs. traditional OCR: Choosing between OCR, AI, and hybrid pipelines PDF SDK compliance and security evaluation checklist for enterprise teams (2026) Invariant Corp replaces paper processes with Nutrient Workflow and scales without limits What is process mapping? A complete guide Nutrient vs. Conga Composer for Salesforce document generation (2026) Document routing: How to automate document distribution The CTO’s AI playbook: Why accountability architecture beats orchestration Compliance workflow automation: Why built-in compliance is table stakes Workflow diagrams: Examples, symbols, and how to build one that actually runs Digital forms: Replace paper forms with automated workflows Approval workflow software: How to automate approvals Why document-centric automation is different The CEO’s AI playbook: Why decision architecture beats model selection Nutrient SDK product updates for Q1 2026 PDF redaction verification: How to prove sensitive data is permanently removed What is a VPAT? The complete guide to accessibility conformance reports What is PDF/UA? The accessible PDF standard explained Salesforce eSignatures: Generate, sign, and track documents in one flow Online document viewer: Options, tradeoffs, and how to embed one Document viewer for web apps: React, Vue, Angular (2026) Best document viewers in 2026: A buyer’s guide How to edit a PDF in Python: Add text, images, and annotations Nutrient advances Workflow platform with agentic AI for enterprise-grade speed and consistency in document-heavy operations How to create a Salesforce quote template from opportunity data The business case for accessibility: Five ways it drives enterprise value Python PDF library comparison (2026): 7 libraries for developers Why your AI agent hallucinates PDF table data PDF.js limitations: When to upgrade to a commercial PDF SDK How Subject scaled 5× with Nutrient’s PDF SDK without rebuilding its document layer I replaced our sales training with an AI coach that runs in Slack — here’s what broke Redirecting to: https://securitybuzz.com/cybersecurity-news/why-enterprise-permissions-are-ais-most-dangerous-inheritance/ Nutrient .NET SDK vs. iText Core: Complete comparison for .NET developers DocuVieware: Support’s most frequently asked setup questions Introducing Nutrient Workflow How to convert PDF to Word in C# (.NET) When email and spreadsheets stop working: Work order approval workflows for field teams on the move Compliance with confidence: Why document-centric automation is the foundation of your mission Nutrient expands AI Assistant, automating multistep document workflows inside any application What is document generation? A developer’s guide to PDF generation Document Converter data flow and how real-time watermarks skip the queue PDF/UA compliance guide: Requirements, standards, and best practices Computers still can’t understand you How Athena Intelligence built AI agents for regulated enterprises with Nutrient’s document infrastructure How to convert HTML to PDF (2026): 4 methods from browser print to SDK How to build a document extraction pipeline with Nutrient Vision API OCR vs. intelligent document processing: Choosing the right document extraction engine Beyond OCR: How document intelligence eliminates manual processing in regulated industries Nutrient vs. IronPDF: Complete comparison for .NET developers Nutrient vs. Aspose.PDF: Complete comparison for .NET developers Redirecting to: https://fortune.com/2026/02/19/openclaw-who-is-peter-steinberger-openai-sam-altman-anthropic-moltbook/ Lufthansa Systems uses Nutrient to deliver reliable, scalable PDF rendering for pilots worldwide Nutrient vs. Syncfusion: Complete comparison for .NET developers React’s useTransition: The hook you’re probably using wrong First City Monument Bank streamlines banking processes with Nutrient Workflow Redirecting to: https://www.sdcexec.com/warehousing/automation/article/22957364/nutrient-workflow-automation-the-missing-link-in-supply-chain-efficiency The complete guide to digital signatures: PAdES, CAdES, and XAdES explained Nutrient Python SDK: Production-grade document processing for Python Introducing agentic document editing for web applications with AI Assistant Nutrient vs. QuestPDF: Complete comparison for .NET developers How we fixed the GdPicture license expiration (and what to do if you’re affected) Red team security testing with agentic AI The future of healthcare document automation Best healthcare workflow software compared Nutrient SDK product updates for Q4 2025 How Harvey scaled legal document workflows 50 percent MoM without rebuilding infrastructure HIPAA-compliant document management in hospitals How we optimized rendering performance while handling thousands of annotations in React — Part 2 Automated PII removal with Nutrient API Redirecting to: https://www.devopsdigest.com/2026-low-code-no-code-predictions Redirecting to: https://www.kmworld.com/Articles/Editorial/ViewPoints/Leaders-predict-AI-to-continue-permeating-all-aspects-of-KM-in-2026-172594.aspx What are deep agents and how do they solve complex problems? Whipping up document magic: Your easy-bake recipe for Vue and Nutrient Web SDK 🧁 What I’ve learned about product iteration planning while building SDKs Passwordless document signing: Three-layer security guide New zip folder functionality streamlines file management in Document Automation Server The keyboard shortcuts playbook: Taking control of keyboard events in Nutrient Web SDK From experienced engineer to AI beginner: My unexpected journey AI-assisted manual testing: Handling Safari’s PDF rendering and UI quirks How to keep a 20-year-old SDK up to date How we optimized rendering performance while handling thousands of annotations in React — Part 1 Nutrient announces new executive hires to accelerate next phase of growth High performance UI using web workers Automate document conversion at scale with Python and Nutrient DCS From curiosity to PLG (and AI): My journey to understanding product-led growth Prost to progress: One year as Nutrient Pigeon usage at Nutrient: Bridging native SDKs to Flutter Modernizing CI build servers: How to migrate from Chef to Ansible Unix man pages: AI-friendly documentation since 1971 Consistent hashing for even load distribution Best AI redaction APIs: Complete comparison guide for 2025 Why AI document redaction matters for modern security From coding to coordinating: How AI transformed my workflow What is intelligent document processing (IDP)? A complete guide Enterprise PDF SDKs: Best PSPDFKit (now Nutrient) alternatives Nutrient SDK product updates for Q3 2025 GdPicture support best practices Redacting sensitive data with Nutrient AI redaction API How AI is transforming the customer experience at Nutrient: From instant answers to intelligent support
Generating API documentation for multiple targets with DocC
Stefan Kieleithner · 2024-05-29 · via Inside Nutrient

Documentation is an extremely important part of many software projects. It not only helps developers understand how to use your APIs, but it also serves as a reference guide for maintaining and extending your codebase. With the introduction of Apple’s DocC(opens in a new tab) documentation compiler, generating comprehensive API documentation has become more streamlined and efficient when developing projects on Apple platforms.

We recently shipped our new API documentation for Nutrient iOS SDK based on DocC. In this blog post, we’ll dive into some of the most interesting problems we had to solve to get our desired outcome: using DocC to generate combined documentation pages for our four frameworks.

For more on this topic, check out our Creating interactive tutorials using DocC post.

Introducing DocC

DocC is a tool provided by Apple that allows developers to generate documentation directly from source code. It extracts information from Swift and Objective-C code and compiles it into a well-formatted documentation structure. This documentation can be hosted on a website and imported to view in Xcode, making it easier for developers to understand the purpose and usage of various components within a framework or library. DocC is shipped within Xcode, but fortunately, DocC is open source(opens in a new tab), so we can follow its development, report bugs more easily, and even try out builds that are newer than what’s shipped with Xcode.

DocC uses symbol graphs, which include details about symbols — like classes, methods, properties, and more — as an intermediate format to extract information from source code and create documentation pages from it. You can create documentation for your app or SDK directly within Xcode by selecting Product > Build Documentation, or by using the command-line tool via the xcrun docc command.

Documentation for multiple targets

Our iOS SDK ships with four distinct frameworks — PSPDFKit, PSPDFKitUI, Instant, and PSPDFKitOCR — with some of them depending on others. Our goal was to ship a combined API reference for documenting all frameworks in a single place. This is because they use symbols from each other, and therefore, cross-referencing and linking between symbols from different frameworks within the documentation pages was important for us.

With high-level usage, DocC currently only supports creating documentation for a single target. There are plans for making it easier to create combined documentation for multiple targets(opens in a new tab), but for now, we have to figure out how to do this ourselves. Luckily, we talked to engineers working on DocC during labs at WWDC 2022 and 2023, and they gave us hints and encouragement on how to approach solving this particular problem.

Generating documentation with DocC is actually a two-step process: First, you need to generate symbol graphs from source code using the Swift compiler or clang. Then, you need to create the actual documentation with the docc tool.

We took a look at the docc command, which you can run via xcrun docc if you have Xcode installed. What we want to do is generate documentation, so we can use the convert subcommand.

Looking at the help via xcrun docc convert --help, we can find --additional-symbol-graph-dir, where symbol graphs are used as input files to create a documentation archive.

We can provide a path to a directory that contains multiple symbol graph files, which we can use to generate the documentation. Contrary to the argument name, symbol graphs don’t need to be additional, as they can be the only input files for docc to create a documentation archive.

Understanding symbol graphs

Symbol graphs are the main input files for DocC. These files contain all type information, documentation comments, etc. that are needed to create a fully fledged documentation output. Xcode creates these behind the scenes when building the documentation for your product, so you might not even be aware that they’re used. DocC supports creating a documentation archive from multiple symbol graphs, which is used by Xcode when you select Build Documentation. This means that building a single documentation for Swift and Objective-C symbols works, as the symbol graphs for Swift code and Objective-C code are generated individually.

We can reverse engineer how building documentation with DocC works in Xcode to replicate some of this and adapt it to our needs in a custom script. Let’s see what Xcode does when choosing Build Documentation by looking in the build logs.

Build Documentation logs in Xcode

Looking more specifically into the build steps, in the screenshot below, we see that clang is used to build a symbol graph, which uses a .symbols.json extension. This is saved in the DerivedData folder — more specifically, in this case, at .../Build/Intermediates.noindex/PSPDFKit.build/Debug-iphonesimulator/PSPDFKit.framework.build/symbol-graph/clang/arm64-apple-ios15.0-simulator/PSPDFKit.symbols.json.

Build symbol graph log in Xcode

Then we find the Compile PSPDFKit step, where we see the following arguments in the build command: -emit-symbol-graph -emit-symbol-graph-dir .../Build/Intermediates.noindex/PSPDFKit.build/Debug-iphonesimulator/PSPDFKit.framework.build/symbol-graph/swift/arm64-apple-ios-simulator.

Compile PSPDFKit build step

Finally, we see the Compile documentation build step, which now uses docc convert to create a documentation archive — which is a .doccarchive directory — for a given target. The command uses an option for setting the input symbol graphs: --additional-symbol-graph-dir .../Build/Intermediates.noindex/PSPDFKit.build/Debug-iphonesimulator/PSPDFKit.framework.build/symbol-graph.

Compile documentation for PSPDFKit framework build step

So we see multiple references to the symbol-graph folder, with files being generated in subfolders for clang and swift. If we navigate to the symbol-graph folder, we can see the symbol graph files for our target and see that the folder contains information about our symbols and their documentation comments.

If the selected scheme contains multiple frameworks to build, we can see all of the above steps multiple times in the build logs, which means that multiple separate documentation archives are being generated by DocC.

4 Compile documentation build steps

This is the part we want to change: We want to have a single documentation archive for all frameworks combined.

We can see that Xcode uses the --additional-symbol-graph-dir option for docc to create combined documentation for Objective-C and Swift APIs from a single target. But using different symbol graphs for different languages isn’t the only supported option; providing symbol graphs for multiple targets in a single call to docc convert also works. We can use this to our advantage to generate combined documentation consisting of multiple frameworks.

Generating a documentation archive containing multiple targets

Let’s give this a try. We’ll copy all the symbol graphs for each target from DerivedData to a common folder, and then we’ll run the docc command-line tool to create the documentation from these files:

cp -r ~/Library/Developer/Xcode/DerivedData/PSPDFKit-dzthlwwmacmlcwcxdmzuxhyezbpr/Build/Intermediates.noindex/*.build/Debug-iphonesimulator/*.build/symbol-graph/ ~/Documents/combined-symbol-graphs/

xcrun docc convert --fallback-display-name PSPDFKit --fallback-bundle-identifier com.pspdfkit.sdk --fallback-bundle-version 1 --output-dir ~/Documents/PSPDFKit.doccarchive --additional-symbol-graph-dir ~/Documents/combined-symbol-graphs/

This creates a PSPDFKit.doccarchive, just as expected. If we now replace convert with preview, DocC will both create the documentation archive and host a local web server so we can directly preview the generated documentation:

========================================

Starting Local Preview Server

Address: http://localhost:8080/documentation/instant

http://localhost:8080/documentation/pspdfkit

http://localhost:8080/documentation/pspdfkitocr

http://localhost:8080/documentation/pspdfkitui

========================================

The documentation is still distributed between four different links, one for each framework, which is fine. However, we can see that a symbol from PSPDFKitUI now links to the correct page in the PSPDFKit documentation. In this case, we’re looking at an initializer of PDFViewController from the PSPDFKitUI framework. Clicking Document will show the documentation for Document from the PSPDFKit (model) framework.

Cross-linking symbol in generated documentation

This is great! It means that linking to symbols in other frameworks is working. The main part of what we were trying to achieve has been done successfully. We can now extract these steps into a script so we can run the script to generate the documentation instead of needing to choose Build Documentation in Xcode and copying symbol graphs from DerivedData manually.

Creating a build script

Let’s create a script that combines all of the manual steps above. Where we used Build Documentation in Xcode before, we now use xcodebuild docbuild to generate symbol graphs.

We combine the symbol graphs from all our targets and build a documentation archive using the following bash script:

WORKSPACE_PATH="PSPDFKit.xcworkspace"

SCHEME="AllFrameworks"

DERIVED_DATA_DIR="~/Documents/docc-derived-data"

SYMBOL_GRAPHS_DIR="~/Documents/combined-symbol-graphs"

xcodebuild docbuild -destination 'generic/platform=iOS' \

-workspace "${WORKSPACE_PATH}" \

-scheme "${SCHEME}" \

-derivedDataPath "${DERIVED_DATA_DIR}"

mkdir -p "${SYMBOL_GRAPHS_DIR}"

cp -r "${DERIVED_DATA_DIR}/Build/Intermediates.noindex/*.build/Debug-iphoneos/*.build/symbol-graph/" "${SYMBOL_GRAPHS_DIR}"

xcrun docc preview \

--fallback-display-name PSPDFKit --fallback-bundle-identifier com.pspdfkit.sdk --fallback-bundle-version 1 \

--output-dir ~/Documents/PSPDFKit.doccarchive \

--additional-symbol-graph-dir "${SYMBOL_GRAPHS_DIR}"

By using this script, we got functional documentation for multiple targets working. However, we can still improve the experience for the combined API documentation.

Currently, each framework is showing only the symbols it contains in the sidebar, with no way to browse the API available for the other frameworks. Since the sidebar and the search use the same index, this also means you can only search for symbols inside the framework you’re currently viewing. Searching for symbols in another framework requires you to manually navigate to that framework using the /documentation/{framework-name} URL and start a search from there.

Since we want a combined documentation of all our frameworks, it should be possible to easily browse all symbols from all frameworks, navigate between those frameworks, and search symbols from any framework no matter the current page. By default, showing multiple modules in the sidebar when combining the symbol graphs isn’t supported, and only the symbols of a single framework are shown.

To improve this behavior, we’ll add a documentation catalog(opens in a new tab). A documentation catalog can contain supplemental content that isn’t available in the documentation you add to your API directly in the source code. A documentation catalog is just a regular folder with contents inside, so we can create a Documentation.docc folder.

DocC supports manual ordering of symbols. We can use the topics group(opens in a new tab) to do that.

We can also add another top-level page in addition to the module pages via @TechnologyRoot(opens in a new tab). This will act as our landing page and our collection of listing all frameworks.

If you add links to modules in the topics group of a page marked with @TechnologyRoot, these modules — and all the symbols inside them — show up in the sidebar.

So let’s add a markdown file called Overview.md to the Documentation.docc folder with the following contents:

# PSPDFKit Documentation

@Metadata {

@TechnologyRoot

}

## Topics

- `/PSPDFKit`

- `/PSPDFKitUI`

- `/Instant`

- `/PSPDFKitOCR`

Then we add the Documentation.docc catalog as an input file to our docc command:

xcrun docc preview "./Documentation.docc" \

--fallback-display-name PSPDFKit --fallback-bundle-identifier com.pspdfkit.sdk --fallback-bundle-version 1 \

--output-dir PSPDFKit.doccarchive \

--additional-symbol-graph-dir "${SYMBOL_GRAPHS_DIR}"

Adding the technology root with the topics group will add a /documentation/overview page showing all of the modules in expandable sections in the sidebar, just as we wanted.

Documentation with a sidebar listing all frameworks

And searching symbols across frameworks from the overview page is now also supported.

Searching across frameworks in the documentation

However, when navigating to the pages inside these modules, the sidebar doesn’t contain any content.

Empty sidebar in framework pages

This behavior is slightly different when using DocC from the open source repo, at least when using the main branch at the time of writing from this commit(opens in a new tab). The sidebar is correctly populated in Swift, even for pages inside modules. However, as soon as you switch to Objective-C, the sidebar still shows Swift symbol names, which breaks search for Objective-C names. We filed an issue on the Swift-DocC repo(opens in a new tab) to potentially get this issue fixed in a future build, but for now, let’s find a workaround.

To fix this, we need to figure out where the sidebar actually gets its content from. I found that the sidebar is using the /index/index.json file in the generated documentation archive as its source.

When inspecting this file, we see a JSON hierarchy that looks like this:

{

"interfaceLanguages": {

"occ": [],

"swift": [{

"children": [{

"children": [...],

"path": "/documentation/pspdfkit",

"title": "PSPDFKit",

"type": "module"

}, {

"children": [...],

"path": "/documentation/pspdfkitui",

"title": "PSPDFKitUI",

"type": "module"

}, {

"children": [...],

"path": "/documentation/instant",

"title": "Instant",

"type": "module"

}, {

"children": [...],

"path": "/documentation/pspdfkitocr",

"title": "PSPDFKitOCR",

"type": "module"

}

],

"path": "/documentation/overview",

"title": "PSPDFKit Documentation",

"type": "module"

}]

},

"schemaVersion": { "major": 0, "minor": 1, "patch": 1 }

}

The entries in occ, which refers to Objective-C, are empty. Therefore, the sidebar would be empty when switching to Objective-C in the language switcher. And swift contains only one entry for the /documentation/overview page.

As a workaround for the Objective-C entry being empty, we add another technology root page, this time making it Objective-C-only by specifying @SupportedLanguage(objc):

# PSPDFKit Documentation

@Metadata {

@TechnologyRoot

@SupportedLanguage(objc)

}

## Topics

- ``/PSPDFKit``

- ``/PSPDFKitUI``

- ``/Instant``

- ``/PSPDFKitOCR``

This will ensure occ is also populated in the index file.

Then we can add the following Ruby script to modify the index.json file to add entries for our other frameworks, copying the same children data that the overview pages use:

json_file_path = '~/Documents/PSPDFKit.doccarchive/index/index.json'

data = JSON.parse(File.read(json_file_path))

def add_toplevel_objects(language_root_object)

language_object = language_root_object[0]

frameworks_to_add = [

{ 'path' => '/documentation/pspdfkit', 'type' => 'module' },

{ 'path' => '/documentation/pspdfkitui', 'type' => 'module' },

{ 'path' => '/documentation/instant', 'type' => 'module' },

{ 'path' => '/documentation/pspdfkitocr', 'type' => 'module' }

]

frameworks_to_add.each do |framework|

new_object = Marshal.load(Marshal.dump(language_object))

new_object['path'] = framework['path']

new_object['type'] = framework['type']

language_root_object << new_object

end

end

occ_object = data['interfaceLanguages']['occ']

add_toplevel_objects(occ_object)

swift_object = data['interfaceLanguages']['swift']

add_toplevel_objects(swift_object)

File.write(json_file_path, JSON.pretty_generate(data))

We’re modifying files in the .doccarchive, so after we build our documentation archive using docc, we need to execute this script.

This change now enables the sidebar to be shown with all symbols from all frameworks on every page in the API documentation, enabling easy browsing between symbols of different frameworks.

Final PDFViewController documentation page with working sidebar

Since the sidebar and the search feature use the same index file, you can now also search for any symbols using the built-in search (by pressing /) across all of the modules without first navigating to the specific module page.

Advanced topics in DocC

DocC offers a range of advanced features that allow you to create and customize your documentation in powerful ways. Here are some advanced topics to explore:

  • Using extension files — Extension files enable you to add custom content to your documentation. This can include additional explanations, examples, or even custom tutorials that enhance the standard documentation.
  • Creating custom documentation catalogs — Organize your content more effectively by creating custom documentation catalogs. These catalogs can help you structure your documentation in a way that makes sense for your users, providing a more intuitive navigation experience.
  • Interactive coding exercises — DocC supports interactive coding exercises, allowing you to create immersive learning experiences. These exercises can help developers practice using your API or framework in a hands-on way, reinforcing their understanding.
  • Customizing appearance and behavior — Tailor the look and feel of your documentation using DocC’s theme and layout options. This customization can help your documentation align with your brand and provide a more cohesive user experience.
  • Integration with other tools — Integrate DocC with other tools and services, such as web servers and continuous integration pipelines. This can streamline your documentation workflow and ensure your documentation is always up to date and accessible.

By exploring these advanced features, you can produce rich API reference documentation that not only informs but also engages and educates your users.

Conclusion

While navigating DocC’s current limitation of supporting documentation for only a single target, we dove deeper to consolidate documentation for all our frameworks into a single archive. By combining the documentation for different frameworks, we enhanced the user experience of our API documentation, enabling seamless navigation and search capabilities across all frameworks. With the help of scripts like the one discussed in this post, developers can effortlessly generate and deploy high-quality documentation for their projects, enhancing the overall developer experience.

FAQ

DocC is Apple’s documentation compiler that creates structured API documentation from Swift and Objective-C code, supporting both website and Xcode hosting.

Yes. By manually combining symbol graphs, DocC can generate unified API documentation for multiple frameworks.

You generate symbol graphs for each framework and combine them using the --additional-symbol-graph-dir option to create a single documentation archive.

By using custom documentation catalogs and topic groups, you can organize frameworks in the sidebar for easier navigation and unified search.

Switching between Swift and Objective-C documentation may cause navigation issues, which can be mitigated by adding a dedicated technology root page for Objective-C symbols.