# ModelDownloader in Palmier Pro: How It Manages AI Model Caching

> Discover how Palmier Pros ModelDownloader efficiently manages AI model caching. This Swift utility downloads, verifies, and installs models once for secure reuse across app launches, optimizing performance.

- Repository: [Palmier/palmier-pro](https://github.com/palmier-io/palmier-pro)
- Tags: internals
- Published: 2026-06-23

---

**TLDR:** `ModelDownloader` is a Swift utility in the `palmier-io/palmier-pro` repository that downloads, verifies, compiles, and installs machine‑learning encoders into a dedicated Application Support cache, ensuring AI models are fetched only once and reused securely across app launches.

The `ModelDownloader` class, implemented in [`Sources/PalmierPro/Search/Models/ModelDownloader.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Search/Models/ModelDownloader.swift), orchestrates the complete lifecycle of AI model assets required for Palmier Pro’s visual search features. It stores assets in `~/Library/Application Support/PalmierPro/Models` using a versioned directory structure that prevents duplicate downloads and guarantees file integrity through cryptographic hashing.

## How ModelDownloader Manages AI Model Caching

### Determining the Install Location

The downloader first resolves the permanent cache directory using Apple’s standard filesystem APIs. It calls `FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)` and appends the path component `PalmierPro/Models` to establish the root of the model cache. This ensures compliance with macOS conventions and proper sandboxing behavior.

### Checking Existing Installs (Idempotency)

Before initiating any network requests, the `installed(for:)` method checks the cache for existing assets. It looks for three required items: `ImageEncoder.mlmodelc`, `TextEncoder.mlmodelc`, and [`tokenizer/tokenizer.json`](https://github.com/palmier-io/palmier-pro/blob/main/tokenizer/tokenizer.json). If all files exist at the expected version path, the method returns an `InstalledModel` struct immediately, skipping the download process entirely. This idempotent design prevents redundant network traffic and speeds up app launches when models are already present.

### Downloading and Streaming Files

When assets are missing, the downloader creates a temporary staging area named `palmier-model-<UUID>` under the system temporary directory. The `download(_:to:progress:)` method then streams each file described in the manifest using `URLSession` with a custom `URLSessionDownloadDelegate` that reports fractional progress. This allows the caller to aggregate overall progress across the image encoder, text encoder, and tokenizer archives.

### Verifying Integrity with SHA-256

After each download completes, the `verify(_:sha256:)` method computes the SHA-256 hash of the file and compares it against the expected checksum defined in the model manifest. If the hash does not match, the installation aborts immediately, preventing corrupted or tampered models from entering the cache.

### Compiling and Moving to Permanent Cache

The `unzip(_:in:)` method extracts archives using `/usr/bin/ditto`. If the extracted entry is an `.mlpackage`, it is compiled into a `.mlmodelc` bundle using `MLModel.compileModel(at:)`. Tokenizer archives remain as plain files. Finally, the compiled assets are moved from the staging area into a versioned directory (`<model>-v<version>`) within the Application Support cache, accompanied by a [`spec.json`](https://github.com/palmier-io/palmier-pro/blob/main/spec.json) metadata file. The temporary staging directory is automatically removed after completion.

## Caching Strategy and Security

The `ModelDownloader` implements a **filesystem‑based cache** with several safeguards:

- **Versioned Directories:** Each model resides in a directory named `<model>-v<version>`, allowing simultaneous coexistence of different versions without conflicts.
- **Idempotent Install:** The `installed(for:)` check runs before every download attempt, ensuring network activity only occurs when necessary.
- **Integrity Guard:** SHA-256 verification guarantees that cached files match the exact bytes specified in the manifest, protecting against corruption or supply‑chain attacks.
- **Atomic Installation:** By staging files in a temporary location and moving them only after successful verification and compilation, the system never leaves partial or broken models in the permanent cache.

## Complete Implementation Example

```swift
import PalmierPro

// 1️⃣ Build the manifest (normally fetched from a server)
let manifest = ModelDownloader.Manifest(
    model: "siglip2",
    version: 1,
    embeddingDim: 768,
    imageSize: 224,
    contextLength: 77,
    files: .init(
        imageEncoder: .init(name: "image-encoder.zip", sha256: "a1b2...", bytes: 12_345_678),
        textEncoder:   .init(name: "text-encoder.zip",   sha256: "c3d4...", bytes: 9_876_543),
        tokenizer:     .init(name: "tokenizer.zip",     sha256: "e5f6...", bytes: 123_456)
    )
)

// 2️⃣ Base URL where the zip files are hosted
let baseURL = URL(string: "https://models.palmier.io/siglip2/")!

// 3️⃣ Create the downloader and start the install
Task {
    do {
        let downloader = ModelDownloader()
        let installed = try await downloader.install(
            manifest: manifest,
            baseURL: baseURL,
            progress: { fraction in
                print("Overall progress: \(Int(fraction * 100))%")
            })
        // 4️⃣ Use the installed model
        let imageEncoder = try MLModel(contentsOf: installed.imageEncoderURL)
        let textEncoder  = try MLModel(contentsOf: installed.textEncoderURL)
        // … feed the models into VisualEmbedder …
    } catch {
        print("Installation failed:", error)
    }
}

```

This example constructs a `Manifest`, points the downloader at a remote server, and calls `install(manifest:baseURL:progress:)`. The method automatically checks the cache, downloads missing pieces, verifies checksums, unpacks archives, compiles Core ML bundles, and returns URLs to ready‑to‑use assets.

## Key Source Files

The following files define the model caching architecture:

- **[`Sources/PalmierPro/Search/Models/ModelDownloader.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Search/Models/ModelDownloader.swift)** – Core implementation of download orchestration, verification, and cache management.
- **[`Tests/PalmierProTests/Search/ModelDownloaderTests.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Tests/PalmierProTests/Search/ModelDownloaderTests.swift)** – Unit tests validating idempotent installs, checksum failures, and directory layout correctness.
- **[`Sources/PalmierPro/Search/VisualEmbedder.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Search/VisualEmbedder.swift)** – Consumes `InstalledModel` instances to execute visual embeddings.
- **[`Sources/PalmierPro/Utilities/DiskCache.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Utilities/DiskCache.swift)** – Provides generic filesystem caching utilities supporting the model downloader.

## Summary

- **ModelDownloader** handles the complete AI model lifecycle in Palmier Pro, from network download to local compilation.
- It caches models in `~/Library/Application Support/PalmierPro/Models` using **versioned directories** to support multiple simultaneous versions.
- **Idempotent checks** via `installed(for:)` prevent redundant downloads by verifying the existence of `ImageEncoder.mlmodelc`, `TextEncoder.mlmodelc`, and tokenizer files before network activity.
- **SHA-256 verification** ensures cache integrity, while **atomic staging** guarantees that only fully verified and compiled models reach the permanent cache.
- The utility compiles `.mlpackage` files into `.mlmodelc` bundles using `MLModel.compileModel(at:)` for optimal runtime performance.

## Frequently Asked Questions

### Where does ModelDownloader store cached AI models?

Models are stored in the Application Support directory at `~/Library/Application Support/PalmierPro/Models/<model>-v<version>`. This location persists across app launches and follows macOS conventions for user-specific application data.

### How does ModelDownloader handle different versions of the same model?

The downloader embeds the version number in the directory name (e.g., `siglip2-v1`). When checking for existing installs, `installed(for:)` looks for the specific version requested in the manifest, allowing multiple versions to coexist without collision and enabling rollback scenarios.

### What happens if a downloaded file fails the integrity check?

The `verify(_:sha256:)` method computes the SHA-256 hash of the downloaded file and compares it to the manifest. If the hash mismatches, the method throws an error and aborts the installation, preventing corrupted or malicious files from entering the cache. The temporary staging files are cleaned up automatically.

### How does ModelDownloader integrate with Palmier Pro’s search features?

After successful installation, `ModelDownloader` returns an `InstalledModel` struct containing URLs to the compiled encoder bundles. This struct is consumed by `VisualEmbedder` (defined in [`Sources/PalmierPro/Search/VisualEmbedder.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Search/VisualEmbedder.swift)), which loads the models using `MLModel(contentsOf:)` to generate embeddings for visual search queries.