Developer guideSwift 6.2+macOS 26+

How AerialDrop fits together.

A quick map of the app, its native wallpaper integration, the files that own each part, and the checks to run when you change them.

For the full manual QA matrix, see TESTING.md.

Architecture map

Start at the app entry point, then follow the injected model into focused services. Each card links to the implementation.

App lifecycle

AerialDropApp

Creates the shared AppModel, hosts the SwiftUI window, and defines app commands and the Settings scene. Open AerialDropApp.swift

Navigation and screens

ContentView and Views

ContentView hosts the Library/Import destinations. ImportPane owns import controls and preview presentation; LibraryPane drives catalogue actions. ContentView · Views directory

Application state

AppModel

The main-actor observable coordinator for selected media, import progress, catalogue state, preferences, activation, and recovery. It injects the video processor and wallpaper service so model behavior can be tested. Open AppModel.swift

Media pipeline

VideoProcessing

The protocol is consumed by AppModel; VideoProcessor validates sources, composes short loops, writes the native MOV, validates its samples, and creates the preview image. Protocol · Implementation

Catalogue ownership

ManifestStore

Reads and mutates Aerial entries, creates managed video and preview records, validates writes, and keeps recovery backups. Open ManifestStore.swift

System integration

Wallpaper service

SystemWallpaperService applies the linked selection, refreshes wallpaper processes, and verifies macOS’s result. WallpaperProcessController owns restart and fresh-agent checks. Service · Process controller

Selection ownership

WallpaperSelectionStore

Owns the private linked wallpaper selection format. It has its own compare-before-write and backup contract, separate from the manifest. Open WallpaperSelectionStore.swift

Pure import policy

Geometry and conversion

VideoGeometry handles preferred transforms and display dimensions. ConversionOptions clamps output size and resolves crop and quality choices without upscaling. Geometry · Conversion options

Follow an import

The model commits user choices before starting work, then installs media before activation.

01 · Prepare

ImportPane → AppModel

The view gathers a source and conversion choices. Geometry and output policy stay in the pure VideoGeometry and ConversionOptions types.

02 · Validate

Check the source and destination

AppModel.importSelectedVideo() checks readiness, source validity, available storage, and the Tahoe requirement before writing the installed media.

03 · Process

Encode and verify the loop

VideoProcessor normalizes the video to an 80-second native stream. Short sources are encoded once and repeated by passthrough export.

04 · Register

Write the catalogue entry

ManifestStore adds the managed video and preview to the Aerial manifest after validating the candidate and creating a backup.

05 · Activate

Apply a native selection

When requested, the wallpaper service updates the linked selection, refreshes macOS wallpaper processes, and verifies the selected asset.

06 · Recover or report

Keep ownership explicit

Before catalogue installation, failed imports remove their incomplete files. After installation, activation failures preserve the wallpaper and expose a retry path.

The landing page has the user-facing seven-step import walkthrough; this map names the code boundaries behind it.

Two private stores, two contracts

Both formats belong to macOS. Keep their mutations and recovery rules separate.

Aerial catalogue

aerials/manifest/entries.json

ManifestStore owns managed catalogue entries. It round-trips through JSONSerialization to preserve unknown keys, checks that the file has not changed before writing, compares foreign entries semantically, and backs up before each write. Backups are under aerials/AerialDropBackups.

Manifest implementation · Manifest tests

Linked wallpaper selection

Store/Index.plist

WallpaperSelectionStore owns the private selection plist. It compares the binary plist before writing, preserves unrelated selection values, verifies the written selection, and backs up under Store/AerialDropBackups.

Selection implementation · Selection tests

Paths and preferences

Inject paths in tests

WallpaperPaths(homeDirectory:) centralizes both store locations. Tests pass a temporary home directory; preference tests use isolated UserDefaults. Never aim tests at a real user's wallpaper catalogue.

WallpaperPaths · Preference tests

Contracts to keep intact

These details are coupled to macOS’s private Aerial reader and catalogue. Check the implementation and testing notes before changing them.

Native video format and loop boundaries

The output is HEVC Main10 at 30 fps, 10-bit full-range YUV, Rec.709, with temporal sub-layers and a 15 fps base layer. It uses a closed 1.9-second GOP. The validator requires a stream at least 79.5 seconds long that starts at PTS zero and has a sync sample at every repeated-loop boundary. Short loops are snapped to whole 30 fps frames before passthrough repetition. See VideoProcessor.

Preview image contents

The thumbnail contains HEIF image data and deliberately uses a .png catalogue filename suffix to match Apple’s assets. Tahoe detects the image by its contents; do not change the suffix based on the codec. See thumbnail generation.

Manifest preservation

Do not convert manifest handling to Codable. Unknown Apple keys must survive round trips. Every mutation validates foreign entries, refuses a concurrent file change, and writes a safety backup before the atomic replacement. See manifest mutation.

Runtime verification still needs Tahoe

VideoProcessor has no unit tests for real encoded streams. On a macOS Tahoe test account, import both short and longer MP4/MOV sources, exercise lock/unlock playback, and inspect WallpaperAerialsExtension logs for VideoSampleReadingErrors. Day/Night application is also gated to verified system builds; imported-pair playback and natural solar switching remain unverified. Follow the current manual testing matrix.

Where to start a change

Find the narrowest owner and its tests before changing a shared flow.

SwiftUI presentation

Views stay focused

Change the relevant type in Views; keep workflow state and side effects in AppModel.

Build and validate locally

Use macOS Tahoe 26, the macOS 26 SDK, and Swift 6.2 or newer. The project uses Swift 6 language mode and strict concurrency.

Core checks

Build and run tests

swift build
swift test
swift build -c release

Run a focused suite with swift test --filter ManifestStoreTests. If XCTest is missing under Command Line Tools, select Xcode with DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer.

Package the app

Build the .app bundle

Scripts/build-app.sh

This creates dist/AerialDrop.app and wipes .build first. Read build-app.sh before using packaging wrappers that also clear dist/.

The authoritative command list and manual scenarios live in TESTING.md. The existing CI workflow runs on macOS 26 and includes Swift build, tests, packaging, and distribution checks.

Follow the code from here.

Use the README for the product and setup path, and TESTING.md for the full manual matrix.