Display, navigate, search, annotate, and manipulate PDF documents with PDFView, PDFDocument, PDFPage, PDFAnnotation, and PDFSelection.
Contents
- Workflow
- Route by Task
- Core Decisions
- Common Mistakes
- Review Checklist
- References
Workflow
- Load the document safely, handle password/invalid data, and define who owns mutations and saves.
- Configure
PDFViewdisplay, scaling, navigation, and observation for the actual product surface. - Perform search, selection, annotation, form, thumbnail, or page operations with PDF coordinate conversion in mind.
- Keep SwiftUI representable identity stable and strongly own weak delegates/providers.
- Save to a deliberate destination and verify reload, rotation, large files, protected files, and annotation persistence.
Route by Task
- Read core implementation details for loading, viewing, navigation, search, annotations, thumbnails, SwiftUI integration, and page overlays.
- Read PDF generation and forms for forms, watermarks, merging, and printing recipes.
- Read PDF viewing and annotations for outlines, custom drawing, and overlay lifecycle recipes.
Core Decisions
- Never assume
PDFDocumentinitialization succeeds or that a protected document is unlocked. - Convert between view and page coordinates before placing or hit-testing annotations.
- Keep PDF mutations serialized and update UI-facing state on the main actor.
- Avoid representable update loops caused by comparing document objects or rebuilding the view.
Common Mistakes
DON'T: Force-unwrap PDFDocument init
PDFDocument(url:) and PDFDocument(data:) are failable initializers.
// WRONG
let document = PDFDocument(url: url)!
// CORRECT
guard let document = PDFDocument(url: url) else { return }
DON'T: Forget autoScales on PDFView
Without autoScales, the PDF renders at its native resolution.
// WRONG
pdfView.document = document
// CORRECT
pdfView.autoScales = true
pdfView.document = document
DON'T: Ignore PDF coordinate system in annotations
PDF page coordinates have origin at the bottom-left with Y increasing upward -- opposite of UIKit.
// WRONG: UIKit coordinates
let bounds = CGRect(x: 50, y: 50, width: 200, height: 30)
// CORRECT: PDF coordinates (origin bottom-left)
let pageBounds = page.bounds(for: .mediaBox)
let pdfY = pageBounds.height - 50 - 30
let bounds = CGRect(x: 50, y: pdfY, width: 200, height: 30)
DON'T: Modify annotations on a background thread
PDFKit classes are not thread-safe. All mutations must occur on @MainActor.
// WRONG: Modifying PDFKit objects from background tasks
Task.detached { page.addAnnotation(annotation) }
// CORRECT: Perform mutations on @MainActor
@MainActor
func addNote(to page: PDFPage, annotation: PDFAnnotation) {
page.addAnnotation(annotation)
}
DON'T: Compare PDFDocument with == in UIViewRepresentable
PDFDocument is a reference type. Use identity (!==).
// WRONG: Always replaces document
func updateUIView(_ pdfView: PDFView, context: Context) {
pdfView.document = document
}
// CORRECT
func updateUIView(_ pdfView: PDFView, context: Context) {
if pdfView.document !== document {
pdfView.document = document
}
}
Review Checklist
-
PDFDocumentinit uses optional binding, not force-unwrap -
pdfView.autoScales = trueset for proper initial display - Page indices checked against
pageCountbefore access -
displayModeanddisplayDirectionconfigured to match design - Annotations use PDF coordinate space (origin bottom-left, Y up)
- All PDFKit mutations happen on the main thread
- Password-protected PDFs handled with
isLocked/unlock(withPassword:) - SwiftUI wrapper uses
!==identity check inupdateUIView -
PDFViewPageChangednotification observed for page tracking -
PDFThumbnailView.pdfViewlinked to the mainPDFView - Large-document search uses async
beginFindStringwith delegate - Saved documents use
write(to:withOptions:)when encryption needed
References
- PDF generation, forms, and printing: references/pdf-generation-and-forms.md
- PDF viewing, annotations, and overlays: references/pdf-viewing-and-annotations.md
- PDFKit framework
- PDFView
- PDFDocument
- PDFPage, PDFAnnotation, PDFSelection, PDFThumbnailView
- PDFPageOverlayViewProvider
- Adding Widgets to a PDF Document
- Adding Custom Graphics to a PDF
- Core implementation details -- setup, API wiring, and focused implementation recipes moved out of the entrypoint.