Native macOS PDF reader built with SwiftUI + PDFKit as a Swift Package
(no Xcode project required — builds with swift build / swift run).
- Basic viewing — open PDFs, continuous / single / two-up layouts, zoom in/out + auto-fit, page navigation, thumbnail strip.
- Search + outline — full-text search with match list + highlight, table-of-contents navigation, per-file bookmarks (persisted).
- Annotations — highlight, underline, strikethrough, sticky notes with a switchable palette (yellow / green / blue / pink / orange / purple / red / teal / gray) plus an editable custom color (persisted). Build your own rotation shortlist (star colors in the color menu) and cycle through it with the Next Color button (⇧⌘C). Highlights apply automatically once a text selection settles, and the color advances to the next rotation color after every highlight. Undo / redo for annotation changes (⌘Z / ⇧⌘Z). Annotation list inspector with jump-to + delete.
- Saving — Save (⌘S) writes annotations back into the opened PDF; Save Copy exports an annotated duplicate.
- Recents — the Open menu keeps an Open Recent list (with Clear Menu); the welcome screen shows your recent files with relative dates. Missing files are reported and dropped on open.
- Reading modes — layout menu, book mode, RTL, night background, presentation (full-screen).
- Settings — ⌘, opens Settings; toggle "Remember where I left off in each file" (on by default) to control last-position restore.
- Updates — Help → Check for Updates… compares the build's version against the latest GitHub release and offers the DMG download.
- Themes — system / light / dark / sepia-style page themes from the toolbar; preference is persisted.
- macOS 14+, Intel or Apple Silicon.
- For development: Swift 6 toolchain (Command Line Tools is enough).
- Download
PDFReader-vX.Y.Z-macOS-universal.dmgfrom Releases. - Open the DMG and drag PDFReader into Applications (or run it straight from the DMG to try it out).
- First launch: the app is ad-hoc signed, so Gatekeeper may block it.
Right-click PDFReader → Open → Open once — afterwards it
launches normally. (Signing with a Developer ID + notarization removes
this step; see
scripts/.) - To uninstall: drag PDFReader from Applications to Trash, and optionally
delete its preferences (
~/Library/Preferences/com.example.pdfreader.plist).
- Clone the repo and install dependencies:
git clone https://github.com/Marvelxy/PDFReader.git cd PDFReader swift build - Build the bundled app (required for macOS Tahoe+ cursor support):
scripts/make-app.sh debug
- Copy the app to Applications:
cp -R .build/app/PDFReader.app /Applications/
- First launch: right-click PDFReader → Open → Open once to
overcome Gatekeeper's ad-hoc signature. (Or sign with a Developer ID
- notarize for seamless launching.)
Running swift run PDFReader launches a headless binary without the
app bundle — useful for quick testing but not recommended for daily
use because cursor styles and other AppKit-dependent features won't
render correctly on macOS 15+.
cd PDFReader
swift build
swift run PDFReader
# Open a sample immediately:
PDFREADER_SAMPLE=~/Downloads/sample.pdf swift run PDFReaderFor normal use, run it as a bundled app (required on macOS Tahoe for
cursors — resize handle, I-beam, etc. — to render; a raw swift run
binary is stuck with the arrow pointer):
scripts/make-app.sh [debug|release]
open .build/app/PDFReader.appswift run PDFReaderVerify # headless checks, works with Command Line Tools
swift test # XCTest suite, requires full XcodeSee CONTRIBUTING.md for setup, branch workflow,
verification (swift build / swift run PDFReaderVerify / swift test),
and code conventions.
Sources/PDFReader/
PDFReaderApp.swift # @main SwiftUI App
Models/
PDFReaderState.swift # document state, search, outline, annotations
HighlightColor.swift # switchable markup palette + tools
BookmarkStore.swift # persisted per-file bookmarks
Views/
ContentView.swift # split view, toolbar, status bar
PDFKitView.swift # PDFView + PDFThumbnailView bridges
SidebarView.swift # pages / contents / search / bookmarks / notes
AboutView.swift # App menu → About
SettingsView.swift # App menu → Settings… (⌘,)
Services/UpdateChecker.swift # Help → Check for Updates…
Releases are cut from version tags:
git tag vX.Y.Z && git push origin vX.Y.ZCI builds the universal binary + DMG, stamps the app version from the tag
(CFBundleShortVersionString), and creates the GitHub release. Don't
create the GitHub release manually first — gh release create in CI
fails if one with the same tag name already exists.
If git push fails mid-upload of the icon resources with
send-pack: unexpected disconnect, raise the HTTP post buffer:
git config http.postBuffer 524288000- PDFKit does the heavy lifting (
PDFDocument,PDFView,PDFThumbnailView,PDFSelection,PDFAnnotation). - Highlight colors map to
NSColorso saved PDFs show the same color in Preview / other readers. - Night mode darkens the window + viewer background. True per-page dark rendering would need custom page drawing — left for v2.
Copyright (c) 2026 Marvelous Akpotu.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. See LICENSE for the full text — distributed modifications must stay under the GPL.