1

I am in some desperate need of assistance, since my head is really sore from beating it against the wall.

I have one view controller where I am recording audio with my microphone and saving it to the apps document directory. This is working well and I can see the files if I navigate to that folder in the simulator.

My problem is that I have a TableView with its own TableViewController where I want to list the contents of that document directory that contains my recordings. I created the TableView and it is reading and populating the table based on a basic array. My problem is that I am failing to convert it over to read the Document Directory contents instead.

I have future goals for this table to also allow me play, delete, or rename the file from within this TableView via swiping the particular cell.

I have been working on this issue for about 5 days now and I am certain I am overlooking something very simple to you all.

I am using Xcode 7.3.1 with Swift 2.

Thanks in advance and I can't wait until I get to a point where I can contribute.

ViewControllerRecorder.swift (This is where I record and save the audio to the Document Directory, which is working.)

import UIKit
import AVFoundation
import PermissionScope

class ViewControllerRecorder: UIViewController, AVAudioRecorderDelegate, AVAudioPlayerDelegate {

    @IBOutlet weak var stopButton: UIButton!
    @IBOutlet weak var recordButton: UIButton!
    @IBOutlet weak var playButton: UIButton!

    @IBOutlet weak var audioDuration: UISlider!


    var audioRecorder:AVAudioRecorder?
    var audioPlayer:AVAudioPlayer?

    let pscope = PermissionScope()


    override func viewDidLoad() {
        super.viewDidLoad()

        audioDuration.value = 0.0

        // Set up permissions
        pscope.addPermission(MicrophonePermission(),
                             message: "Inorder to use this app, you need to grant the microphone permission")

        // Show dialog with callbacks
        pscope.show({ finished, results in
            print("got results \(results)")
            }, cancelled: { (results) -> Void in
                print("thing was cancelled")
        })

        // Disable Stop/Play button when application launches
        stopButton.enabled = false
        playButton.enabled = false




        // Get the document directory. If fails, just skip the rest of the code
        guard let directoryURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).first else {

            let alertMessage = UIAlertController(title: "Error", message: "Failed to get the document directory for recording the audio. Please try again later.", preferredStyle: .Alert)
            alertMessage.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
            presentViewController(alertMessage, animated: true, completion: nil)

            return
        }

        // Set the default audio file
        let audioFileURL = directoryURL.URLByAppendingPathComponent("PWAC_" + NSUUID().UUIDString + ".m4a")

        // Setup audio session
        let audioSession = AVAudioSession.sharedInstance()

        do {
            try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: AVAudioSessionCategoryOptions.DefaultToSpeaker)




            // Define the recorder setting
            let recorderSetting: [String: AnyObject] = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                AVSampleRateKey: 32000.0,
                AVNumberOfChannelsKey: 2,
                AVEncoderAudioQualityKey: AVAudioQuality.Medium.rawValue
            ]

            // Initiate and prepare the recorder
            audioRecorder = try AVAudioRecorder(URL: audioFileURL, settings: recorderSetting)
            audioRecorder?.delegate = self
            audioRecorder?.meteringEnabled = true
            audioRecorder?.prepareToRecord()

        } catch {
            print(error)
        }

    }


    func updateaudioDuration(){

        audioDuration.value = Float(audioPlayer!.currentTime)
    }

    @IBAction func sliderAction(sender: AnyObject) {


        audioPlayer!.currentTime = NSTimeInterval(audioDuration.value)

    }

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

    @IBAction func play(sender: AnyObject) {
        if let recorder = audioRecorder {
            if !recorder.recording {
                do {
                    audioPlayer = try AVAudioPlayer(contentsOfURL: recorder.url)

                    audioDuration.maximumValue = Float(audioPlayer!.duration)
                    _ = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: #selector(ViewControllerRecorder.updateaudioDuration), userInfo: nil, repeats: true)

                    audioPlayer?.delegate = self
                    audioPlayer?.play()
                    playButton.setImage(UIImage(named: "playing"), forState: UIControlState.Selected)
                    playButton.selected = true
                } catch {
                    print(error)
                }
            }
        }
    }

    @IBAction func stop(sender: AnyObject) {
        recordButton.setImage(UIImage(named: "record"), forState: UIControlState.Normal)
        recordButton.selected = false
        playButton.setImage(UIImage(named: "play"), forState: UIControlState.Normal)
        playButton.selected = false

        stopButton.enabled = false
        playButton.enabled = true

        audioRecorder?.stop()

        let audioSession = AVAudioSession.sharedInstance()

        do {
            try audioSession.setActive(false)
        } catch {
            print(error)
        }
    }

    @IBAction func record(sender: AnyObject) {
        // Stop the audio player before recording
        if let player = audioPlayer {
            if player.playing {
                player.stop()
                playButton.setImage(UIImage(named: "play"), forState: UIControlState.Normal)
                playButton.selected = false
            }
        }

        if let recorder = audioRecorder {
            if !recorder.recording {
                let audioSession = AVAudioSession.sharedInstance()

                do {
                    try audioSession.setActive(true)

                    // Start recording
                    recorder.record()
                    recordButton.setImage(UIImage(named: "recording"), forState: UIControlState.Selected)
                    recordButton.selected = true


                } catch {
                    print(error)
                }

            } else {
                // Pause recording
                recorder.pause()
                recordButton.setImage(UIImage(named: "pause"), forState: UIControlState.Normal)
                recordButton.selected = false
            }
        }

        stopButton.enabled = true
        playButton.enabled = false

    }


    // MARK: - AVAudioRecorderDelegate Methods
    func audioRecorderDidFinishRecording(recorder: AVAudioRecorder, successfully flag: Bool) {
        if flag {

            // iOS8 and later
            let alert = UIAlertController(title: "Recorder",
                                          message: "Finished Recording",
                                          preferredStyle: .Alert)
            alert.addAction(UIAlertAction(title: "Keep", style: .Default, handler: {action in
                print("keep was tapped")
            }))
            alert.addAction(UIAlertAction(title: "Delete", style: .Default, handler: {action in
                print("delete was tapped")
                self.audioRecorder!.deleteRecording()
            }))

            self.presentViewController(alert, animated:true, completion:nil)

//            let alertMessage = UIAlertController(title: "Finish Recording", message: "Successfully recorded the audio!", preferredStyle: .Alert)
//            alertMessage.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
//            presentViewController(alertMessage, animated: true, completion: nil)
            print("Audio has finished recording")
            print(recorder.url)
        }
    }

    // MARK: - AVAudioPlayerDelegate Methods
    func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
        playButton.setImage(UIImage(named: "play"), forState: UIControlState.Normal)
        playButton.selected = false

        let alertMessage = UIAlertController(title: "Finish Playing", message: "Finish playing the recording!", preferredStyle: .Alert)
        alertMessage.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
        presentViewController(alertMessage, animated: true, completion: nil)
        print("Audio has finished playing")
        print(player.url)
    }


}

