Soundable is a small AVFoundation-based library for playing individual sounds or sound queues in iOS applications. It supports grouping, muting, looping, stopping, completion callbacks, audio-session configuration, and interruption observation.
- iOS 13 or later
- Swift 6.3 or later
- Xcode 26 or a compatible Swift 6.3 toolchain
Soundable's public playback API is isolated to @MainActor. Calls made from UI types normally require no additional work. Calls originating outside the main actor must explicitly switch to it:
Task { @MainActor in
Soundable.play(fileName: "notification.wav")
}In Xcode, choose File > Add Package Dependencies and enter:
https://github.com/lcardevnas/Soundable.git
Select version 2.0.0 or later, then add the Soundable product to your app target.
You can also add Soundable to a package manifest:
dependencies: [
.package(url: "https://github.com/lcardevnas/Soundable.git", from: "2.0.0")
]pod 'Soundable', '~> 2.0'github "lcardevnas/Soundable" ~> 2.0
Create a Sound from a file in a bundle or from a URL:
import Soundable
let sound = Sound(fileName: "guitar-chord.wav")
sound.play { error in
if let error {
print("Playback failed: \(error.localizedDescription)")
}
}The static convenience API retains the sound for the duration of playback:
Soundable.play(fileName: "guitar-chord.wav")The former String.tryToPlay and URL.tryToPlay conveniences remain available for source compatibility but are deprecated in Soundable 2.0.
Play an array or any other sequence of sounds in order:
let intro = Sound(fileName: "intro.wav")
let message = Sound(fileName: "message.wav")
[intro, message].play { error in
if let error {
print("Queue failed: \(error.localizedDescription)")
}
}An empty queue completes immediately with SBError.playingFailed(reason: .noSoundsToPlay).
let ambience = Sound(fileName: "rain.mp3")
ambience.volume = 0.4
ambience.play(groupKey: "ambience", loopsCount: -1)
Soundable.muteAll(for: "ambience")
Soundable.unmuteAll(for: "ambience")
Soundable.stopAll(for: "ambience")loopsCount is the number of additional plays. Use -1 for continuous looping. Muting remembers and restores the previous volume.
Calling stop() or a Soundable.stop… function intentionally does not invoke the pending completion callback.
Soundable.soundEnabled = falseDisabling playback stops all tracked sounds and queues. Later playback attempts complete with .audioDisabled until playback is enabled again.
import AVFoundation
Soundable.activateSession(category: .playback)
// Later, when appropriate:
Soundable.deactivateSession()Audio-session configuration failures are logged and never terminate the host application.
Soundable.observeInterruptions { type, userInfo in
switch type {
case .began:
print("Interruption began")
case .ended:
print("Interruption ended")
@unknown default:
break
}
}The observer remains active for subsequent interruption notifications. Registering again replaces the callback without installing another observer.
Soundable 2.0 raises the minimum deployment target from iOS 9 to iOS 13 and adopts Swift 6 main-actor isolation. Existing API names remain available, but code that calls Soundable from a background or nonisolated context must switch to the main actor. Persisted soundEnabled values are migrated automatically from the legacy string representation.
Soundable is available under the MIT license. See LICENSE.