Skip to main content

media-play-system-sound-on-mac | ios-app-dev-memo

swift
@State var selectedSoundEffect: SoundEffect? = nil

SoundEffectPicker(selection: $selectedSoundEffect)
swift
import AudioToolbox

struct SoundEffect: Hashable {
let id: SystemSoundID
let name: String

func play() {
AudioServicesPlaySystemSoundWithCompletion(id, nil)
}
}

extension SoundEffect {
static let systemSoundEffects: [SoundEffect] = {
guard let systemSoundFiles = getSystemSoundFileEnumerator() else { return [] }
return systemSoundFiles.compactMap { item in
guard let url = item as? URL, let name = url.deletingPathExtension().pathComponents.last else { return nil }
var soundId: SystemSoundID = 0
AudioServicesCreateSystemSoundID(url as CFURL, &soundId)
return soundId > 0 ? SoundEffect(id: soundId, name: name) : nil
}.sorted(by: { $0.name.compare($1.name) == .orderedAscending })
}()
}

func getSystemSoundFileEnumerator() -> FileManager.DirectoryEnumerator? {
guard let libraryDirectory = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .systemDomainMask, true).first,
let soundsDirectory = NSURL(string: libraryDirectory)?.appendingPathComponent("Sounds"),
let soundFileEnumerator = FileManager.default.enumerator(at: soundsDirectory, includingPropertiesForKeys: nil) else { return nil }
return soundFileEnumerator
}

import SwiftUI

struct SoundEffectPicker: View {
@Binding var selection: SoundEffect?

var body: some View {
Picker(selection: $selection, label: Text("Sound:")) {
Text("None").tag(nil as SoundEffect?)
ForEach(SoundEffect.systemSoundEffects, id: \.self) { sound in
Text(sound.name).tag(sound as SoundEffect?)
}
}.onChange(of: selection) { _, sound in
sound?.play()
}
}
}

func applicationDidFinishLaunching(_: Notification) {
_ = SoundEffect.systemSoundEffects
}