File Read and Write Reference

Files, folders, and persistent data touched by RawCull

File Read and Write Reference

This page lists the main places RawCull reads and writes files. Use it before changing sandbox access, cache locations, persistence, or export behavior.

File Map

File/folderAccessOwner
User-selected catalog folderReadRawCullViewModel, ScanFiles, DiscoverFiles, parser package
RAW files (.arw, .nef, .dng)Readscan, thumbnails, focus parsing, zoom, export, diagnostics
focuspoints.json beside catalogRead optionalScanFiles fallback
App Support savedfiles.json and backupsRead/write/moveCullingModel, ReadSavedFilesJSON, WriteSavedFilesJSON
App Support settings.jsonRead/writeSettingsViewModel, SettingsFileWriter
App Support analysis artifacts and burst snapshotsRead/write/deletePerFileAnalysisArtifactStore, BurstAnalysisCache
App Support AI models and licence acceptanceRead/writeAI model download/resource and licence services
Thumbnail cache directoryRead/write/deleteDiskCacheManager
Full-size JPEG preview cacheRead/write/pruneFullSizeJPGDiskCache, ZoomPreviewHandler
Subject-mask cacheRead/write/deletePhotoAIKit subject-mask stores configured by RawCullAIIntegration
Exported .jpg files in a chosen destinationWriteExtractAndSaveJPGs, SaveJPGImage
Temporary rsync include lists / process streamsWrite/delete/readExecuteCopyFiles, ArgumentsSynchronize, PrepareOutputFromRsync
Destination security-scoped bookmarkRead/write UserDefaultsOpencatalogView, copy workflow
AI selections and managed-model metadataRead/write UserDefaults and app metadataRawCullAISettingsModel, model download service

Catalog Reads

The active catalog comes from the sidebar folder selection. RawCullViewModel.startCatalogLoad(for:) starts security-scoped access and then runs the scan.

Catalog reads include:

  • directory enumeration,
  • URL resource values,
  • EXIF metadata via ImageIO,
  • MakerNote focus points via RawParserKit,
  • embedded thumbnails/JPEGs,
  • optional focuspoints.json.

DiscoverFiles uses RawFormatRegistry.allExtensions so it follows the parser registry.

App Support Files

Application Support is used for durable app-owned data:

~/Library/Application Support/RawCull/

Important files:

FilePurpose
savedfiles.jsonRatings, sharpness/saliency persistence, and manual burst winner overrides
savedfiles.backup.jsonAtomic backup of the previous valid saved-file store before replacement
savedfiles-corrupt-<timestamp>.jsonUser-approved archive of a store that failed decoding
settings.jsonThumbnail, cache, scoring, and focus-mask settings
AnalysisArtifacts/Per-file, descriptor-valid Vision/CLIP similarity artifacts
BurstAnalysis/Derived catalog snapshots containing grouping, ranking, artifacts, and review states
Models/Installed AI model bundles grouped by model identity
ModelLicenceAcceptances.jsonRecorded model-licence acceptance state
CopyLists/Operation-unique NUL-separated rsync include lists, removed during cleanup

savedfiles.json is written atomically after the old data is copied atomically to savedfiles.backup.json. A decode failure is surfaced to the UI; rating mutations are blocked until the user retries or explicitly archives the damaged store. settings.json, per-file artifacts, and burst snapshots also use atomic replacement. Burst-analysis validity is checked against file metadata, descriptors, artifact digest, and algorithm/signature versions before reuse.

Cache Files

Generated caches live under the user cache directory for the RawCull app identifier. They are performance data, not source-of-truth data.

CachePurpose
Schema-specific thumbnail disk cacheStores generated JPEG representations keyed by source fingerprint, purpose, requested size, and orientation policy
Full-size JPEG disk cacheStores larger embedded JPEG previews for zoom
Subject-mask cacheStores reusable segmentation masks outside the durable app-data namespace

Deleting these caches should only make RawCull slower until they are rebuilt. It should not lose ratings or manual decisions.

Exported JPEGs

ExtractAndSaveJPGs exports the current selection into a user-selected destination catalog. It supports two modes:

ModeInput pathOutput name
Embedded JPGFullSizePreviewLoader.loadEmbeddedPreviewOriginal basename plus .jpg
Demosaiced RAWSonyRawFormat.createFullSizeJPEGOriginal basename plus _demosaic.jpg

