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

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.

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 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, the registry constructs the destination path:

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.

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.

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. If the registry is still loading, the mutation closure is queued in pendingMutations instead of running immediately.

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.

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.

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.

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.

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.

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.

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →