AudioManager
  • December 12, 2024

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

 Features

  • Custom Audio: Easily define and trigger audio feedback.
  • SwiftUI Extensions: Add audio feedback to SwiftUI views in a declarative way.
  • User Preferences: Enable or disable audio feedback based on user settings through simple configuration.
  • Custom Audio Patterns: Extend and create your own audio feedback.

Installation


Add AudioManager to your Swift project using Swift Package Manager.

dependencies: [
.package(url: “https://github.com/markbattistella/AudioManager”, from: “1.0.0”)
]

Usage


There are three type of ways to use the AudioManager:

  • Static Action: This is the simplest method, used when you want to trigger audio feedback for a particular state change. It’s consistent and straightforward — ideal when the audio feedback needs to occur every time a specific condition (like a state variable changing) is met.
  • Static Action with Condition: This approach adds more control compared to the standard static action. Here, you specify a set of conditions to determine when the audio feedback should be triggered. This allows you to handle more nuanced scenarios — such as only playing feedback when transitioning from one specific state to another, while ignoring others.
  • Dynamic Action: The most flexible of the three, dynamic actions let you determine the type of audio feedback based on the old and new values during a state change. This means you can implement complex feedback behaviours that respond differently based on how the state transitions, allowing for a more dynamic and tailored user experience.

Static Action

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)

Static Action with Condition

You can also use a condition to control when the audio feedback should be triggered, allowing for more focused control over when feedback occurs.

Old and New Values

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
}

New Value Only

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
}

No Parameters

@State private var phase: Bool = false

Button(“Toggle Phase”) {
phase.toggle()
}
.audioFeedback(.system(.ui(.tink)), trigger: phase) {
// Audio feedback triggered
}

Dynamic Action

The dynamic action approach gives you full control over both the type of feedback and the conditions under which it’s triggered.

Old and New Values

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
}
}

New Values Only

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
}
}

No Parameters

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
}

Configuring Audio Settings

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.

Built-in UserDefaults Suite

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)
}

Extending Audio Feedback Types

If the built-in feedback types are not sufficient, you can use your own custom audio files using the .custom(CustomSoundRepresentable) Playback case.

Custom File Restrictions

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.

Creating a Custom Feedback

To add a custom audio feedback type:

  1. Define a CustomSoundRepresentable conforming enum:

enum MyCustomSound: CustomSoundRepresentable {
case tupperwareFallingOutOfCrampedCupboard

var soundFile: SoundFile {
switch self {
case .tupperwareFallingOutOfCrampedCupboard:
Soundfile(name: “tupperwareFallingOutOfCrampedCupboard”, extension: .aif)
}
}
}

GitHub


View Github

#audio #AudioManager #ios 17 #macos #swift #video #videobackground #vision os
YOU MIGHT ALSO LIKE...
exyte

     

camerakit-ios

CameraKit helps you add reliable camera to your app quickly. Our open source camera platform provides consistent capture results, service ...

HybridCamera

[video width="192" height="416" mp4="https://swiftgit.com/wp-content/uploads/2024/12/68747470733a2f2f7261776769742e636f6d2f7374796c656b69742f696d672f6d61737465722f7669645f6564697465645f325f326d622e676966.mp4"][/video]

TakeASelfie

An iOS framework that uses the front camera, detects your face and takes a selfie. This api opens the front ...

iOS-Depth-Sampler

Code examples of Depth APIs in iOS