To generate a PDF in SwiftUI using PDFKit, you can follow these steps:
- Import PDFKit: Make sure you have imported PDFKit in your Swift file.
- Render PDF Content: Define and render the content you want to include in your PDF using
UIGraphicsPDFRenderer. You can add text, images, or custom views into PDF. - Generate PDF: Add rendered data into a PDF Document.
Here is a complete example:
import SwiftUI
import PDFKit
struct ContentView: View {
var body: some View {
Button("Generate PDF") {
generatePDF()
}
}
func generatePDF() {
let pageRect = CGRect(x: 0, y: 0, width: 612, height: 792) // US Letter size
let renderer = UIGraphicsPDFRenderer(bounds: pageRect)
// 1. adding a temporary pdf file
let url = FileManager.default.temporaryDirectory.appendingPathComponent("output.pdf")
// 2. attributes for text like size
let attributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12)
]
do {
try renderer.writePDF(to: url) { context in
// 3. start a pdf page
context.beginPage()
let text = "I'm a PDF!"
// 4. postion of text in the pdf page
text.draw(at: CGPoint(x: 20, y: 50), withAttributes: attributes)
}
print("PDF saved at: \(url.path)")
} catch {
print("Error generating PDF: \(error)")
}
}
}
If you run the app and click on the Generate PDF button, the PDF URL will be printed in the console like this:
/Users/name/Library/Developer/CoreSimulator/Devices/.../data/Containers/Data/Application/.../tmp/output.pdf
You can copy and paste this URL to any browser to view the PDF. You can also view this PDF inside your app. We will see that later.

You can add an image like this:
// adding image from SF Symbols
let globeIcon = UIImage(systemName: "globe")
let globeIconRect = CGRect(x: 50, y: 100, width: 100, height: 100)
globeIcon!.draw(in: globeIconRect)
If we want to add multiple items like text, images, etc, we need to set the position of each item. PDFKit will not add items one after another like regular text processor software. Calculating the position of each item manually is not a good idea. So we have to calculate the position dynamically.
Let’s say we have a Person structure like this:
struct Person: Identifiable {
let id = UUID()
let name: String
let number: String
let address: String
}
We want to create a PDF page from the list of people. We can do that like this:
for person in people {
let personContent = """
Name: \(person.name)
Number: \(person.number)
Address: \(person.address)
"""
let attributedString = NSAttributedString(string: personContent, attributes: attributes)
let textHeight = attributedString.boundingRect(with: CGSize(width: contentWidth, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).height + 30
// adding new page if we reach bottom of the current page
if currentY + textHeight > contentHeight + margin {
context.beginPage()
currentY = margin
}
attributedString.draw(in: CGRect(x: margin, y: currentY, width: contentWidth, height: textHeight))
currentY += textHeight
}
Here is the complete code:
import SwiftUI
import PDFKit
struct ContentView: View {
let people = [
Person(name: "John Doe", number: "123-456-7890", address: "123 Main St"),
Person(name: "Jane Smith", number: "987-654-3210", address: "456 Elm St"),
]
var body: some View {
Button("Generate PDF") {
generatePDF()
}
}
func generatePDF() {
// adding a temporary pdf file
let url = FileManager.default.temporaryDirectory.appendingPathComponent("output.pdf")
// US Letter size pdf
let pageWidth: CGFloat = 612
let pageHeight: CGFloat = 792
// variables for adding margin to content
let margin: CGFloat = 50
let contentWidth = pageWidth - 2 * margin
let contentHeight = pageHeight - 2 * margin
// variable for keep tracking height of content.
var currentY: CGFloat = margin
let pageRect = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
let renderer = UIGraphicsPDFRenderer(bounds: pageRect)
// attributes for text size & fonts
let attributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12)
]
do {
try renderer.writePDF(to: url) { context in
// start a pdf page
context.beginPage()
// adding dynamic text to pdf page
for person in people {
let personContent = """
Name: \(person.name)
Number: \(person.number)
Address: \(person.address)
"""
let attributedString = NSAttributedString(string: personContent, attributes: attributes)
let textHeight = attributedString.boundingRect(with: CGSize(width: contentWidth, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).height + 30
// adding new page if we reach bottom of the current page
if currentY + textHeight > contentHeight + margin {
context.beginPage()
currentY = margin
}
attributedString.draw(in: CGRect(x: margin, y: currentY, width: contentWidth, height: textHeight))
currentY += textHeight
}
}
print("PDF saved at: \(url.path)")
} catch {
print("Error generating PDF: \(error)")
}
}
}
struct Person: Identifiable {
let id = UUID()
let name: String
let number: String
let address: String
}
The output will be:

You can apply various markdown formats to the text. This code will add pages automatically if required. You can try to increase data and see the result.
You can generate PDFs from SwiftUI views also. Where you can design PDF using SwiftUI and then create PDF. Here is the complete tutorial: Generate PDF from SwiftUI view using PDFKit – iOS