5

While I play recorded Audio I get this error:

fatal error: unexpectedly found nil while unwrapping an Optional value

on this line of code:

SoundPlayer = try AVAudioPlayer(contentsOfURL: getFileURL())

But it is working perfectly on Simulator except real device.

ViewController.Swift:

import UIKit
import AVFoundation

class ViewController: UIViewController, AVAudioPlayerDelegate, AVAudioRecorderDelegate {

    @IBOutlet var PlayBTN: UIButton!
    @IBOutlet var RecordBTN: UIButton!


    var soundRecorder : AVAudioRecorder!
    var SoundPlayer : AVAudioPlayer!

    var fileName = "audioFile.m4a"

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        setupRecorder()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func setupRecorder(){

        let recordSettings = [AVSampleRateKey : NSNumber(float: Float(44100.0)),
            AVFormatIDKey : NSNumber(int: Int32(kAudioFormatMPEG4AAC)),
            AVNumberOfChannelsKey : NSNumber(int: 1),
            AVEncoderAudioQualityKey : NSNumber(int: Int32(AVAudioQuality.Max.rawValue))]

        do{
            try soundRecorder = AVAudioRecorder(URL: getFileURL(), settings: recordSettings)
                soundRecorder.delegate = self
                soundRecorder.prepareToRecord()
        }
        catch let error as NSError {
            error.description
        }

    }

    func getCacheDirectory() -> String {

        let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) 

        return paths[0]

    }

    func getFileURL() -> NSURL{
        let path  = (getCacheDirectory() as NSString).stringByAppendingPathComponent(fileName)

        let filePath = NSURL(fileURLWithPath: path)


        return filePath
    }


    @IBAction func Record(sender: UIButton) {

        if sender.titleLabel?.text == "Record"{

            soundRecorder.record()
            sender.setTitle("Stop", forState: .Normal)
            PlayBTN.enabled = false

        }
        else{

            soundRecorder.stop()
            sender.setTitle("Record", forState: .Normal)
            PlayBTN.enabled = false
        }

    }

    @IBAction func PlaySound(sender: UIButton) {

        if sender.titleLabel?.text == "Play" {

            RecordBTN.enabled = false
            sender.setTitle("Stop", forState: .Normal)

            preparePlayer()
            SoundPlayer.play()

        }
        else{

            SoundPlayer.stop()
            sender.setTitle("Play", forState: .Normal)

        }

    }

    func preparePlayer(){
        do{
            SoundPlayer = try AVAudioPlayer(contentsOfURL: getFileURL())
            SoundPlayer.delegate = self
            SoundPlayer.volume = 1.0
            SoundPlayer.prepareToPlay()
        }
        catch let error as NSError {
            error.description
        }
    }

    func audioRecorderDidFinishRecording(recorder: AVAudioRecorder, successfully flag: Bool) {
        PlayBTN.enabled = true
    }

    func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
        RecordBTN.enabled = true
        PlayBTN.setTitle("Play", forState: .Normal)
    }

    @IBAction func actDone(sender: AnyObject) {
        //viewRecorder.hidden = true

        let fm = NSFileManager.defaultManager()
        let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String

        do {
            let items = try fm.contentsOfDirectoryAtPath(documentsDirectory)

            for item in items {
                print(item)
            }
        }
        catch let error as NSError {
            error.description
        }
    }
}

Storyboard file for ViewController.Swift:

enter image description here

Additional info:

  • Xcode version : 7.2.1
  • Device : iPhone 5s
  • iOS version : 9.2.1

Note: I have looked for similar SO solutions but none of them helped me. Some of them due to older Swift version and deprecated method. Some solutions were regarding playing audio file from application bundle and some of were using web URLs. So, this case is different(Real Device vs Simulator)

Here is the link of source code on Google Drive.

Bista
  • 7,869
  • 3
  • 27
  • 55

1 Answers1

10

On a real device, it needs to setCategory() for AVAudioSession.sharedInstance()

And a fixed project can be downloaded here too.

func setupRecorder(){

        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
        } catch let error as NSError {
            print(error.description)
        }

        let recordSettings = [AVSampleRateKey : NSNumber(float: Float(44100.0)),
            AVFormatIDKey : NSNumber(int: Int32(kAudioFormatMPEG4AAC)),
            AVNumberOfChannelsKey : NSNumber(int: 1),
            AVEncoderAudioQualityKey : NSNumber(int: Int32(AVAudioQuality.Max.rawValue))]

        do{
            try soundRecorder = AVAudioRecorder(URL: getFileURL(), settings: recordSettings)
                soundRecorder.delegate = self
                soundRecorder.prepareToRecord()
        }
        catch let error as NSError {
            error.description
        }

    }

NOTE: An audio session is the intermediary between your app and iOS used to configure your app’s audio behaviour. If we set category for AVAudioSessionCategoryPlayAndRecord, we define iOS audio behaviour to allow audio input (recording) and output (playback). Referred from: Audio Session Programming Guide

Allen
  • 1,714
  • 17
  • 19
  • Did you try it on real device? – Allen Feb 17 '16 at 13:18
  • 1
    @Mr.UB Could you please check my revised answer? I just tested it on my device. – Allen Feb 17 '16 at 14:00
  • Yep, I tested it too. With @Allen 's fix it works on my devise. – euvs Feb 17 '16 at 14:06
  • it would be great if you can explain what is "session" thing does here. – Bista Feb 17 '16 at 17:01
  • 1
    An audio session is the intermediary between your app and iOS used to configure your app’s audio behaviour. If we set category for ``AVAudioSessionCategoryPlayAndRecord``, we define iOS audio behaviour to allow audio input (recording) and output (playback). – Allen Feb 17 '16 at 17:08
  • One added question: how to record audio in`kAudioFormatiLBC` format? – Bista Feb 18 '16 at 05:46
  • To record in iLBC, you need to remove ``AVSampleRateKey`` in settings and change file extension to ``ilbc``. – Allen Feb 18 '16 at 08:09