The actor bounds parallel extraction, tracks progress and per-file failures, and passes JPEG Data to SaveJPGImage; non-Sendable image objects do not cross the save boundary. SaveJPGImage creates files without overwriting. If a name already exists, it retries with (1), (2), and so on; the filesystem enforces exclusivity for case-insensitive and simultaneous-export collisions. RawCullViewModel.startSelectedJPGExtraction starts destination security-scoped access before constructing the actor and stops it on the main actor after the awaited result returns.

rsync Copy Workflow

The copy workflow is separate from thumbnail/scoring export. It uses rsync to copy selected RAW files based on rating/tag choices.

Main files:

FileRole
CopyFilesView.swiftUI and execution lifecycle
OpencatalogView.swiftDestination picker and bookmark creation
ExecuteCopyFiles.swiftProcess owner and progress/result state
ArgumentsSynchronize.swiftBuilds rsync arguments
PrepareOutputFromRsync.swiftParses process output
RemoteDataNumbers.swiftSummarizes copied file counts and sizes

ExecuteCopyFiles.startcopyfiles first derives the selected filenames from the current RawCullViewModel. It then creates an operation-unique file under Application Support/RawCull/CopyLists/. Each UTF-8 filename is terminated by NUL, and rsync receives --from0 plus --files-from=<path>. This preserves spaces and newlines without converting the list into command-line arguments.

The source is the currently selected catalog URL and the destination is restored only from destBookmark; there is no arbitrary path fallback. Both successful scope acquisitions remain owned by the ExecuteCopyFiles instance while /usr/bin/rsync runs. A stale destination bookmark is refreshed while its resolved grant is active. Process handlers stream progress and a typed CopyOutcome (success, failed, or cancelled) back to main-actor state.

Startup returns a typed CopyStartupFailure for unavailable arguments, missing model state, an empty selection, Application Support/include-list failures, security-scope failures, and process-launch failures. All failure paths call the same idempotent cleanup used by completion, cancellation, close(), and deinitialization. Cleanup finishes the progress stream, stops both acquired scopes exactly once, removes only this operation’s include-list file, and releases process handlers.

Security-Scoped Bookmarks

The copy workflow stores destination bookmark Data in UserDefaults after the user picks a folder. Picker access is balanced immediately after bookmark creation. At execution time, ExecuteCopyFiles starts a fresh scope for the active catalog URL and resolves destBookmark with .withSecurityScope for the operation-lifetime destination scope. Failure asks the user to reopen the catalog or reselect the destination rather than attempting a plain-path fallback.

The catalog browsing flow is different: RawCullViewModel owns one active security-scoped catalog URL and stops it during catalog transition or successful application termination. Do not transfer that ownership implicitly to a child actor.

See Security-Scoped URLs for lifecycle details.

Settings

SettingsViewModel stores its SavedSettings value as pretty-printed, sorted JSON in Application Support/RawCull/settings.json, not in UserDefaults. Settings affect:

  • thumbnail sizes,
  • cache size maximums,
  • focus/scoring options,
  • memory/cache defaults.

The main-actor model loads once through ensureLoaded(). Encoding happens on the main actor from a consistent observable snapshot, and SettingsFileWriter performs directory creation and atomic writing through an actor. Background actors use SettingsViewModel.shared.asyncgetsettings() to obtain a Sendable SavedSettings value rather than reading observable properties across isolation boundaries.

AI model selection and copy bookmarks are separate preferences and may still use UserDefaults; do not treat those as part of settings.json without an explicit migration.

Diagnostics Reads

RawCull currently has no separate RAW-diagnostics report file or persistent similarity-diagnostics log in the app target. Developer diagnostics use OSLog, package tests, and focused app integration tests. If a file-backed log is added later, keep it bounded, app-owned, and free of full user paths in ordinary presentation.

What To Check When Changing This Area

  • Writes outside the app container need active security-scoped access.
  • App-owned durable data belongs in Application Support, not Caches.
  • Rebuildable performance data belongs in Caches, not Application Support.
  • Keep savedfiles.json backup/corruption recovery semantics when changing culling persistence.
  • Keep settings.json separate from bookmarks and AI-selection preferences unless a migration is designed.
  • If a cache stores derived algorithm output, include enough version/signature metadata to reject stale data.
  • Keep process-output parsing separate from process lifecycle management.
  • Keep rsync include lists operation-unique and remove them on success, failure, cancellation, and deinitialization.
  • Balance every successful security-scope start exactly once at the layer that owns its lifetime.

Last modified September 15, 2026: sam3 (7b120c4)