# How to Represent Video Clips and Tracks in Swift: The Palmier Pro Data Model

> Learn to represent video clips and tracks in Swift using Palmier Pro's type-safe data model with Clip and ClipLocation structs. Manage media segments efficiently.

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

---

**Palmier Pro implements a type-safe, immutable data model where `Clip` structs represent media segments, `ClipLocation` value types track spatial positioning, and ordered arrays of clips function as tracks within a `Project` document.**

Building a video editing application in Swift requires a robust architecture for timeline data. The palmier-io/palmier-pro repository demonstrates how to represent video clips and tracks in Swift using lightweight, `Codable` conforming structs that prioritize immutability and thread safety.

## Core Data Models for Video Editing

### The Clip Struct in Timeline.swift

The foundational entity for media representation lives in [`Sources/PalmierPro/Models/Timeline.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Timeline.swift). The `Clip` struct conforms to `Codable`, `Identifiable`, `Equatable`, and `Sendable`, making it safe for concurrent operations and serialization.

Key properties include:

- **`id`**: Unique identifier conforming to `Identifiable`
- **`type`**: Media classification via the `ClipType` enum (defined in [`Sources/PalmierPro/Models/ClipType.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/ClipType.swift))
- **`source`**: Media asset reference supporting URL or in-memory assets
- **`range`**: Temporal boundaries containing start time and duration
- **`trackIndex`**: Integer indicating the track placement
- **`isSelected`**: Boolean flag for UI state management

### ClipLocation for Spatial Positioning

The `ClipLocation` struct, defined alongside `Clip` in [`Timeline.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Timeline.swift), provides a lightweight value object that records where a clip sits on the timeline. It pairs a **track index** with a **clip index** and an optional **offset** for sub-clip positioning. This enables the UI to map data model entities to visual rows and grid positions.

### Tracks as Arrays of Clips

Rather than using a dedicated `Track` class, Palmier Pro represents tracks as ordered arrays of `Clip` objects stored within the `Project` model. This design is implemented in [`Sources/PalmierPro/Media/ProjectDocumentIO.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Media/ProjectDocumentIO.swift), where the document maintains an array of tracks, each containing its respective clips. Each track is identified by its index in this array, allowing the UI to render separate lanes while the data layer remains a simple, flat structure.

## Timeline UI and Interaction Layer

### Visual Feedback with ClipGeneratingOverlay

The `ClipGeneratingOverlay` in [`Sources/PalmierPro/Timeline/ClipGeneratingOverlay.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Timeline/ClipGeneratingOverlay.swift) provides SwiftUI visualizations for clips during creation and drag operations. This component bridges the immutable data model with real-time user interaction, rendering visual feedback without mutating underlying state until the gesture completes.

### DragState and Interaction Management

User gestures are captured via drag state structs defined in [`Sources/PalmierPro/Timeline/DragState.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Timeline/DragState.swift). These structures maintain references to the affected `Clip` and its `ClipLocation`, enabling precise manipulation of the underlying data model while maintaining UI responsiveness. The `MoveClipDrag` struct specifically tracks ongoing clip relocation operations.

## Editing Operations and the Ripple Engine

### Immutable Transformations via RippleEngine

All editing operations—trimming, moving, ripple deletes, and speed changes—are handled through immutable transformations in [`Sources/PalmierPro/Editor/RippleEngine.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Editor/RippleEngine.swift). The engine processes arrays of `Clip` objects to produce new state snapshots rather than mutating existing instances. This approach ensures thread safety and simplifies implementation of undo/redo functionality.

Unit tests in [`Tests/PalmierProTests/Timeline/ClipMathTests.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Tests/PalmierProTests/Timeline/ClipMathTests.swift) and [`ClipMutationsTests.swift`](https://github.com/palmier-io/palmier-pro/blob/main/ClipMutationsTests.swift) verify the correctness of these algorithms, ensuring that clip shifts and ripple operations maintain temporal integrity across tracks.

## Practical Implementation Examples

```swift
import PalmierPro
import Foundation

// Create a video clip from a local file URL
let videoURL = URL(fileURLWithPath: "/Users/me/Movies/intro.mov")
let videoClip = Clip(
    id: UUID(),
    type: .video,
    source: .url(videoURL),
    range: .init(start: .seconds(0), duration: .seconds(12.5)),
    trackIndex: 0,
    isSelected: false
)

// Access the project document
var project = Project.empty()

// Insert clip into the first track
project.tracks[0].append(videoClip)

// Move clip between tracks using immutable patterns
func move(clip: Clip, toTrack newTrack: Int, in project: Project) -> Project {
    var mutable = project
    
    // Remove from source track
    mutable.tracks[clip.trackIndex].removeAll { $0.id == clip.id }
    
    // Update track index and insert into destination
    var movedClip = clip
    movedClip.trackIndex = newTrack
    mutable.tracks[newTrack].append(movedClip)
    
    return mutable
}

// Calculate visual rectangle for timeline rendering
func rect(for clip: Clip, zoomScale: Double) -> CGRect {
    let startX = CGFloat(clip.range.start.seconds) * CGFloat(zoomScale)
    let width = CGFloat(clip.range.duration.seconds) * CGFloat(zoomScale)
    return CGRect(x: startX, y: 0, width: width, height: 60)
}

```

## Summary

- The **`Clip`** struct in [`Sources/PalmierPro/Models/Timeline.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Timeline.swift) provides the foundational media representation with `Codable`, `Identifiable`, and `Sendable` conformance for serialization and concurrency safety.
- **Tracks** are implemented as ordered arrays of clips within the `Project` document, eliminating complex hierarchy while maintaining editing flexibility.
- **`ClipLocation`** offers precise spatial tracking for UI positioning and drag operations without cluttering the core media model.
- **Immutable transformations** via the `RippleEngine` ensure thread-safe editing operations and simplify state management across the application.
- The architecture cleanly separates data models from UI components like `ClipGeneratingOverlay`, enabling responsive SwiftUI integration while preserving data integrity.

## Frequently Asked Questions

### What properties define the Clip struct in Palmier Pro?

The `Clip` struct defined in [`Sources/PalmierPro/Models/Timeline.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Timeline.swift) includes an `id`, `type` (via `ClipType` enum), `source` for media references, temporal `range`, `trackIndex`, and UI-specific fields like `isSelected`. It conforms to `Codable`, `Identifiable`, `Equatable`, and `Sendable` to support serialization and concurrent access patterns.

### How does Palmier Pro represent tracks without a dedicated Track struct?

Tracks are represented as ordered arrays of `Clip` objects stored within the `Project` model in [`Sources/PalmierPro/Media/ProjectDocumentIO.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Media/ProjectDocumentIO.swift). Each track index corresponds to a position in the tracks array, allowing the UI to render distinct lanes while the data layer remains a simple, flat structure that is easy to serialize and manipulate.

### How are drag-and-drop operations handled in the timeline?

Drag interactions are managed through `DragState` structs in [`Sources/PalmierPro/Timeline/DragState.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Timeline/DragState.swift) that capture the active `Clip` and its `ClipLocation`. The `ClipGeneratingOverlay` provides visual feedback during these operations, while the underlying model updates through immutable transformations in the `RippleEngine` once the gesture completes.

### Where are timeline editing algorithms like ripple delete implemented?

The `RippleEngine` in [`Sources/PalmierPro/Editor/RippleEngine.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Editor/RippleEngine.swift) contains the core algorithms for ripple edits, clip shifts, and trimming operations. These functions manipulate arrays of `Clip` objects to produce new timeline states, ensuring all mutations remain immutable and thread-safe according to the repository's architectural patterns.