0 Replies
      Latest reply on Aug 6, 2019 1:45 PM by Bill_G212
      Bill_G212 Level 1 Level 1 (0 points)

        Hello,

        I'm new in programming and I'm trying to make a recording app but I've been stuck with an error; Fatal error: Unexpectedly found nil while unwrapping an optional value . I searched all over the internet and found so much answers but some how I can't fix it. I want to learn how to fix this error.

        The error happens in line 41, before I add the ? to audioFile. Now The error happens in line 91.

        Please note I can record and get the recorded file from directory, but the app crashes at that point

         

        Also in the console I'm receiving this error but the app isn't crashing:

        Optional(/Users/belal/Library/Developer/CoreSimulator/Devices/ADD7FEB7-F61B-4999-B148-8951C0B2F6A3/data/Containers/Data/Applicati ... oice.wav)

        2019-08-06 19:12:44.994689+0300 Sound Recorder[1289:26015] 317:  ca_debug_string: inPropertyData == NULL

         

        Thanks in advance.

        Here is my code:

         

        //
        //  PlaySoundsViewController=Audio.swift
        //  Sound Recorder
        //
        //  Created by Belal Gawish on 8/3/19.
        //  Copyright © 2019 Belal Gawish. All rights reserved.
        //
        
        import UIKit
        import AVFoundation
        
        // MARK: - PlaySoundsViewController: AVAudioPlayerDelegate
        
        extension PlaySoundsViewController: AVAudioPlayerDelegate {
            
            // MARK: Alerts
            
            struct Alerts {
                static let DismissAlert = "Dismiss"
                static let RecordingDisabledTitle = "Recording Disabled"
                static let RecordingDisabledMessage = "You've disabled this app from recording your microphone, Check your Settings."
                static let RecordingFailedTitle = "Recording Failed"
                static let RecordingFailedMessage = "Something went wrong with your recording."
                static let AudioRecorderError = "Audio Recorder Error"
                static let AudioSessionError = "Audio Session Error"
                static let AudioRecordingError = "Audio Recording Error"
                static let AudioFileError = "Audio File Error"
                static let AudioEngineError = "Audio Engine Error"
            }
            
            // MARK: PlayingState (raw values correspond to sender tags)
            
            enum PlayingState { case playing, notPlaying }
            
            // MARK: Audio Functions
            
            func setupAudio() {
                
                    // initialize (recording) audio file
                    do {
                        audioFile? = try AVAudioFile(forReading: recordedAudioURL as URL)
                    } catch {
                        showAlert(Alerts.AudioFileError, message: String(describing: error))
                    }
                }
        
            func playSound(rate: Float? = nil, pitch: Float? = nil, echo: Bool = false, reverb: Bool = false) {
                
                // initialize audio engine components
                audioEngine = AVAudioEngine()
                
                // node for playing audio
                audioPlayerNode = AVAudioPlayerNode()
                audioEngine.attach(audioPlayerNode)
                
                // node for adjusting rate/pitch
                let changeRatePitchNode = AVAudioUnitTimePitch()
                if let pitch = pitch {
                    changeRatePitchNode.pitch = pitch
                }
                if let rate = rate {
                    changeRatePitchNode.rate = rate
                }
                audioEngine.attach(changeRatePitchNode)
                
                // node for echo
                let echoNode = AVAudioUnitDistortion()
                echoNode.loadFactoryPreset(.multiEcho1)
                audioEngine.attach(echoNode)
                
                // node for reverb
                let reverbNode = AVAudioUnitReverb()
                reverbNode.loadFactoryPreset(.cathedral)
                reverbNode.wetDryMix = 50
                audioEngine.attach(reverbNode)
                
                // connect nodes
                if echo == true && reverb == true {
                    connectAudioNodes(audioPlayerNode, changeRatePitchNode, echoNode, reverbNode, audioEngine.outputNode)
                } else if echo == true {
                    connectAudioNodes(audioPlayerNode, changeRatePitchNode, echoNode, audioEngine.outputNode)
                } else if reverb == true {
                    connectAudioNodes(audioPlayerNode, changeRatePitchNode, reverbNode, audioEngine.outputNode)
                } else {
                    connectAudioNodes(audioPlayerNode, changeRatePitchNode, audioEngine.outputNode)
                }
                
                // schedule to play and start the engine!
                audioPlayerNode.stop()
                
                audioPlayerNode.scheduleFile(audioFile, at: nil)
                {
                    
                    var delayInSeconds: Double = 0
                    
                    if let lastRenderTime = self.audioPlayerNode.lastRenderTime, let playerTime = self.audioPlayerNode.playerTime(forNodeTime: lastRenderTime) {
                        
                        if let rate = rate {
                            delayInSeconds = Double(self.audioFile.length - playerTime.sampleTime) / Double(self.audioFile.processingFormat.sampleRate) / Double(rate)
                        } else {
                            delayInSeconds = Double(self.audioFile.length - playerTime.sampleTime) / Double(self.audioFile.processingFormat.sampleRate)
                        }
                    }
                    
                    // schedule a stop timer for when audio finishes playing
                    self.stopTimer = Timer(timeInterval: delayInSeconds, target: self, selector: #selector(PlaySoundsViewController.stopAudio), userInfo: nil, repeats: false)
                    RunLoop.main.add(self.stopTimer!, forMode: RunLoop.Mode.default)
                }
                
                do {
                    try audioEngine.start()
                } catch {
                    showAlert(Alerts.AudioEngineError, message: String(describing: error))
                    return
                }
                
                // play the recording!
                audioPlayerNode.play()
            }
            
            @objc func stopAudio() {
                
                if let audioPlayerNode = audioPlayerNode {
                    audioPlayerNode.stop()
                }
                
                if let stopTimer = stopTimer {
                    stopTimer.invalidate()
                }
                
                configureUI(.notPlaying)
                
                if let audioEngine = audioEngine {
                    audioEngine.stop()
                    audioEngine.reset()
                }
            }
            
            // MARK: Connect List of Audio Nodes
            
            func connectAudioNodes(_ nodes: AVAudioNode...) {
                for x in 0..            audioEngine.connect(nodes[x], to: nodes[x+1], format:
                        audioFile?.processingFormat)
                }
            }
            
            // MARK: UI Functions
            
            func configureUI(_ playState: PlayingState) {
                switch(playState) {
                case .playing:
                    setPlayButtonsEnabled(false)
                    stopButton.isEnabled = true
                case .notPlaying:
                    setPlayButtonsEnabled(true)
                    stopButton.isEnabled = false
                }
            }
            
            func setPlayButtonsEnabled(_ enabled: Bool) {
                snailButton.isEnabled = enabled
                chipmunkButton.isEnabled = enabled
                rabbitButton.isEnabled = enabled
                vaderButton.isEnabled = enabled
                echoButton.isEnabled = enabled
                reverbButton.isEnabled = enabled
            }
            
            func showAlert(_ title: String, message: String) {
                let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        
        
                alert.addAction(UIAlertAction(title: Alerts.DismissAlert, style: .default, handler: nil))
                self.present(alert, animated: true, completion: nil)
            }
        }