Pdfkit
Display and manipulate PDF documents using PDFKit. Use when embedding PDFView to show PDF files, creating or modifying PDFDocument instances, adding annotations (highlights, notes, signature widgets), extracting text with PDFSelection, navigating pages, generating thumbnails, filling PDF forms, or wrapping PDFView in SwiftUI.
- Skill ID
- dpearson2699/swift-ios-skills/pdfkit
- Publisher
- dpearson2699
- Repository
- swift-ios-skills
- Installs
- 0
- Files
- 3
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
dpearson2699/swift-ios-skills/pdfkitInstalls these files- SKILL.md
- evals/evals.json
- references/pdfkit-patterns.md
What this skill tells the agent
PDFKit
Display, navigate, search, annotate, and manipulate PDF documents with PDFView, PDFDocument, PDFPage, PDFAnnotation, and PDFSelection.
Contents
- Setup
- Displaying PDFs
- Loading Documents
- Page Navigation
- Text Search and Selection
- Annotations
- Thumbnails
- SwiftUI Integration
- Common Mistakes
- Review Checklist
- References
Setup
PDFKit requires no entitlements or Info.plist entries.
import PDFKit| API | Availability |
|---|---|
| PDFKit framework | iOS/iPadOS/tvOS 11+, Mac Catalyst 13.1+, macOS 10.4+, visionOS 1.0+ |
| Find interaction and page overlays | iOS/iPadOS 16+ |
Displaying PDFs
PDFView renders PDF content and handles zoom, scrolling, text selection, and page navigation.
import PDFKit
import UIKit
class PDFViewController: UIViewController {
let pdfView = PDFView()
override func viewDidLoad() {
super.viewDidLoad()
pdfView.frame = view.bounds
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(pdfView)
pdfView.autoScales = true
pdfView.displayMode = .singlePageContinuous
pdfView.displayDirection = .vertical
if let url = Bundle.main.url(forResource: "sample", withExtension: "pdf") {
pdfView.document = PDFDocument(url: url)
}
}
}Display Modes
| Mode | Behavior |
|---|---|
.singlePage | One page at a time |
.singlePageContinuous | Pages stacked vertically, scrollable |
.twoUp | Two pages side by side |
.twoUpContinuous | Two-up with continuous scrolling |
Scaling and Appearance
pdfView.autoScales = true
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.maxScaleFactor = 4.0
pdfView.displaysPageBreaks = true
pdfView.pageShadowsEnabled = true
pdfView.interpolationQuality = .highLoading Documents
PDFDocument loads from a URL, Data, or can be created empty.
let fileDoc = PDFDocument(url: fileURL)
let dataDoc = PDFDocument(data: pdfData)
let emptyDoc = PDFDocument()Password-Protected PDFs
guard let document = PDFDocument(url: url) else { return }
if document.isLocked {
if !document.unlock(withPassword: userPassword) {
// Show password prompt
}
}Saving and Page Manipulation
document.write(to: outputURL)
document.write(to: outputURL, withOptions: [
.ownerPasswordOption: "ownerPass", .userPasswordOption: "userPass"
])
let data = document.dataRepresentation()
// Pages are zero-based. Validate indices; out-of-range calls raise exceptions.
let count = document.pageCount
document.insert(PDFPage(), at: count)
if document.pageCount > 2 {
document.removePage(at: 2)
}
if document.pageCount > 3 {
document.exchangePage(at: 0, withPageAt: 3)
}Page Navigation
PDFView provides built-in navigation with history tracking.
// Go to a specific page
let pageIndex = 5
if let document = pdfView.document,
pageIndex >= 0,
pageIndex < document.pageCount,
let page = document.page(at: pageIndex) {
pdfView.go(to: page)
}
// Sequential navigation
pdfView.goToNextPage(nil)
pdfView.goToPreviousPage(nil)
pdfView.goToFirstPage(nil)
pdfView.goToLastPage(nil)
// Check navigation state
if pdfView.canGoToNextPage { /* ... */ }
// History navigation
if pdfView.canGoBack { pdfView.goBack(nil) }
// Go to a specific point on the current page
if let page = pdfView.currentPage {
let destination = PDFDestination(page: page, at: CGPoint(x: 0, y: 500))
pdfView.go(to: destination)
}Observing Page Changes
NotificationCenter.default.addObserver(
self, selector: #selector(pageChanged),
name: .PDFViewPageChanged, object: pdfView
)
@objc func pageChanged(_ notification: Notification) {
guard let page = pdfView.currentPage,
let doc = pdfView.document else { return }
let index = doc.index(for: page)
pageLabel.text = "Page \(index + 1) of \(doc.pageCount)"
}Text Search and Selection
Synchronous Search
let results: [PDFSelection] = document.findString(
"search term", withOptions: [.caseInsensitive]
)Asynchronous Search
Use PDFDocumentDelegate for background searches on large documents. Implement didMatchString(_:) to receive each match and documentDidEndDocumentFind(_:) for completion.
Incremental Search and Find Interaction
// Find next match from current selection
let next = document.findString("term", fromSelection: current, withOptions: [.caseInsensitive])
// System find bar; apply the Setup availability gate
pdfView.isFindInteractionEnabled = trueText Extraction
let fullText = document.string // Entire document
let firstPage = document.pageCount > 0 ? document.page(at: 0) : nil
let pageText = firstPage?.string // Single page
let attributed = firstPage?.attributedString // With formatting
// Region-based extraction
if let page = firstPage {
let selection = page.selection(for: CGRect(x: 50, y: 50, width: 400, height: 200))
let text = selection?.string
}Highlighting Search Results
let results = document.findString("important", withOptions: [.caseInsensitive])
for selection in results { selection.color = .yellow }
pdfView.highlightedSelections = results
if let first = results.first {
pdfView.setCurrentSelection(first, animate: true)
pdfView.go(to: first)
}Annotations
Annotations are created with PDFAnnotation(bounds:forType:withProperties:) and added to a PDFPage.
Highlight Annotation
func addHighlight(to page: PDFPage, selection: PDFSelection) {
let highlight = PDFAnnotation(
bounds: selection.bounds(for: page),
forType: .highlight, withProperties: nil
)
highlight.color = UIColor.yellow.withAlphaComponent(0.5)
page.addAnnotation(highlight)
}Text Note Annotation
let note = PDFAnnotation(
bounds: CGRect(x: 100, y: 700, width: 30, height: 30),
forType: .text, withProperties: nil
)
note.contents = "This is a sticky note."
note.color = .systemYellow
note.iconType = .comment
page.addAnnotation(note)Free Text Annotation
let freeText = PDFAnnotation(
bounds: CGRect(x: 50, y: 600, width: 300, height: 40),
forType: .freeText, withProperties: nil
)
freeText.contents = "Added commentary"
freeText.font = UIFont.systemFont(ofSize: 14)
freeText.fontColor = .darkGray
page.addAnnotation(freeText)Link Annotation
let link = PDFAnnotation(
bounds: CGRect(x: 50, y: 500, width: 200, height: 20),
forType: .link, withProperties: nil
)
link.url = URL(string: "https://example.com")
page.addAnnotation(link)
// Internal page link
link.destination = PDFDestination(page: targetPage, at: .zero)Removing Annotations
for annotation in page.annotations {
page.removeAnnotation(annotation)
}Common subtypes include .highlight, .underline, .strikeOut, .text, .freeText, .ink, .link, .line, .square, .circle, .stamp, and .widget.
Thumbnails
PDFThumbnailView
PDFThumbnailView shows a strip of page thumbnails linked to a PDFView.
let thumbnailView = PDFThumbnailView()
thumbnailView.pdfView = pdfView
thumbnailView.thumbnailSize = CGSize(width: 60, height: 80)
thumbnailView.layoutMode = .vertical
thumbnailView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(thumbnailView)Generating Thumbnails Programmatically
let thumbnail = page.thumbnail(of: CGSize(width: 120, height: 160), for: .mediaBox)
// All pages
let thumbnails = (0..<document.pageCount).compactMap {More from dpearson2699
Use this skill in a project
Open any RiverX project, search the Skills panel for dpearson2699/swift-ios-skills/pdfkit, and install it in one click.
