# What Is the Palmier Pro Project Registry and How Are Projects Persisted?

> Discover the Palmier Pro project registry, a singleton that tracks video projects in an observable list and persists them as atomic JSON to app storage. Learn how projects are managed.

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

---

**The Palmier Pro project registry is a main-actor singleton that tracks every video project in an observable `ProjectEntry` list and persists it as atomic JSON to an app-wide storage directory.**

Palmier Pro maintains a central project registry that survives app launches and remains observable for SwiftUI views. The registry records metadata for each video project—its stable UUID, file URL, creation date, and last opened date—and flushes that data to disk as JSON. Understanding how this system works is essential for anyone extending the app or debugging project-list behavior.

## ProjectEntry Data Model

At the heart of the registry is the `ProjectEntry` struct, defined in [`Sources/PalmierPro/Project/ProjectRegistry.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Project/ProjectRegistry.swift).

```swift
struct ProjectEntry: Codable, Identifiable, Sendable {
    let id: UUID
    var url: URL
    var createdDate: Date
    var lastOpenedDate: Date
}

```

Each entry captures a stable identity for SwiftUI, the absolute bundle location, and temporal metadata for sorting.

## Registry Singleton Architecture

The `ProjectRegistry` class is implemented as a singleton via `ProjectRegistry.shared` and is isolated to the main actor. This design lets SwiftUI views read `entries` or `sortedEntries` directly without threading violations, while the singleton itself manages an internal array and delegates disk access to a dedicated actor. Unit tests in [`Tests/PalmierProTests/Media/ProjectRegistryTests.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Tests/PalmierProTests/Media/ProjectRegistryTests.swift) verify registration, removal, URL updating, and full persistence round-trips.

## Where Palmier Pro Persists Registry Data

Persistence lives in a single JSON file inside the application’s storage folder. During initialization in [`Sources/PalmierPro/Project/ProjectRegistry.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Project/ProjectRegistry.swift), the registry constructs the destination path:

```swift
fileURL = Project.storageDirectory.appendingPathComponent(Project.registryFilename)

```

All subsequent load and save operations target this location, keeping the index lightweight and portable.

## Loading the Registry Asynchronously

Registry hydration happens in a background `Task` so startup is not blocked by file I/O. The singleton asks a `ProjectRegistryDisk` actor to read the file via the static helper `loadEntries(from:)`. If the file is missing or decoding fails, the helper returns an empty array rather than throwing.

```swift
let loaded = await self.disk.load(from: self.fileURL)
self.finishLoading(loaded)

```

Once `finishLoading` receives the array, the registry transitions out of its loading state and drains any queued mutations.

## Atomic Save Operations

After every mutation, the registry immediately calls the static `saveEntries(_:to:)` helper. This routine JSON-encodes the current `entries` array and writes it atomically to the same `fileURL`.

```swift
Self.saveEntries(entries, to: fileURL)

```

Because the write is atomic, a crash mid-save cannot leave the registry in a corrupt partial state.

## Serializing Mutations While Loading

All public mutators—`register`, `remove`, `delete`, and `updateURL`—pass through a private `mutate(_:)` method in [`Sources/PalmierPro/Project/ProjectRegistry.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Project/ProjectRegistry.swift). If the registry is still loading, the mutation closure is queued in `pendingMutations` instead of running immediately.

```swift
guard !isLoading else {
    pendingMutations.append(apply)
    return
}
apply(&entries)
save()

```

After loading finishes, the registry applies each queued closure in order, then triggers a single save. This eliminates race conditions between background file reading and user-driven changes.

## Deleting Projects Safely via Trash

The `delete(_:)` method performs a two-phase teardown. It first asks `ProjectRegistryDisk.trashIfPresent(_:)` to move the project bundle to the Finder trash. Only if that operation succeeds does the registry call `remove(url)` to strip the entry and persist the updated list.

```swift
guard let self, await self.disk.trashIfPresent(url) else { return }
self.remove(url)

```

This guarantees that the registry never tracks a project whose bundle is already gone, while still allowing macOS trash recovery.

## How the Rest of the App Consumes the Registry

Several key modules interact with the registry throughout the Palmier Pro codebase.

