Avatar ·

Как воспроизвести звук в приложении iOS на Swift?

Всем привет! Я новичок в iOS-разработке и пытаюсь добавить звуковые эффекты в своё приложение. Нужно воспроизвести короткий звуковой файл (например, звук нажатия кнопки). Я пробовал использовать AVAudioPlayer, но что-то идёт не так — звук не воспроизводится, или приложение вылетает. Вот мой код: ```swift import UIKit import AVFoundation class ViewController: UIViewController { var player: AVAudioPlayer? @IBAction func playSound(_ sender: UIButton) { let path = Bundle.main.path(forResource: "sound.mp3", ofType:nil)! let url = URL(fileURLWithPath: path) do { player = try AVAudioPlayer(contentsOf: url) player?.play() } catch { print("Ошибка воспроизведения: \(error.localizedDescription)") } } } ``` Файл `sound.mp3` я добавил в проект (перетащил в папку Supporting Files). Но при нажатии кнопки ничего не происходит, и в консоль выводится ошибка: "The operation couldn’t be completed. (OSStatus error 1954045808.)" или просто ничего не выводится. Подскажите, что я делаю не так? Нужно ли как-то по-особенному настраивать аудиосессию? Или, может быть, есть более простой способ проиграть звук (например, для коротких эффектов)? Буду благодарен за подробное объяснение! <<>>

Проблема: неправильная настройка аудиосессии и подготовка плеера

Ваша ошибка (OSStatus 1954045808) — это код kAudioFileUnsupportedDataFormatError. Она возникает, когда AVAudioPlayer не может декодировать файл. Чаще всего это связано с тем, что файл не добавлен в Bundle или имеет неподдерживаемый формат. Но есть и вторая, более скрытая причина: аудиосессия не активирована.

Правильное решение

Вот рабочий пример с полной настройкой:

import UIKit
import AVFoundation

class ViewController: UIViewController {
    
    var player: AVAudioPlayer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupAudioSession()
    }
    
    // 1. Настраиваем аудиосессию
    private func setupAudioSession() {
        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
            try AVAudioSession.sharedInstance().setActive(true)
        } catch {
            print("Ошибка настройки аудиосессии: \(error.localizedDescription)")
        }
    }
    
    // 2. Воспроизведение звука
    @IBAction func playSound(_ sender: UIButton) {
        guard let url = Bundle.main.url(forResource: "sound", withExtension: "mp3") else {
            print("Файл не найден в Bundle")
            return
        }
        
        do {
            player = try AVAudioPlayer(contentsOf: url)
            player?.prepareToPlay() // подготовка к воспроизведению
            player?.play()
        } catch {
            print("Ошибка воспроизведения: \(error.localizedDescription)")
        }
    }
}

Ключевые моменты:

  • Аудиосессия — обязательно вызывайте setCategory(.playback) и setActive(true) до воспроизведения. Категория .playback позволяет звуку играть даже в беззвучном режиме.
  • Проверка Bundle — используйте Bundle.main.url(forResource:withExtension:) вместо path, это надёжнее.
  • prepareToPlay() — подготавливает буфер, уменьшает задержку при первом воспроизведении.
  • Форматы файлов: поддерживаются .mp3, .wav, .aac, .m4a. Убедитесь, что файл действительно добавлен в target (галочка в "Add to targets").

Для коротких звуковых эффектов (SystemSound)

Если вам нужно проиграть короткий звук (до 30 секунд) без управления громкостью/паузой, используйте AudioToolbox — он проще и быстрее:

import AudioToolbox

// Воспроизведение системного звука
AudioServicesPlaySystemSound(1000) // звук "входящее сообщение"

// Воспроизведение своего файла
if let soundURL = Bundle.main.url(forResource: "click", withExtension: "wav") {
    var soundID: SystemSoundID = 0
    AudioServicesCreateSystemSoundID(soundURL as CFURL, &soundID)
    AudioServicesPlaySystemSound(soundID)
}

Почему ваш код не работал?

  1. Отсутствовала настройка аудиосессии — iOS блокирует воспроизведение, если сессия не активирована.
  2. Force unwrap path — если файл не найден, приложение упадёт с крашем.
  3. Файл мог быть не скопирован в Bundle — проверьте, что в "Build Phases" → "Copy Bundle Resources" есть ваш файл.

Теперь ваш звук будет работать! Для коротких эффектов рекомендую SystemSound — он не требует создания экземпляра плеера и работает быстрее.

<<>>

Альтернатива: AVAudioPlayer с делегатом и обработкой ошибок

Если вам нужно больше контроля (например, отслеживать окончание воспроизведения или обрабатывать прерывания), используйте делегат AVAudioPlayerDelegate:

class ViewController: UIViewController, AVAudioPlayerDelegate {
    
    var player: AVAudioPlayer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupAudio()
    }
    
    private func setupAudio() {
        guard let url = Bundle.main.url(forResource: "sound", withExtension: "mp3") else { return }
        do {
            player = try AVAudioPlayer(contentsOf: url)
            player?.delegate = self
            player?.prepareToPlay()
        } catch {
            print("Ошибка: \(error)")
        }
    }
    
    @IBAction func play(_ sender: UIButton) {
        player?.play()
    }
    
    // Делегат: вызывается когда звук закончился
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        print("Воспроизведение завершено. Успешно: \(flag)")
        // Можно перезапустить или сделать что-то ещё
    }
    
    // Обработка прерываний (звонок, другой звук)
    func audioPlayerBeginInterruption(_ player: AVAudioPlayer) {
        player.pause()
    }
    
    func audioPlayerEndInterruption(_ player: AVAudioPlayer, withOptions flags: Int) {
        player.play()
    }
}

Также обратите внимание: для длительного воспроизведения (музыка, подкасты) лучше использовать AVPlayer (более мощный), а для простых звуков — AVAudioPlayer или SystemSound. Не забывайте вызывать player?.stop() и обнулять плеер при уходе с экрана, чтобы избежать утечек памяти.