Search code examples
iosswiftoption-typeforced-unwrapping

Best way to remove force unwrapping


var recorder : Recorder? = nil

func startAudioRecording() {
    if recorder == nil {
        recorder = Recorder()
    }
    if !recorder!.isRunning() {
        recorder?.startRecording({ [weak self] audioData in
            self?.remoteInterface?.sendVoice(audioData.0)
        }, withCompletionBlock: { (_) in })
    }
}

func stopAudioRecording(_ keyCommand: String!){
    if let _ = recorder {
        if(recorder?.isRunning())! {
            recorder?.stopRecording(completionBlock: { (isFinished: Bool) in
                DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + Double(Int64(1 * NSEC_PER_MSEC)) / Double(NSEC_PER_SEC), execute: { [unowned self] in
                    self.remoteInterface?.sendTouchUp(keyCommand)
                    self.audioRecorder = nil
                })
            })
        }
    }
}

How we can remove the force unwrap?


Solution

  • First one you can deal with using if let (or alternatively guard let):

    if let recorder = recorder {
        // here it is unwrapped
    } else {
        // handle nil case
    }
    

    In the second one I would use default value:

    if (recorder?.isRunning() ?? false) {
        // ...
    }
    

    However, I see that you are already using if let in the second case, so you don't need that. So I think the best it would be with if let in both case, so maybe something like this:

    var recorder : Recorder? = nil
    
    func startAudioRecording(){
        if recorder == nil {
            recorder = Recorder()
        }
        if let recorder = recorder,
            !recorder.isRunning() {
    
            recorder.startRecording({ [weak self] audioData in
                self?.remoteInterface?.sendVoice(audioData.0)
            }, withCompletionBlock: { (_) in })
        }
    }
    
    func stopAudioRecording(_ keyCommand: String!){
        if let recorder = recorder,
            recorder.isRunning() {
    
            recorder.stopRecording(completionBlock: { (isFinished: Bool) in
                DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + Double(Int64(1 * NSEC_PER_MSEC)) / Double(NSEC_PER_SEC), execute: { [unowned self] in
                    self.remoteInterface?.sendTouchUp(keyCommand)
                    self.audioRecorder = nil
                })
            })
        }
    }
    

    Or, as @Fogmaster noted, using guard in the second method might look a bit nicer:

    func stopAudioRecording(_ keyCommand: String!){
        guard let recorder = recorder,
            recorder.isRunning() else {
            return
        }
    
        recorder.stopRecording(completionBlock: { (isFinished: Bool) in
            DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + Double(Int64(1 * NSEC_PER_MSEC)) / Double(NSEC_PER_SEC), execute: { [unowned self] in
                self.remoteInterface?.sendTouchUp(keyCommand)
                self.audioRecorder = nil
            })
        })
    }