A fast, native macOS image gallery and viewer built with Swift and SwiftUI. No Xcode required — built entirely with Swift Package Manager from the command line.
Note: This application was written entirely by Claude (Anthropic) and has not been reviewed by a human developer. Use at your own discretion.
- Grid view of all images in a folder with thumbnail previews
- View button in the toolbar opens a popover for layout, thumbnail style, and size:
- Grid or Masonry layout toggle
- Square or aspect-ratio thumbnail mode (also Cmd+T)
- Adjustable thumbnail size slider (100–280px, persisted per folder)
- Filter button opens a popover for favorites, file type, and date:
- Favorites — star images and filter to show only favorites
- Filter by file type (JPEG, PNG, HEIC, RAW, etc.)
- Filter by date range (modified date)
- Sort by name, date modified, or file size (ascending or descending)
- Search by filename (Cmd+S to focus search field)
- Multi-select with Cmd+click (individual) or Shift+click (range)
- Remembers your sort, filters, thumbnail size, and view settings per folder
- Auto-refreshes when files are added, removed, or renamed in the folder
- Optional recursive subfolder scanning (toggled in folder settings)
- Recent folders list — quick access to the last 10 opened folders via the toolbar
- Drag a folder onto the gallery window to open it; drag images out to Finder
- Click any thumbnail to open the full image
- Smooth zoom with scroll wheel, trackpad pinch, Cmd++ / Cmd+-, or Cmd+1 (actual pixels)
- Pan by dragging
- Cmd+0 to reset zoom to fit
- Arrow keys to navigate between images (or pan when zoomed in)
- Image position counter (e.g. 3 / 47) shown in the bottom-right corner
- Image info overlay (I) — filename, pixel dimensions, file size, date modified
- Metadata sheet (M) — full ImageIO metadata (General, TIFF, EXIF, GPS, IPTC, PNG); ComfyUI workflow images show parsed model, generation, and prompt sections; every field has a copy-to-clipboard button
- Right-click context menu with the same actions as the gallery thumbnail menu
- Play/pause slideshow button in the top-right corner (Cmd+P)
- Trash button in the top-right corner
- Returns to gallery scrolled to the current image
- Press Cmd+P to start/stop a slideshow (or use the play/pause button in the full-image toolbar)
- Crossfade transition between images on auto-advance; instant cut on manual navigation
- Adjustable interval (0.5s minimum) via controls overlay
- Shuffle mode — randomly selects the next image instead of advancing sequentially
- Ken Burns pan & zoom effect — portrait images pan top-to-bottom, landscape pan left-to-right
- Cmd+Delete moves the current image to Trash (gallery and full-image view)
- Trash button in the top-right of the full-image view
- Multi-select trash via the action bar in the gallery
- Plays the system trash sound on deletion
- In-app deletions do not trigger an unnecessary folder refresh
- Move selected images to Trash
- Move selected images to another folder
- Copy file paths to clipboard
- Open in default app
- Show in Finder
- Add/remove from Favorites
- Copy image to clipboard
- Copy file path
- Set as desktop wallpaper
- Move to Trash
- Same actions as the thumbnail context menu
- Lock any open folder with Touch ID via the gear icon in the gallery toolbar
- Authentication is required each time a locked folder is opened — including on app launch
- Falls back to your macOS login password if Touch ID is unavailable
- Lock state is stored per-folder in UserDefaults and enforced via macOS LocalAuthentication
- Disabling a lock requires Touch ID or your login password
- Locked folders show a lock icon in the toolbar gear button
- Toggle recursive subfolder scanning per-folder in the settings sheet
- Multiple independent windows (Cmd+N), each with its own folder and state
- Full-screen support (Cmd+F)
- Refresh folder (Cmd+R)
- Open folder (Cmd+O)
- Remembers the last opened folder across launches
- Per-folder settings persistence (sort, filters, thumbnail size, layout mode, recursive scan)
- Window title bar shows folder name, image count, and total file size — updates live
- Favorites persist using file bookmarks — survive renames and moves within a volume
Standard: jpg jpeg png gif heic heif tiff tif bmp webp avif
RAW: dng raw cr2 cr3 arw nef orf rw2 raf pef srw x3f 3fr mef nrw rwl iiq
RAW formats are decoded via macOS ImageIO — support depends on your macOS version and the camera model.
- macOS 14 (Sonoma) or later
- Xcode Command Line Tools or a full Xcode install (for the Swift compiler)
Install command line tools if needed:
xcode-select --installClone the repository and run the build script from the project root:
git clone <repo-url>
cd image-viewer-gallery
./build-app.shThis compiles a release build and produces ImageViewer.app in the project directory.
Always remove the old bundle before copying — overwriting in place can cause macOS to cache the old binary or code signature:
rm -rf /Applications/ImageViewer.app
cp -r ImageViewer.app /Applications/If macOS shows a security warning the first time you open the app, right-click ImageViewer.app and choose Open, then confirm. This is expected for apps distributed outside the Mac App Store.
After installation, refresh the Launch Services database so the app appears correctly in Spotlight and Launchpad:
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -r -domain local -domain system -domain user && killall FinderIf a newly built version doesn't reflect your latest changes, macOS likely cached the old bundle. Quit the app first, then do a clean replace:
./build-app.sh && killall ImageViewer 2>/dev/null; rm -rf /Applications/ImageViewer.app && cp -r ImageViewer.app /Applications/The killall step ensures the old process isn't still holding the bundle open. The rm -rf before copying is the critical part — never overwrite a running or previously-installed .app in place.
open ImageViewer.appOr run directly from the terminal (prints logs to stdout):
.build/release/ImageViewerThe app icon is pre-generated and included in the repo (AppIcon.icns). If you want to regenerate it:
swift make-icon.swift
iconutil -c icns AppIcon.iconset| Shortcut | Action |
|---|---|
| Arrow keys | Navigate thumbnails |
| Enter / Space | Open selected image |
| Cmd+Delete | Move selected image to Trash |
| Cmd+S | Focus search field |
| Cmd+T | Toggle square / aspect-ratio thumbnails |
| Cmd+O | Open folder |
| Cmd+R | Refresh folder |
| Cmd+N | New window |
| Cmd+F | Toggle full screen |
| Escape | Clear multi-selection / clear search |
| Shortcut | Action |
|---|---|
| ← → | Previous / next image (or pan when zoomed) |
| ↑ ↓ | Pan up / down |
| Scroll wheel | Zoom in / out at cursor |
| Cmd++ / Cmd+- | Zoom in / out |
| Cmd+0 | Reset zoom to fit |
| Cmd+1 | Zoom to actual pixels (1:1) |
| F | Toggle favorite |
| I | Toggle image info overlay |
| M | Open metadata sheet |
| Cmd+P | Start / stop slideshow |
| Cmd+Delete | Move current image to Trash |
| Cmd+S | Return to gallery and focus search |
| Cmd+O | Open folder |
| Escape / Enter / Space | Return to gallery |
| Cmd+R | Refresh folder |
| Shortcut | Action |
|---|---|
| Cmd+S | Focus search field |
| Escape | Clear search text (first press) / defocus (second press) |
Sources/ImageViewer/
├── AppState.swift # Central state, navigation, sort, filter, slideshow, folder watching
├── ImageViewerApp.swift # App entry point, per-window setup, menu commands, title bar
├── Views/
│ ├── RootView.swift # Top-level view switcher, folder auth gate
│ ├── FolderPickerView.swift # Initial folder selection screen, auth failure UI
│ ├── GalleryView.swift # Thumbnail grid, masonry layout, toolbar, filter popover
│ ├── ThumbnailCell.swift # Individual thumbnail with context menu (grid + masonry)
│ ├── FullImageView.swift # Full-image viewer, zoom/pan, Ken Burns, crossfade, context menu
│ ├── MetadataPanelView.swift # Full ImageIO + ComfyUI metadata sheet
│ ├── SlideshowControlsOverlay.swift
│ ├── InfoOverlayView.swift # Image metadata HUD
│ └── FolderSettingsSheet.swift # Touch ID lock and recursive scan toggle
└── Utilities/
├── ImageLoader.swift # Async image loading with LRU thumbnail cache
├── FolderScanner.swift # Async directory enumeration
├── FolderLockManager.swift # Keychain-backed Touch ID lock state
└── ComfyUIWorkflowParser.swift # Parses ComfyUI workflow JSON embedded in PNG metadata
- Built with SwiftUI + AppKit on Swift Package Manager — no Xcode project file
- Minimum deployment target: macOS 14
- Thumbnail loading is fully asynchronous with an in-memory LRU cache keyed on
(url, size)— prevents blurry thumbnails when switching between grid and masonry layouts - Directory scanning runs off the main thread to avoid UI freezes on large folders; optional recursive mode uses
FileManager.enumerator - Each window owns an independent
AppStateobject — windows don't share state - Per-folder settings are stored in
UserDefaultsas JSON, capped at 50 entries to prevent unbounded growth - Folder changes are detected via
DispatchSourcefile system events with a 0.5s debounce - In-app deletions suppress the folder watcher to avoid redundant refreshes
- Folder lock state is stored in
UserDefaults; authentication is enforced viaLAContext.evaluatePolicy(.deviceOwnerAuthentication)which gates access with Touch ID or the macOS login password - Favorites use
URL.bookmarkData()for persistence, so starred images survive renames and moves within a volume; path-based fallback handles migration from older data - File sizes and modification dates are cached during sort, eliminating redundant disk reads on re-sort
- Trackpad pinch-to-zoom uses an
NSEvent.magnifylocal monitor alongside the scroll-wheel zoom monitor
MIT