import AppKit import Foundation enum SoundEffectCatalog { /// All discoverable system sound names, with "Glass" pinned first. static var systemOptions: [String] { var names = Set(Self.discoveredSoundMap.keys).union(Self.fallbackNames) names.remove("Glass") let sorted = names.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending } return ["Glass"] + sorted } static func displayName(for raw: String) -> String { raw } static func url(for name: String) -> URL? { self.discoveredSoundMap[name] } // MARK: - Internals private static let allowedExtensions: Set = [ "aif", "aiff", "caf", "wav", "m4a", "mp3", ] private static let fallbackNames: [String] = [ "Glass", // default "Ping", "Pop", "Frog", "Submarine", "Funk", "Tink", "Basso", "Blow", "Bottle", "Hero", "Morse", "Purr", "Sosumi", "Mail Sent", "New Mail", "Mail Scheduled", "Mail Fetch Error", ] private static let searchRoots: [URL] = [ FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library/Sounds"), URL(fileURLWithPath: "/Library/Sounds"), URL(fileURLWithPath: "/System/Applications/Mail.app/Contents/Resources"), // Mail “swoosh” URL(fileURLWithPath: "/System/Library/Sounds"), ] private static let discoveredSoundMap: [String: URL] = { var map: [String: URL] = [:] for root in Self.searchRoots { guard let contents = try? FileManager.default.contentsOfDirectory( at: root, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) else { continue } for url in contents where Self.allowedExtensions.contains(url.pathExtension.lowercased()) { let name = url.deletingPathExtension().lastPathComponent // Preserve the first match in priority order. if map[name] == nil { map[name] = url } } } return map }() } @MainActor enum SoundEffectPlayer { private static var lastSound: NSSound? static func sound(named name: String) -> NSSound? { if let named = NSSound(named: NSSound.Name(name)) { return named } if let url = SoundEffectCatalog.url(for: name) { return NSSound(contentsOf: url, byReference: false) } return nil } static func sound(from bookmark: Data) -> NSSound? { var stale = false guard let url = try? URL( resolvingBookmarkData: bookmark, options: [.withoutUI, .withSecurityScope], bookmarkDataIsStale: &stale) else { return nil } let scoped = url.startAccessingSecurityScopedResource() defer { if scoped { url.stopAccessingSecurityScopedResource() } } return NSSound(contentsOf: url, byReference: false) } static func play(_ sound: NSSound?) { guard let sound else { return } self.lastSound = sound sound.stop() sound.play() } }