- December 30, 2024
- Mins Read
AudioManager
is a Swift package that provides a modular and easy-to-use interface for implementing audio feedback in your applications. It integrates seamlessly with SwiftUI, enabling you to enhance user experience through customisable audible feedback
Add AudioManager
to your Swift project using Swift Package Manager.
dependencies: [
.package(url: “https://github.com/markbattistella/AudioManager”, from: “1.0.0”)
]
There are three type of ways to use the AudioManager
:
The static action format allows you to trigger audio feedback consistently and simply. In the example below, audio feedback is triggered whenever the isSuccess
state changes.
@State private var isSuccess: Bool = false
Button(“isSuccess: \(isSuccess)”) {
isSuccess.toggle()
}
.audioFeedback(.system(.ui(.tock)), trigger: isSuccess)
You can also use a condition to control when the audio feedback should be triggered, allowing for more focused control over when feedback occurs.
enum Phase { case inactive, active, completed }
@State private var phase: Phase = .inactive
Button(“Update phase”) {
switch phase {
case .inactive: phase = .active
case .active: phase = .completed
case .completed: phase = .inactive
}
}
.audioFeedback(.system(.ui(.tink)), trigger: phase) { oldValue, newValue in
oldValue != .completed && newValue == .completed
}
enum Phase { case inactive, active, completed }
@State private var phase: Phase = .inactive
Button(“Update phase”) {
switch phase {
case .inactive: phase = .active
case .active: phase = .completed
case .completed: phase = .inactive
}
}
.audioFeedback(.system(.ui(.tink)), trigger: phase) { newValue in
newValue == .completed
}
@State private var phase: Bool = false
Button(“Toggle Phase”) {
phase.toggle()
}
.audioFeedback(.system(.ui(.tink)), trigger: phase) {
// Audio feedback triggered
}
The dynamic action approach gives you full control over both the type of feedback and the conditions under which it’s triggered.
enum LoadingState { case ready, success, failure }
@State private var loadingState: LoadingState = .ready
Button(“Update loading state”) {
switch loadingState {
case .ready: loadingState = .success
case .success: loadingState = .failure
case .failure: loadingState = .ready
}
}
.audioFeedback(trigger: loadingState) { oldValue, newValue in
switch (oldValue, newValue) {
case (.failure, .ready):
return .system(.modern(.cameraShutterBurstBegin))
case (.ready, .success):
return .system(.nano(.screenCapture))
case (.success, .failure):
return .system(.new(.update))
default:
return nil
}
}
enum LoadingState { case ready, success, failure }
@State private var loadingState: LoadingState = .ready
Button(“Update loading state”) {
switch loadingState {
case .ready: loadingState = .success
case .success: loadingState = .failure
case .failure: loadingState = .ready
}
}
.audioFeedback(trigger: loadingState) { newValue in
switch newValue {
case .success: return .system(.modern(.cameraShutterBurstBegin))
case .failure: return .system(.nano(.screenCapture))
default: return nil
}
}
enum LoadingState { case ready, success, failure }
@State private var loadingState: LoadingState = .ready
Button(“Update loading state”) {
switch loadingState {
case .ready: loadingState = .success
case .success: loadingState = .failure
case .failure: loadingState = .ready
}
}
.audioFeedback(trigger: loadingState) {
// Audio feedback triggered
}
AudioManager
includes a .audioEffectsEnabled
UserDefaults
key, allowing you to dynamically enable or disable audio based on user settings.
This is helpful if you want to add a settings screen for toggling audio sound effects, or if you need an overall logic to control audio — for example, making it a premium feature.
The package uses an internal, publicly exposed UserDefaults
suite for storing audio-related settings:
@main
struct MyAwesomeApp: App {
init() {
UserDefaults.audio.register([
AudioUserDefaultsKey.audioEffectsEnabled : true
])
}
var body: some Scene {
WindowGroup { … }
}
}
Or manually updating it:
Button(“Turn audio off”) {
UserDefaults.audio.set(false, for: AudioUserDefaultKeys.isAudioEnabled)
}
Button(“Turn audio on”) {
UserDefaults.audio.set(true, for: AudioUserDefaultKeys.isAudioEnabled)
}
If the built-in feedback types are not sufficient, you can use your own custom audio files using the .custom(CustomSoundRepresentable)
Playback
case.
There are a limited accepted audio file extensions allowed: .aif
, .aiff
, .caf
, .mp3
, and .wav
.
Also your audio file is limited to 30
seconds. The playback will simply not occur if it is longer than 30 seconds.
To add a custom audio feedback type:
CustomSoundRepresentable
conforming enum:
enum MyCustomSound: CustomSoundRepresentable {
case tupperwareFallingOutOfCrampedCupboard
var soundFile: SoundFile {
switch self {
case .tupperwareFallingOutOfCrampedCupboard:
Soundfile(name: “tupperwareFallingOutOfCrampedCupboard”, extension: .aif)
}
}
}