- **Opening or creating** – `AppState` calls `ProjectRegistry.shared.register(url)` whenever a project is opened or newly created. *Source: [`Sources/PalmierPro/App/AppState.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/App/AppState.swift)*
- **Home screen list** – `HomeView` reads `ProjectRegistry.shared.sortedEntries` to display the most-recently-used projects. *Source: [`Sources/PalmierPro/Project/HomeView.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Project/HomeView.swift)*
- **Editor UI** – `EditorViewModel` exposes `ProjectRegistry.shared.entries` so the editor interface reacts to additions and deletions. *Source: [`Sources/PalmierPro/Editor/ViewModel/EditorViewModel.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Editor/ViewModel/EditorViewModel.swift)*

## Practical Registry Code Examples

### Register a new project

After a user creates a project bundle, call `register(url)` to add it to the index and trigger an immediate save.

```swift
import PalmierPro

let projectURL = URL(fileURLWithPath: "/Users/me/Movies/MyFirstProject.palmier")
ProjectRegistry.shared.register(projectURL)

```

### List projects by recency

Use `sortedEntries` to retrieve the array ordered by last-opened date.

```swift
let recentProjects = ProjectRegistry.shared.sortedEntries
for entry in recentProjects {
    print("\(entry.url.lastPathComponent) – last opened: \(entry.lastOpenedDate)")
}

```

### Update a URL after renaming or moving

If a project bundle is relocated, preserve the entry’s history by updating its URL.

```swift
let oldURL = URL(fileURLWithPath: "/Users/me/Movies/OldName.palmier")
let newURL = URL(fileURLWithPath: "/Users/me/Movies/NewName.palmier")
ProjectRegistry.shared.updateURL(from: oldURL, to: newURL)

```

### Delete a project and trash its bundle

This moves the bundle to the system trash and removes the registry entry.

```swift
let targetURL = URL(fileURLWithPath: "/Users/me/Movies/Obsolete.palmier")
ProjectRegistry.shared.delete(targetURL)

```

## Summary

- The **Palmier Pro project registry** is a main-actor singleton (`ProjectRegistry.shared`) that maintains an in-memory array of `ProjectEntry` structs.
- Each entry stores a stable `UUID`, absolute file `URL`, `createdDate`, and `lastOpenedDate`.
- **Persistence** is handled as JSON at `Project.storageDirectory/Project.registryFilename` via atomic writes through `saveEntries(_:to:)`.
- A dedicated `ProjectRegistryDisk` actor isolates file-system I/O, and a private `mutate(_:)` queue ensures race-safe updates while the registry is loading.
- Deletion moves the project bundle to the Finder trash before removing the registry entry, preventing orphaned files.

## Frequently Asked Questions

### What is the Palmier Pro project registry?

The Palmier Pro project registry is a central, observable index that tracks every video project a user creates, opens, or deletes. It is implemented as a main-actor singleton, `ProjectRegistry.shared`, and stores an array of `ProjectEntry` structs that each record a stable UUID, absolute file URL, creation date, and last opened date. This design lets SwiftUI views read the project list directly while guaranteeing that metadata survives between app launches.

### How does Palmier Pro persist the project registry?

The registry persists data as a JSON file located at `Project.storageDirectory/Project.registryFilename`. On launch, a background `Task` loads this file through the `ProjectRegistryDisk` actor, and every mutating operation triggers an atomic rewrite via the static helper `saveEntries(_:to:)`. Because the entire array is encoded and replaced on each change, the on-disk state always remains consistent.

### How does the registry handle mutations while it is loading?

If a mutation such as `register(url)` arrives before loading completes, the private `mutate(_:)` method queues the closure in `pendingMutations` instead of applying it immediately. Once `finishLoading` receives the decoded array from disk, the registry drains the queue in order, applies each closure to the loaded entries, and then saves once. This serialization prevents race conditions between asynchronous file I/O and user-initiated changes.

### How does project deletion work in the Palmier Pro registry?

The `delete(_:)` method first instructs `ProjectRegistryDisk.trashIfPresent(_:)` to move the underlying project bundle to the Finder trash. Only after that filesystem operation succeeds does the registry invoke `remove(url)` to strip the entry and persist the updated list. This two-phase approach ensures the index never references a bundle that no longer exists, while still allowing recovery through the macOS trash.