Swift-html-to-pdf 1.0

I'm excited to share swift-html-to-pdf 1.0 โ€“ a production-ready HTML to PDF library for Swift that delivers exceptional performance without compromising safety or simplicity.

What It Does

Render HTML (bytes, Data, String, or HTML protocol) to PDF files.

@Dependency(\.pdf) var pdf
try await pdf.render(html: "<h1>Invoice #1234</h1>", to: fileURL)

Performance

Benchmarked on Apple Silicon M3 (8 cores, 24GB RAM), macOS 26.0:

  • 1,939 PDFs/sec (continuous mode, single-page)

  • 677 PDFs/sec (paginated mode, multi-page)

  • 35 MB constant memory (4-24 concurrent workers)

  • Zero memory leaks - tested with 1M+ PDFs in production

Memory doesn't scale with concurrency because WebKit infrastructure is shared across workers. At 24 concurrent workers, memory stays at 35 MB instead of the expected 2,400 MB.

See the Performance Guide for detailed benchmarks and methodology.

What's New in 1.0

Complete rewrite built on Swift 6 strict concurrency, swift-resource-pool, and swift-dependencies:

  • 5x performance improvement (1,939 PDFs/sec vs ~380 in 0.x)

  • Constant memory usage (35 MB regardless of concurrency)

  • Simpler API - One dependency property, accessible everywhere, highly configurable

  • Full Sendable guarantees - Zero data races possible

Migration time: <10 minutes for new API. The 0.x API remains available but deprecated.

Key Features

  • Swift 6 strict concurrency - Sendable guarantees throughout, zero data races

  • WebView resource pooling - Pre-warmed WKWebView instances with automatic lifecycle management (powered by swift-resource-pool)

  • Streaming results - Process PDFs as they're generated, don't wait for batches

  • Optional type-safe HTML - Enable the HTML trait for compile-time safety via swift-html DSL

  • Production metrics - Built-in swift-metrics integration for monitoring

  • Comprehensive documentation

Stream Processing

Stream results as they're generated - don't wait for batches to complete:

for try await result in try await pdf.render(html: htmlArray, to: directory) {
    print("โœ… \(result.url.lastPathComponent) - \(result.pageCount) pages")
    try await uploadToS3(result.url) // Process immediately
}

Configuration

Configure per-operation if needed:

try await withDependencies {
    $0.pdf.render.configuration.paperSize = .a4
    $0.pdf.render.configuration.margins = .wide
} operation: {
    try await pdf.render(html: invoices, to: directory)
}

Type-Safe HTML (Optional)

Enable the HTML trait to use swift-html for compile-time guarantees:

struct Invoice: HTMLDocument {
    let number: Int

    var body: some HTML {
        h1 { "Invoice #\(number)" }
    }
}

try await pdf.render(html: Invoice(number: 1234), to: fileURL)

Invalid HTML won't compile with swift-html!

Comprehensive Testing

102 test functions across 23 test suites covering:

  • Functional: Basic operations, error handling, cancellation, convenience APIs

  • Performance: Benchmarks, stress tests (up to 1M PDFs), concurrency analysis

  • Quality: Memory stability, resource pooling, pagination modes, platform-specific behavior

  • Integration: Metrics collection, async streaming, naming strategies

Stress-tested with 1M PDFs (~17 minutes, constant memory profile).

Production Context

Powers invoice and receipt generation at coenttb.com and tenthijeboonkkamp.nl. Stress-tested with 1M PDFs (~17 minutes, constant memory profile). Zero OOM errors in production.

Installation

Swift Package Manager:


dependencies: [
    .package(url: "https://github.com/coenttb/swift-html-to-pdf.git", from: "1.0.0")
]

Optional: Enable type-safe HTML DSL

.package(
    url: "https://github.com/coenttb/swift-html-to-pdf.git", from: "1.0.0",
    traits: ["HTML"] // โ† Enable swift-html integration
)

Requirements:

  • Swift 6.0+
  • macOS 14.0+ or iOS 17.0+
  • Xcode 16.0+

Links

Roadmap

This release lays the groundwork for supporting platforms beyond iOS and macOS, and I look forward to exploring Linux support in particular.

Feedback, questions, and contributions welcome!

8 Likes