fulldecent/fdwaveformview
FDWaveformView displays audio waveforms in Swift apps so users can preview audio, scrub, and pick positions with ease.
Usage
Add an FDWaveformView programmatically, then load audio. If your file is missing an extension, see the Stack Overflow answer on AVURLAsset without extensions.
let thisBundle = Bundle(for: type(of: self))
let url = thisBundle.url(forResource: "Submarine", withExtension: "aiff")
self.waveform.audioURL = url[Waveform overview showing loaded audio]
Features
Highlight playback
Highlight a portion of the waveform to show progress.
self.waveform.highlightedSamples = 0..<(self.waveform.totalSamples / 2)[Waveform with highlighted progress]
Zoom for detail
Render only the visible portion while progressively adding detail as you zoom.
self.waveform.zoomSamples = 0..<(self.waveform.totalSamples / 4)[Zoomed waveform segment]
Gesture control
Allow scrubbing, stretching, and scrolling with built-in gestures.
self.waveform.doesAllowScrubbing = true
self.waveform.doesAllowStretch = true
self.waveform.doesAllowScroll = true[Gesture-driven waveform interaction]
Animated updates
Animate property changes for smoother UI feedback.
UIView.animate(withDuration: 0.3) {
let randomNumber = arc4random() % self.waveform.totalSamples
self.waveform.highlightedSamples = 0 ..< randomNumber
}[Animated waveform highlight change]
Rendering quality
- Antialiased waveforms draw extra pixels to avoid jagged edges.
- Autolayout-driven size changes trigger re-rendering to prevent pixelation.
- Supports iOS 15+, visionOS 1+, and Swift tools version 6.4, as declared in
Package.swift. Xcode 26 cannot load that tools version. - Includes unit tests that run on GitHub Actions.
Installation
Add this package with Swift Package Manager. In Xcode that is File > Add Package Dependencies...
API
Following is the complete API for this module:
FDWaveformView(open class, subclass ofUIView)
- init() (public init) default initializer - delegate: FDWaveformViewDelegate? (open var, get/set) delegate for loading and rendering callbacks - audioURL: URL? (open var, get/set) audio file to render asynchronously - totalSamples: Int (open var, get) sample count of the loaded asset - highlightedSamples: CountableRange<Int>? (open var, get/set) range tinted with progressColor - zoomSamples: CountableRange<Int> (open var, get/set) range currently displayed - doesAllowScrubbing: Bool (open var, get/set) enable tap and pan scrubbing - doesAllowStretch: Bool (open var, get/set) enable pinch-to-zoom - doesAllowScroll: Bool (open var, get/set) enable panning across the waveform - wavesColor: UIColor (open var, get/set) tint for the base waveform image - progressColor: UIColor (open var, get/set) tint for the highlighted waveform - loadingInProgress: Bool (open var, get) indicates async load in progress
FDWaveformViewDelegate(@objc public protocol)
- waveformViewWillRender( waveformView: FDWaveformView) (optional) - waveformViewDidRender( waveformView: FDWaveformView) (optional) - waveformViewWillLoad( waveformView: FDWaveformView) (optional) - waveformViewDidLoad( waveformView: FDWaveformView) (optional) - waveformDidBeginPanning( waveformView: FDWaveformView) (optional) - waveformDidEndPanning( waveformView: FDWaveformView) (optional) - waveformDidEndScrubbing(_ waveformView: FDWaveformView) (optional)
A couple other things are exposed that we do not consider public API:
FDWaveformView(implementsUIGestureRecognizerDelegate)
- gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) -> Bool
Testing
GitHub Actions runs the suite on the Xcode 27 runner image. The default Xcode there is 27.0, and that copy provides an iPhone 17 simulator on iOS 27.0. The command is:
xcodebuild test -scheme FDWaveformView -destination 'platform=iOS Simulator,name=iPhone 17,OS=27.0'xcrun swift test builds the package for macOS. These sources import UIKit, so that command does not compile this package.
On another Xcode, list the simulators that copy of Xcode can see and pass one of those ids. xcodebuild only accepts destinations from the selected Xcode, so an id printed by a different install will fail.
xcrun simctl list devices available | grep iPhone
xcodebuild test -scheme FDWaveformView -destination 'id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'The Example app target deploys to iOS 18.6, so its simulator has to be iOS 18.6 or newer:
xcodebuild build -project Example/Example.xcodeproj -scheme Example -destination 'id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'Releases
Commit messages use fix:, feat: or BREAKING CHANGE:. Release Please opens a release pull request from those messages. Merging that pull request runs .github/workflows/release.yml, which tests the package and publishes the GitHub Release.
.release-please-manifest.json is the last released version, 5.1.1. Tags do not use a v prefix, because the existing tags do not. Swift Package Manager installs from the git tag. This module imports UIKit, so the release does not attach a Linux static library.
In the repository settings, under Actions, General, Workflow permissions, select read and write permissions and check "Allow GitHub Actions to create and approve pull requests". Under General, Releases, enable release immutability.
Contributing
- This project's layout is based on <https://github.com/fulldecent/swift6-module-template>
- Ignore rules are inlined from Swift.gitignore and Global/Xcode.gitignore. The macOS and secret rules above them come from project-template.
- Releases follow swift6-module-template, release 16.5.0. The published result here is the git tag. That template publishes a Linux static library, and this module imports UIKit.
- lint.yml, .editorconfig, .markdownlint.json, .markdownlintignore, and .prettierignore follow swift6-module-template release 16.6.0. Prettier checks files that are not Markdown. markdownlint checks Markdown. The commands are in that template's Development section.
Package Metadata
Repository: fulldecent/fdwaveformview
Default branch: main
README: README.md