Generating PDF Reports in Swift for iOS and macOS
Creating PDF documents is a common task in mobile and desktop applications. On Apple platforms (iOS, macOS), powerful built-in tools are available for this: PDFKit and Core Graphics. In this article, we will walk through how to generate a multi-page report with tables, text, and graphics using only native Swift.
Why Use Built-in Tools?
- No third-party dependencies required — everything is already included in the SDK.
- Full control over rendering — work with coordinates, fonts, and colors.
- Cross-platform — the code works on both iOS and macOS.
- High performance — Core Graphics uses hardware acceleration.
Main Approaches
There are two main ways to generate PDFs in Swift:
- PDFKit — a high-level API for working with PDFs (reading, editing, creating).
- Core Graphics — low-level rendering via a PDF context.
We will look at the second approach, as it provides maximum flexibility when creating reports "from scratch."
Creating a PDF Context
The first step is to create a PDF context into which we will draw elements. The function UIGraphicsBeginPDFContextToFile (iOS) or its macOS equivalent is used for this.
import UIKit
func generatePDF() -> URL? { let fileName = "Report.pdf" let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) guard let documentsURL = paths.first else { return nil } let pdfURL = documentsURL.appendingPathComponent(fileName) let pageSize = CGRect(x: 0, y: 0, width: 612, height: 792) // A4 UIGraphicsBeginPDFContextToFile(pdfURL.path, pageSize, nil) // We will draw pages here UIGraphicsEndPDFContext() return pdfURL}Adding Pages and Content
Each page begins with a call to UIGraphicsBeginPDFPage(). After that, standard UIKit drawing methods can be used.
func drawReport() { guard let pdfURL = generatePDF() else { return } UIGraphicsBeginPDFContextToFile(pdfURL.path, CGRect(x: 0, y: 0, width: 612, height: 792), nil) // First page UIGraphicsBeginPDFPage() drawHeader(title: "Monthly Report") drawTable(data: sampleData) // Second page (if needed) UIGraphicsBeginPDFPage() drawChart() UIGraphicsEndPDFContext()}Drawing Text and Headers
To draw text, we use NSString with attributes.
func drawHeader(title: String) { let titleAttributes: [NSAttributedString.Key: Any] = [ .font: UIFont.boldSystemFont(ofSize: 24), .foregroundColor: UIColor.darkGray ] title.draw(at: CGPoint(x: 50, y: 40), withAttributes: titleAttributes)}Creating a Simple Table
A table can be drawn using lines and text. Below is an example of generating a table with two columns.
func drawTable(data: [(String, String)]) { let startX: CGFloat = 50 var startY: CGFloat = 100 let colWidth: CGFloat = 250 let rowHeight: CGFloat = 30 // Draw headers let headerAttributes: [NSAttributedString.Key: Any] = [ .font: UIFont.boldSystemFont(ofSize: 14), .foregroundColor: UIColor.black ] "Name".draw(at: CGPoint(x: startX, y: startY), withAttributes: headerAttributes) "Value".draw(at: CGPoint(x: startX + colWidth, y: startY), withAttributes: headerAttributes) startY += rowHeight // Draw rows for (key, value) in data { let rowAtt