v0.1.0 — the document format, and an encoder that won't lie about it
Save a kadr composition to a file, and open it again.
let data = try KadrCoding.data(for: video) // save
let video = try KadrCoding.video(from: data) // openWhy this is a package
kadr's Video cannot be Codable. It holds [any Compositor] and TimingFunction.custom — closures — and PlatformImage, which is pixels with no record of where they came from. So every app that saves a kadr project hand-writes a mirror of the DSL.
A hand-written mirror has one failure mode, and it is a bad one: you add a field upstream, forget the mirror, and nothing fails. Not the compiler, not the round-trip test — a field missing from both sides of a comparison compares equal. The project saves without complaint and reopens subtly wrong.
It will not drop something silently
Encoding refuses by default when the composition holds content a file cannot represent, and lossyContent(in:) lets you ask before committing:
let losses = KadrCoding.lossyContent(in: video)
// "A custom compositor on clip “hero” can't be saved — it's code, not data."Five things cannot be represented — clip compositors, the composition's compositor, custom timing closures, text animations, and images with no ImageStore. All five are reported. allowingLoss: true saves anyway and tells you what it left behind.
What round-trips
Video, image, title and transition clips, and nested tracks. Trims, reversal, muting, replacement audio, volume, speed and speed curves. Filters with their FilterIDs. Transforms, opacity, and their animations. Audio tracks with volume ramps and pitch algorithm. Text, image and sticker overlays. Crop, captions, preset, export quality.
The completeness guard
CompletenessTests reflects over each kadr type and asserts its stored properties are exactly the set this package handles. It is the only test here that can catch the bug the package exists to prevent, and it caught six missing Video fields — including quality, an entire kadr release — on its first run, against this package's own first draft.
Notes
Times are stored as value/timescale, not seconds, so frame boundaries survive exactly. JSON keys are sorted, so two saves of an unchanged project are byte-identical. A document from a newer schema is refused rather than read best-effort, because a best-effort read erases the fields it didn't understand on the next save.
Building this found four API gaps in kadr, fixed upstream in v0.21.0.
Requires Swift 6, kadr 0.21+. 60 tests across four suites.