ViewControllerFileManager.swift (This is the view controller that I want to display the contents of the document directory to a TableView.)

UPDATED 2016/07/27

import UIKit

class ViewControllerFileManager: UIViewController, UITableViewDataSource, UITableViewDelegate {

var arrayRecordings = [String]()

override func viewDidLoad() {
    super.viewDidLoad()

    let fileManager = NSFileManager.defaultManager()
    let dirPath = NSBundle.mainBundle().resourcePath!


    let items = try! fileManager.contentsOfDirectoryAtPath(dirPath)

    for item in items {
        if item.hasSuffix("m4a") {

            arrayRecordings.append(item)

        }
        print("Found \(item)")
    }



    // Do any additional setup after loading the view, typically from a nib.
}

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


func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // Return the number of rows in the section.
    return arrayRecordings.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cellIdentifier = "Cell"
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)



    // Configure the cell...


    cell.textLabel?.text = arrayRecordings[indexPath.row]
    return cell
}

override func prefersStatusBarHidden() -> Bool {
    return true
}

}
Bill
  • 147
  • 1
  • 1
  • 10
  • Hi! Have you checked already this thread: http://stackoverflow.com/questions/8376511/list-saved-files-in-ios-documents-directory-in-a-uitableview ? – fiks Jul 24 '16 at 20:59
  • Hello fiks, indeed that was one of the threads I tried. I could not seem to get it working even thought that seemed to be my answer. I am certain that I was putting the code in the wrong locations. Based on my "ViewControllerFileManager", would you possibly be able to guide me to a correct implementation? – Bill Jul 24 '16 at 21:08
  • 1
    I would put loading on the files in the viewDidLoad. Have you tried this? If yes and it didn't work, I can then spend some time to implement something based on your code. – fiks Jul 24 '16 at 21:10
  • I did try that as well, I must be screwing up something. Also when I enter the code as shown on that page you sent me the link to, i get a number of errors. – Bill Jul 24 '16 at 22:16
  • how are you populating the recordingFileNames variable in your ViewController class? – Christian Abella Jul 24 '16 at 23:32
  • I am very close now and I think I have one minor issue. I have figured out how to populate an array based on the contents of a directory. Unfortinalty it is the "Main Bundle" and not the apps "Document" Directory. Above is my new code for my "ViewControllerFileManager.swift". Would you mind looking it over and see what I am doing wrong. The "ViewControllerRecorder.swift" listed above has not change since the original posting. – Bill Jul 28 '16 at 02:45

2 Answers2

1

If you are populating the array in viewDidLoad, you need to call reloadData of tableView for it to refresh.

override func viewDidLoad()
 {
   super.viewDidLoad()

   // populate recordingFileNames here

   // refresh your table
   reloadTableViewContent()
 } 

 func reloadTableViewContent()
 {
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
      self.tableView.reloadData()
      self.tableView.scrollRectToVisible(CGRectMake(0, 0, 1, 1), animated: false)
    })
 }
Christian Abella
  • 5,747
  • 2
  • 30
  • 42
-1

Here is the code that I got working to populate a TableView with the contents of my apps Document Folder

import UIKit

class ViewControllerFileManager: UIViewController, UITableViewDataSource, UITableViewDelegate {



    var arrayRecordings = [String]()



    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: Read Document Directory and populate Array

        // Get the document directory url:
        let documentsUrl =  NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!

        do {
            // Get the directory contents urls (including subfolders urls):
            let directoryContents = try NSFileManager.defaultManager().contentsOfDirectoryAtURL( documentsUrl, includingPropertiesForKeys: nil, options: [])
            print(directoryContents)

            // Filter directory contents:
            let audioFiles = directoryContents.filter{ $0.pathExtension == "m4a" }
            print("m4a urls:",audioFiles)

            // Get the File Names:
            let audioFileNames = audioFiles.flatMap({$0.URLByDeletingPathExtension?.lastPathComponent})
            print("m4a list:", audioFileNames)

            for audioFileName in audioFileNames {
                arrayRecordings.append(audioFileName)
                print("Found \(audioFileName)")
            }


        } catch let error as NSError {
            print(error.localizedDescription)
        }

    }


    // MARK: TableView Functions
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // Return the number of rows in the section.
        return arrayRecordings.count
    }



    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cellIdentifier = "Cell"
        let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)


        // Configure the cell...
        cell.textLabel?.text = arrayRecordings[indexPath.row]
        return cell
    }


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



    override func prefersStatusBarHidden() -> Bool {
        return true
    }


}
Bill
  • 147
  • 1
  • 1
  • 10