How To Play Sounds Files And Manage Duration Progress – AVAudioPlayer Tutorial

Update November 2016: Fully updated for Xcode 8 and Swift 3.

AVAudioPlayer class is one of the AVFoundation framework classes and APIs. It comes in handy to play any type of audio file in your iOS app. The class allows to, among other, manage playback operations such as play, pause, rewind and fast forward.

It also provides a protocol that will inform your app of any interruption during playback and whenever the sound has finished playing.

In this tutorial, you will play around with some sound files using the AVAudioPlayer class. You gonna see and learn how easy it is to implement playback operations and how to report playback progress within a progress view.

As usual, let’s do it!

I prepared a starter project that we will work on, start by downloading it here.

Open the project with Xcode and take a look at the Main.storyboard file. The app mainly consists on two screens. The first interface will show some audio tracks in a table view while the next one will handle all audio playback operations. So all of the AVAudioPlayer work will be implemented on that interface, you got that right 😉

Let’s start by showing up some audio files in the table view, a list from which the user can select which track to play.

First, download the sound files here, unzip the folder and drag all of the seven mp3 files to the Xcode project. Make sure Copy items if needed option is checked and click the “Finish” button.

Import mp3 files into Xcode

Next, select the ViewController.swift file from the Project navigator view and implement the following code inside:

// Table View DataSource
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
    let cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "CellID")
    cell?.textLabel?.text = "Track \(indexPath.row + 1)"
    return cell
}
    
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 7
}
    
    
// Table View Delegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    track = indexPath.row
    performSegue(withIdentifier: "MoveToAudioPlayer", sender: self)
}
    
// Prepare for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let audioPlayerVC = segue.destination as! AudioPlayerVC
    audioPlayerVC.trackID = track
}

The first two methods in the code above will populate seven rows to the table view relevant to the audio files you imported previously to the project. The tableView:didSelectRowAtIndexPath: delegate method will get the selected row and store it to a reference variable called track, it will then call a segue with the “MoveToAudioPlayer” identifier to move to the next screen.

Finally, the prepareForSegue method will cast the destination of the segue object and assign the selected track to its trackID property.

Before moving on to implement the main features for this tutorial, copy the following declaration right after the ViewController class name to add a reference for the selected track.

var track: Int!

Next, select the Main.storyboard file, then click on the segue and set its Identifier property to “MoveToAudioPlayer” from the Attributes inspector view.

Set an Identifier to the storyboard segue in Xcode 8

Always in storyboard, select the prototype cell in the table view and set its Identifier property to “CellID” from the Attributes inspector.

Cool, let’s move to the main part now!

Select the AudioPlayerVC.swift file from the Project navigator view and declare the trackID property to shut down the compiling error. Place the following declaration just after the class name:

var trackID: Int!

You can now run the app to make sure the table view is loading correctly.

So far so good, let’s declare a reference for the audio player that will be used within different methods in the AudioPlayerVC class. Add the following property declaration to the class:

var audioPlayer:AVAudioPlayer!

The AVAudioPlayer class belongs to the AVFoundation framework, copy the following import and place it right above the class name:

import AVFoundation

Next, locate the viewDidLoad method and implement the following code inside (after the super.viewDidLoad call):

trackLbl.text = "Track \(trackID + 1)"
// 1
let path: String! = Bundle.main.resourcePath?.appending("/\(trackID!).mp3")
let mp3URL = NSURL(fileURLWithPath: path)
do
  {
  // 2
  audioPlayer = try AVAudioPlayer(contentsOf: mp3URL as URL)
  audioPlayer.play()
  // 3
  Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateAudioProgressView), userInfo: nil, repeats: true)
  progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: false)
}
catch
{
  print("An error occurred while trying to extract audio file")
}

The code above will first update the label with the track name.

Let’s breakdown the rest of it:

// 1: Here you just got the trackID value you passed from the previous view controller to append to the mp3 file path. Then you constructed a url path for the sound file.

// 2: AVAudioPlayer will try to extract the mp3 file at the specified path. If it succeeds, the audio player object will start playing the sound file, otherwise, it will jump to the catch block to handle any occurred error.

// 3: Here you used the scheduledTimer API to trigger a method within a time interval of one second. That method will be responsible of updating the progress view while the sound file is playing. You also initialise the progress property of the progressView to 0 (since the current time of the sound file is 0 at the beginning of the playback).

Now let’s implement the selector that will be triggered every one second in order to update the progress view’s progress.

Implement the following code before the class closing bracket:

func updateAudioProgressView()
{
   if audioPlayer.isPlaying
      {
       // Update progress
       progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: true)
      }
}

Each time called, the method above will update the progress as long as the sound file is playing. So in a pause state, we are now sure that the progress view will be frozen 🙂

Note: Remember the best way to know how far is the sound file, is to divide the current time by the duration of the track.

To the nice part now!

Locate the fastForward action method and place the following code inside:

var time: TimeInterval = audioPlayer.currentTime
time += 5.0 // Go forward by 5 seconds
  if time > audioPlayer.duration
  {
     stop(self)
  }else
  {
     audioPlayer.currentTime = time
  }

Fast forwarding is just updating the current time property of the audio player. Here, you just get the current time and increment it by 5 seconds. Unless the new current time of the player exceeds the total duration of the track (in such case you call the stop function), the method will update the player current time to reflect the new duration.

Locate the fastBackward action method and implement the following code inside:

var time: TimeInterval = audioPlayer.currentTime
time -= 5.0 // Go back by 5 seconds
  if time < 0
  {
     stop(self)
  }else
  {
     audioPlayer.currentTime = time
  }

The only difference here is just you decrement the current time instead of incrementing it. This is how rewind works.

Let’s implement the remaining action methods. To do so, make the following changes:

// Inside the pause action method
audioPlayer.pause()
// Inside the play action method 
if !audioPlayer.isPlaying{
    audioPlayer.play()
}
// Inside the stop action method
audioPlayer.stop()
audioPlayer.currentTime = 0
progressView.progress = 0.0

Before running your app, make sure to stop playing the sound file once you select the back button on the navigation bar. Otherwise, the sound will remain playing and can interfere with other sounds (at such case, you will listen to a tuneless song).

To solve this, you just need to stop the audio player whenever the screen is about to dismiss. To do so, override the viewWillDisappear method with the following implementation:

    override func viewWillDisappear(_ animated: Bool) {
        audioPlayer.stop()
    }

That’s it folks, run your app and enjoy your audio player 🙂

As usual, you can download the final project here.

Feel free to add your comments below, I always would love to hear your thoughts!

Malek
iOS developer with over than 11 years of extensive experience working on several projects with different sized startups and corporates.

10 Comments

  1. Hi timbojill 🙂
    Of course you can use AVAudioPlayer to play short sounds.
    Let me know how it goes with your Piano app. I am looking forward to see it on the App Store.

  2. Nice. Can I do the same with videos? manage duration, I want to detect when video has been finished

  3. Hi Malek_T! Thank you for sharing your project. I a noob and I am trying to develop a tableview app including audio and images as a gallery per each audio files. I do not know how could I implement this feature in your project, so that during the playback of each audio file, the user could slide 16 images. I am trying to develop a music education app, with examples of music sung and notation. What could you suggest me to do? Thank you in advance!

  4. Good day, sir! 🙂
    I just wanted to ask if, pleeaase, you could update the code to comply the Swift 3 codes. Thank you beforehand 🙂

Comments are closed.