Skip to content

0.6.0 — Zoom, Pan, and Swift 6

Latest

Choose a tag to compare

@GRimAce11 GRimAce11 released this 17 Sep 05:37
· 1 commit to main since this release

Phase 3 in full: the viewport gets gestures, the package moves to the Swift 6 language mode, and the adoption surface — documentation, decoder tests, visionOS — is filled in.

Read this before upgrading

Three changes are not drop-in:

  • Swift 6.0 / Xcode 16 required. Package.swift now uses swift-tools-version: 6.0, which Xcode 15 cannot parse. The language mode is still selectable (swiftLanguageModes: [.v6, .v5]); the toolchain floor is not.
  • Waveforms look different. Resampling defaults to peak pooling instead of mean. Bars gain contrast and keep their transients rather than eroding toward a uniform block as the bar count drops. Pass resampleMode: .mean to restore the previous appearance.
  • Tap-to-seek resolves on touch-up, not touch-down, so the second tap of a double-tap can reset zoom instead of seeking. Scrubbing is unchanged — past the 4 pt threshold, seeks still fire continuously.

Zoom and pan

WaveformViewport shipped in 0.5.0 with complete coordinate math and no gestures. It is now driven by touch.

  • New WaveformZoomOptions: dragBehavior (.seek / .panWhenZoomed), maxZoomFactor, minVisibleDuration, resetsOnDoubleTap, yieldsToVerticalScroll, plus .disabled / .editor / .inScrollView presets.
  • Pinch zooms anchored at the gesture centroid, drag pans, double-tap resets.
  • .inScrollView resolves the real conflict of a waveform row inside a list: vertical drags scroll, horizontal drags seek, taps still seek.
  • Gestures are inert without a viewport binding, so existing call sites are unaffected.

Swift 6

The package builds under the Swift 6 language mode with complete concurrency checking and zero warnings; CI fails on any new warning.

  • Nonisolated-deinit isolation fixed across all six player and recorder types.
  • Every polling Timer became a Task. Besides the concurrency fix, playhead and amplitude updates no longer stall during scroll tracking — a default-mode Timer does not fire while a scroll view is tracking.
  • Audio-session notifications extract their Sendable values in the notification closure rather than sending userInfo across to the main actor.

Caches, decoding, docs

  • WaveformCache gains an LRU byte budget (Configuration, evictIfNeeded(), currentByteSize). It previously grew until clear().
  • ResampleCache is now capacity-bounded — its key includes the visible slice, so a live pinch minted a permanent entry every frame.
  • AudioSource is a real code path via WaveformLoader.load(source:) instead of an unreferenced public enum.
  • AudioDecoder gains end-to-end tests; it previously had none despite being the path every caller takes. Fixtures are synthesized at test time, so no binary assets enter the repo.
  • DocC catalog and .spi.yml for documentation hosted on Swift Package Index — with no swift-docc-plugin dependency, so the package stays dependency-free.
  • visionOS 1.0 added and verified in CI.

Not included

tvOS was evaluated and rejected. SwiftUI marks both DragGesture and MagnifyGesture unavailable on tvOS, so seeking, panning, marker taps, and double-tap cannot compile. A tvOS type-check reports DragGesture as the only error — everything else in the package is tvOS-clean — but the interaction path is not optional. Real support needs a focus-engine interaction model and is tracked for Phase 5.


129 tests passing. Full detail in CHANGELOG.md.