How to Represent Video Clips and Tracks in Swift: The Palmier Pro Data Model
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. 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 toIdentifiabletype: Media classification via theClipTypeenum (defined inSources/PalmierPro/Models/ClipType.swift)source: Media asset reference supporting URL or in-memory assetsrange: Temporal boundaries containing start time and durationtrackIndex: Integer indicating the track placementisSelected: Boolean flag for UI state management
ClipLocation for Spatial Positioning
The ClipLocation struct, defined alongside Clip in 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, 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 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. 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. 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 and ClipMutationsTests.swift verify the correctness of these algorithms, ensuring that clip shifts and ripple operations maintain temporal integrity across tracks.
Practical Implementation Examples
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
Clipstruct inSources/PalmierPro/Models/Timeline.swiftprovides the foundational media representation withCodable,Identifiable, andSendableconformance for serialization and concurrency safety. - Tracks are implemented as ordered arrays of clips within the
Projectdocument, eliminating complex hierarchy while maintaining editing flexibility. ClipLocationoffers precise spatial tracking for UI positioning and drag operations without cluttering the core media model.- Immutable transformations via the
RippleEngineensure 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 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. 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 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 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →