Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.prism.byescaleira.com/llms.txt

Use this file to discover all available pages before exploring further.

PrismVideo wraps AVFoundation’s export pipeline in a Swift concurrency-friendly API. You create a downloader with a remote URL, call download(), and receive an AsyncStream of status updates—progress fractions, a completion path, or a typed error—without any delegate wiring or callback pyramids.

Installation

import PrismVideo

Downloading a video

The following example downloads a remote video and writes it to a temporary file at highest quality.
let url = URL(string: "https://example.com/videos/intro.mp4")!

let downloader = PrismVideoDownloader(
    video: url,
    with: "intro",
    for: .mp4
)

for await status in downloader.download() {
    switch status {
    case .downloading(let progress, _):
        print("\(Int(progress * 100))% downloaded")

    case .completed(let path):
        print("Saved to \(path)")
        // Move or play the file from path

    case .error(let error):
        print("Download failed: \(error.errorDescription ?? "unknown")")
    }
}
PrismVideoDownloader is an actor, so download() is isolated to its executor. The AsyncStream delivers updates on the main run loop because Timer.publish is used for progress polling.
The exported file lands in FileManager.default.temporaryDirectory. If you need to keep it beyond the current session, move it to the Documents directory after the .completed case.

PrismVideoDownloaderStatus

Each value emitted by the stream is one of three cases:
CasePayloadMeaning
.downloading(progress:session:)Double (0.0–1.0), AVAssetExportSessionExport is in progress
.completed(path:)URLExport finished; file is at this path
.error(_:)PrismVideoErrorExport failed with a typed error
The session in .downloading gives you access to AVAssetExportSession for cancellation. It is marked @unchecked Sendable because AVAssetExportSession does not conform to Sendable—do not share it across isolation domains.

PrismVideoError

PrismVideoError conforms to PrismError, which means it includes a localized description, a failure reason, and a recovery suggestion.
CaseDescriptionRecovery
.assetNotPlayableThe AVAsset could not be prepared for playbackVerify the source URL and try reloading
.missingTracksThe file has no valid video or audio tracksConfirm the media file is not corrupted
.failedToCreateExportSessionAVAssetExportSession could not be initializedTry different export parameters
.custom(message:)Wraps an underlying system error messageInspect the message for details
case .error(let error):
    error.log()   // emits description + failure reason + recovery to os.Logger
    showAlert(error.errorDescription ?? "Download failed")

PrismVideoEntity

Use PrismVideoEntity to model video metadata throughout your app. All fields except id, url, and title are optional.
let video = PrismVideoEntity(
    url: localURL,
    title: "Intro Video",
    duration: 142.5,
    resolution: .fullHD,
    type: .mp4,
    thumb: thumbnailURL
)
PrismVideoEntity is Identifiable, Equatable, Hashable, and Sendable, so you can store it in PrismStore state or pass it across actor boundaries without any extra work.

PrismVideoResolution

PrismVideoResolution maps pixel heights to named quality tiers. Initialize it from a raw pixel height and it selects the correct case automatically.
let resolution = PrismVideoResolution(rawValue: 1920)  // .fullHD
let label = resolution?.rawValue                        // "1080p HD"
CasePixel height rangeLabel
.SD0 – 719"SD"
.HD720 – 1079"720p HD"
.fullHD1080 – 2159"1080p HD"
._4K2160+"4K"

Complete download-and-store example

The following pattern downloads a video, updates a PrismStore counter with progress, and stores the local path on completion.
func downloadVideo(url: URL, store: PrismStore<VideoState, VideoAction>) async {
    let downloader = PrismVideoDownloader(
        video: url,
        with: UUID().uuidString,
        for: .mp4
    )

    for await status in downloader.download() {
        switch status {
        case .downloading(let progress, _):
            store.send(.progressUpdated(progress))

        case .completed(let path):
            store.send(.downloadCompleted(localURL: path))

        case .error(let error):
            store.send(.downloadFailed(error))
        }
    }
}
For long downloads, persist the local URL using PrismDiskPersistence so the user can access the file after the app restarts.