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.
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.
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!
Can this be used for short sounds ? I plan on adding a piano keyboard to one of my apps.
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.
Nice. Can I do the same with videos? manage duration, I want to detect when video has been finished
Hi Kevin 🙂
Sure! For video playback, the AVPlayerViewController class provides all what you need. Let me know if you need help. https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVPlayerViewController_Class/index.html#//apple_ref/occ/cl/AVPlayerViewController
Note: MPMoviePlayerController use to be the class to handle movies playback, but it’s deprecated in iOS 9.
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!
Good day, sir! 🙂
I just wanted to ask if, pleeaase, you could update the code to comply the Swift 3 codes. Thank you beforehand 🙂
Hi @islombekhasanov:disqus . I just updated to Swift 3 🙂
Check it out!
Thanks for updating this to Swift 3. Super awesome!
You welcome 🙂