PDF Generation in Swift (iOS/macOS): From Simple to Advanced
Creating PDF documents is one of the common tasks in mobile and desktop development. On Apple platforms (iOS/macOS), built-in tools allow you to generate high-quality PDFs without third-party libraries. In this article, we will cover three main approaches: UIGraphicsPDFRenderer (modern and simple), PDFKit (for working with existing PDFs) and Core Graphics (for full control). All examples are written in Swift and are suitable for both iOS and macOS (with minimal changes).
1. UIGraphicsPDFRenderer — The Modern Standard
UIGraphicsPDFRenderer appeared in iOS 10 and macOS 10.12. It provides a high-level API for creating multi-page PDFs. Main advantages: automatic context management, metadata support, and ease of use.
Basic Example: Creating a PDF with Text
import UIKit
func generateSimplePDF() -> Data? { let pageWidth: CGFloat = 612 // A4 let pageHeight: CGFloat = 792 let renderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)) let data = renderer.pdfData { context in // First page context.beginPage() let text = "Hello, PDF World!" let attributes: [NSAttributedString.Key: Any] = [ .font: UIFont.systemFont(ofSize: 24), .foregroundColor: UIColor.black ] text.draw(at: CGPoint(x: 50, y: 50), withAttributes: attributes) // Second page context.beginPage() let secondText = "This is the second page" secondText.draw(at: CGPoint(x: 50, y: 50), withAttributes: attributes) } return data}
// Saving to fileif let pdfData = generateSimplePDF() { let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let pdfURL = documentsPath.appendingPathComponent("sample.pdf") try? pdfData.write(to: pdfURL) print("PDF saved: \\(pdfURL)")}Advanced Example: Table and Image
func generateInvoicePDF() -> Data { let renderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: 612, height: 792)) return renderer.pdfData { context in context.beginPage() // Draw logo (if available) if let logo = UIImage(named: "logo") { logo.draw(in: CGRect(x: 50, y: 50, width: 100, height: 50)) } // Title let titleAttributes: [NSAttributedString.Key: Any] = [ .font: UIFont.boldSystemFont(ofSize: 28), .foregroundColor: UIColor.darkGray ] "INVOICE #123".draw(at: CGPoint(x: 400, y: 50), withAttributes: titleAttributes) // Table let items = [("Item", "Price"), ("Laptop", "$1200"), ("Mouse", "$25")] var yPosition: CGFloat = 200 for (name, price) in items { let nameAttributes: [NSAttributedString.Key: Any] = [ .font: UIFont.systemFont(ofSize: 14), .foregroundColor: UIColor.black ] name.draw(at: CGPoint(x: 100, y: yPosition), withAttributes: nameAttributes) price.draw(at: CGPoint(x: 400, y: yPosition), withAttributes: nameAttributes) yPosition += 30 } // Total let totalAttributes: [NSAttributedString.Key: Any] = [ .font: UIFont.boldSystemFont(ofSize: 18), .foregroundColor: UIColor.red ] "Total: $1225".draw(at: CGPoint(x: 350, y: yPosition + 20), withAttributes: totalAttributes) }}2. PDFKit — Working with Existing PDFs and Annotations
PDFKit is a more powerful framework that
Also in library
Working with Excel in C# (.NET): EPPlus, ClosedXML, and NPOI Libraries
Task Scheduler in Node.js: node-schedule and node-cron
Machine Learning in C# (.NET): Overview of Libraries and Tools
Elasticsearch client for Go (Golang): complete guide to working with ES
Effect-ts for TypeScript: Complete Guide with Examples