Skip to content

SwiftfulThinking/SwiftfulSoundEffects

Repository files navigation

SwiftfulSoundEffects 🎧

An actor-based sound effect library for iOS and macOS. SwiftfulSoundEffects manages multiple AVAudioPlayers with round-robin playback for simultaneous sound effects.

Features

  • Thread-safe actor-based design with modern Swift concurrency
  • Simple synchronous API with async behavior internalized
  • Simultaneous playback via configurable player pools per sound
  • Round-robin player selection for overlapping sound effects
  • Memory management with selective teardown per sound
  • Optional logging for analytics integration

Setup

Details (Click to expand)

Add SwiftfulSoundEffects to your project.

https://github.com/SwiftfulThinking/SwiftfulSoundEffects.git

Import the package.

import SwiftfulSoundEffects

Create a SoundEffectManager instance.

// Basic setup
let soundEffectManager = SoundEffectManager()

// With optional logger for analytics
let soundEffectManager = SoundEffectManager(logger: yourLogger)

Quick Start

Details (Click to expand)

Prepare a sound effect, then play it.

let url = Bundle.main.url(forResource: "coin", withExtension: "wav")!

// Prepare the player
soundEffectManager.prepareSoundEffect(url: url)

// Play when needed
soundEffectManager.playSoundEffect(url: url)

// Clean up when done (optional)
soundEffectManager.tearDownSoundEffect(url: url)

For sounds that overlap (e.g. rapid coin collection), add simultaneous players.

soundEffectManager.prepareSoundEffect(url: url, simultaneousPlayers: 6)

API Reference

Details (Click to expand)

All public methods are synchronous (nonisolated) with async behavior handled internally.

// Preparation (required before playing)
func prepareSoundEffect(url: URL, simultaneousPlayers: Int = 1, volume: Float = 1)

// Playback
func playSoundEffect(url: URL)

// Memory management (optional)
func tearDownSoundEffect(url: URL)

When to Use Each Method

prepareSoundEffect() — Required before playing

  • Creates one or more AVAudioPlayer instances for the given URL
  • simultaneousPlayers controls how many players are created per sound
  • With 1 player (default): calling playSoundEffect() while already playing restarts the sound
  • With multiple players: supports overlapping playback of the same sound via round-robin
  • volume sets playback volume from 0.0 (mute) to 1.0 (max)

playSoundEffect() — Core functionality

  • Selects the next player for the URL using round-robin
  • If the selected player is already playing, playback restarts from the beginning
  • Thread-safe: can be called from any context

tearDownSoundEffect() — Optional memory management

  • Stops and removes all players for the given URL
  • Call during screen disappear or when a sound is no longer needed

Simultaneous Playback

Details (Click to expand)

By default, each sound gets 1 player. If you playSoundEffect() while it's still playing, the sound restarts.

To support overlapping playback (e.g. rapid coin sounds), prepare with multiple players:

// 1 player — playSoundEffect() restarts if already playing
soundEffectManager.prepareSoundEffect(url: coinURL)

// 6 players — supports up to 6 overlapping plays
soundEffectManager.prepareSoundEffect(url: coinURL, simultaneousPlayers: 6)

Players are selected round-robin. If all players are busy, the next one restarts.

SoundEffectFile Pattern

Details (Click to expand)

Create an enum to manage your sound files (not included in the package).

enum SoundEffectFile: String, Equatable {
    case coin
    case pop
    case success

    var fileName: String {
        switch self {
        case .coin: return "Coin.wav"
        case .pop: return "Pop.wav"
        case .success: return "Success.wav"
        }
    }

    var url: URL {
        let path = Bundle.main.path(forResource: fileName, ofType: nil)!
        return URL(fileURLWithPath: path)
    }
}

Then use it with the manager:

soundEffectManager.prepareSoundEffect(url: SoundEffectFile.coin.url, simultaneousPlayers: 4)
soundEffectManager.playSoundEffect(url: SoundEffectFile.coin.url)

Logging Integration

Details (Click to expand)

SwiftfulSoundEffects supports optional logging for analytics.

// Implement the SoundEffectLogger protocol
class MyAnalytics: SoundEffectLogger {
    func trackEvent(event: SoundEffectLogEvent) {
        print("SoundEffect: \(event.eventName)")
    }

    func addUserProperties(dict: [String: Any], isHighPriority: Bool) {
        // Add user properties for analytics
    }
}

// Initialize with logger
let soundEffectManager = SoundEffectManager(logger: MyAnalytics())

Or use SwiftfulLogging directly.

let logManager = LogManager(services: [
    ConsoleService(printParameters: true),
    FirebaseCrashlyticsService(),
    MixpanelService()
])

let soundEffectManager = SoundEffectManager(logger: logManager)

Performance Tips

Details (Click to expand)
  1. Always call prepareSoundEffect() before playSoundEffect() — play does nothing if no players exist
  2. Use tearDownSoundEffect() during screen disappear or memory warnings
  3. Choose simultaneousPlayers wisely — more players = more memory, but supports overlapping
  4. Use lower volume for ambient or background sounds to blend with other audio

Claude Code

This package includes a .claude/swiftful-sound-effects-rules.md with usage guidelines and integration patterns for projects using Claude Code.

Platform Support

  • iOS 16.0+
  • macOS 12.0+

License

SwiftfulSoundEffects is available under the MIT license.

About

Play sound effect files in Swift applications.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages