The fastest HTML to PDF library for Swift
⚡ 1,939 PDFs/sec • 💾 35 MB memory (4-24 workers) • 🎯 Type-safe • 🧪 Swift 6
Every other solution makes you choose: fast or safe or easy.
This library gives you all three.
@Dependency(\.pdf) var pdf
try await pdf.render(html: "<h1>Invoice #1234</h1>", to: fileURL)
One line. Zero configuration. Production-ready.
Continuous Mode (single-page, maximum speed):
Batch Size | Throughput | Avg Latency | Memory |
---|---|---|---|
100 | 1,772/sec | 0.56ms | 146 MB |
1,000 | 1,939/sec | 0.52ms | 146 MB |
10,000 | 1,814/sec | 0.55ms | 148 MB |
Paginated Mode (multi-page, print-ready):
Batch Size | Throughput | Avg Latency | Memory |
---|---|---|---|
100 | 142/sec | 7.05ms | 102 MB |
1,000 | 677/sec | 1.48ms | 110 MB |
10,000 | 485/sec | 2.06ms | 137 MB |
Test environment: macOS 26.0, Apple Silicon M1 (8 cores), 24 GB RAM, Swift 6.2
Memory usage doesn't scale with concurrency:
Concurrency | Steady-State | Peak | Expected |
---|---|---|---|
4 workers | 34 MB | 34 MB | 400 MB |
8 workers | 34 MB | 35 MB | 800 MB |
16 workers | 35 MB | 35 MB | 1,600 MB |
24 workers | 35 MB | 35 MB | 2,400 MB |
Why? Shared WebKit infrastructure. Memory determined by pool overhead, not worker count.
Measured empirically with 50+ PDF warmup, sustained rendering workload. See WebViewMemoryTests.swift
for methodology.
Memory stays constant during extended batches:
- 500 PDFs with 8 concurrent: Peak 98 MB, range 74 MB (includes startup), steady-state variance <5 MB
- No leaks. No accumulation. Constant memory regardless of batch size.
import HtmlToPdf
import Dependencies
@Dependency(\.pdf) var pdf
// To file
try await pdf.render(html: "<h1>Invoice #1234</h1>", to: fileURL)
// To data (in-memory)
let pdfData = try await pdf.render(html: "<h1>Receipt</h1>")
// Batch processing
let html = invoices.map { "<html><body>\($0.html)</body></html>" }
for try await result in try await pdf.render(html: html, to: directory) {
print("Generated \(result.url)")
}
That's it. No setup. No configuration.
For compile-time safety, enable the HTML trait to use swift-html:
Package.swift:
dependencies: [
.package(
url: "https://github.com/coenttb/swift-html-to-pdf.git",
from: "1.0.0",
traits: ["HTML"] // ← Enable HTML trait
)
]
Usage:
import HtmlToPdf
struct Invoice: HTMLDocument {
let number: Int
let total: Decimal
var head: some HTML {
title { "Invoice #\(number)" }
}
var body: some HTML {
h1 { "Invoice #\(number)" }
p { "Total: $\(total)" }
}
}
@Dependency(\.pdf) var pdf
try await pdf.render(html: Invoice(number: 1234, total: 99.99), to: fileURL)
Invalid HTML? Won't compile. Type safety all the way down.
Process PDFs as they're generated. Don't wait for the batch to finish.
for try await result in try await pdf.render(html: html, to: directory) {
// This PDF is ready NOW
try await uploadToS3(result.url) // Upload immediately
try await db.markComplete(result.index) // Update database
}
Benefits: Lower latency, constant memory, real-time progress.
- Pre-warmed WKWebView instances (instant availability)
- Automatic lifecycle management
- FIFO fairness under load
- Optimal concurrency: 1x CPU count (8 WebViews on 8-core Mac)
- Powered by swift-resource-pool
- Full type safety in concurrent code
- Sendable guarantees throughout
- Actor-isolated state management
- No data races possible
Add to your Package.swift
:
dependencies: [
.package(url: "https://github.com/coenttb/swift-html-to-pdf.git", from: "1.0.0")
]
Add to your target:
.target(
name: "YourTarget",
dependencies: [
.product(name: "HtmlToPdf", package: "swift-html-to-pdf")
]
)
Optional: Enable type-safe HTML DSL
To use the swift-html integration, enable the HTML trait:
dependencies: [
.package(
url: "https://github.com/coenttb/swift-html-to-pdf.git",
from: "1.0.0",
traits: ["HTML"] // ← Enable HTML trait
)
]
- Swift 6.0+
- macOS 14.0+ or iOS 17.0+
- Xcode 16.0+
Need to customize? Full configuration available:
try await withDependencies {
$0.pdf.render.configuration.paperSize = .letter
$0.pdf.render.configuration.margins = .wide
$0.pdf.render.configuration.paginationMode = .paginated
$0.pdf.render.configuration.concurrency = .automatic
} operation: {
try await pdf.render(html: html, to: fileURL)
}
Common configurations:
- Paper sizes:
.a4
,.letter
,.legal
,.a3
,.a5
, or customCGSize
- Margins:
.none
,.minimal
,.standard
,.comfortable
,.wide
, or customEdgeInsets
- Pagination:
.continuous
(fast),.paginated
(print-ready),.automatic
- Concurrency:
.automatic
(1x CPU),.fixed(n)
, or specific count
See Configuration Guide for all options.
Export metrics to Prometheus, StatsD, or other monitoring systems via swift-metrics:
import Metrics
import Prometheus
// Bootstrap once at startup
MetricsSystem.bootstrap(PrometheusMetricsFactory())
// Use library normally - metrics automatically collected
@Dependency(\.pdf) var pdf
try await pdf.render(html: invoices, to: directory)
Available Metrics:
htmltopdf_pdfs_generated_total
- Counterhtmltopdf_pdfs_failed_total
- Counter (withreason
dimension)htmltopdf_render_duration_seconds
- Timer (withmode
dimension; p50/p95/p99)htmltopdf_pool_replacements_total
- Counterhtmltopdf_pool_utilization
- Gaugehtmltopdf_throughput_pdfs_per_sec
- Gauge
- Getting Started Guide - Installation, basic usage, first PDF
- Performance Guide - Optimization, benchmarks, tuning
- Configuration Guide - All configuration options
- API Documentation - Full DocC documentation
Generate docs locally:
swift package generate-documentation --open
# All tests
swift test
# Performance benchmarks
swift test --filter PerformanceBenchmarks
# Memory analysis
swift test --filter WebViewMemoryTests
# Stress tests (10K-1M PDFs)
swift test --filter StressTests
Platform | Status | Notes |
---|---|---|
macOS | ✅ Full support | Optimal performance, 8 concurrent workers (8-core) |
iOS | ✅ Full support | 8 concurrent workers, mobile-optimized |
Linux | 🚧 Coming soon | Architecture ready, needs WebKit renderer |
Windows | 🚧 Possible | Pending WebKit integration |
Contributions welcome! Please:
- Add tests - We have 95%+ coverage
- Follow conventions - Swift 6, strict concurrency, no force-unwraps
- Update docs - DocC comments + README updates
Areas for contribution:
- Linux support (implement WebKit renderer)
- Performance improvements
- Documentation and examples
- Bug reports with reproduction steps
Part of the coenttb Swift ecosystem, and optionally integrates with swift-html - Type-safe HTML & CSS DSL.
Built on Point-Free's swift-dependencies, and integrates with swift-metrics.
Apache 2.0 - See LICENSE for details.
- Point-Free for swift-dependencies and HTML DSL foundations
- Apple for WKWebView and Swift 6
- The Swift Community for feedback and contributions
Questions?
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: [email protected]
Made with ❤️ by Coen ten Thije Boonkkamp
⚡ Fast • 💾 Efficient • 🎯 Type-Safe • 🧪 Production-Ready