1

Fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)

This message is because my variable is set to nil but the code is expecting it to not be nil. But I don't have a solution. When I remove the question mark from the casting and assignment other errors happen.

Thread1 Fatal error green highlighted line at if deviceInput == nil!. And another error green highlighted line at beginSession() call.

The app starts, camera torch gets turned on automatically as per my code but then the app crashes there. App stays running stuck on the launch screen with the torch still on.

Could you please see how much camera session is set up and tell me what's wrong? Thanks

import UIKit
import Foundation
import AVFoundation
import CoreMedia
import CoreVideo

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {

let captureSession = AVCaptureSession()
var captureDevice : AVCaptureDevice?
var validFrameCounter: Int = 0

// for sampling from the camera
enum CurrentState {
    case statePaused
    case stateSampling
}
var currentState = CurrentState.statePaused

override func viewDidLoad() {
    super.viewDidLoad()

    captureSession.sessionPreset = AVCaptureSessionPresetHigh

    let devices = AVCaptureDevice.devices()

    // Loop through all the capture devices on this phone
    for device in devices {
        // Make sure this particular device supports video
        if (device.hasMediaType(AVMediaTypeVideo)) {
            // Finally check the position and confirm we've got the back camera
            if(device.position == AVCaptureDevicePosition.Back) {
                captureDevice = device as? AVCaptureDevice
                if captureDevice != nil {
                    //println("Capture device found")
                    beginSession() // fatal error
                }
            }
        }
    }

}

// configure device for camera and focus mode


// start capturing frames
func beginSession() {

    // Create the AVCapture Session

    var err : NSError? = nil
    captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &err))

    if err != nil {
        println("error: \(err?.localizedDescription)")
    }

    // Automatic Switch ON torch mode
    if  captureDevice!.hasTorch {
        // lock your device for configuration
        captureDevice!.lockForConfiguration(nil)
        // check if your torchMode is on or off. If on turns it off otherwise turns it on
        captureDevice!.torchMode = captureDevice!.torchActive ? AVCaptureTorchMode.Off : AVCaptureTorchMode.On
        // sets the torch intensity to 100%
        captureDevice!.setTorchModeOnWithLevel(1.0, error: nil)
        // unlock your device
        captureDevice!.unlockForConfiguration()
    }

    // Create a AVCaptureInput with the camera device
    var deviceInput : AVCaptureInput = AVCaptureDeviceInput.deviceInputWithDevice(captureDevice, error: &err) as! AVCaptureInput
    if deviceInput == nil! {   // fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)
        println("error: \(err?.localizedDescription)")
    }

    // Set the output
    var videoOutput : AVCaptureVideoDataOutput = AVCaptureVideoDataOutput()

    // create a queue to run the capture on
    var captureQueue : dispatch_queue_t = dispatch_queue_create("captureQueue", nil)

    // setup ourself up as the capture delegate
    videoOutput.setSampleBufferDelegate(self, queue: captureQueue)

    // configure the pixel format
    videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32BGRA)]
    // kCVPixelBufferPixelFormatTypeKey is a CFString btw.

    // set the minimum acceptable frame rate to 10 fps
    captureDevice!.activeVideoMinFrameDuration = CMTimeMake(1, 10)

    // and the size of the frames we want - we'll use the smallest frame size available
    captureSession.sessionPreset = AVCaptureSessionPresetLow

    // Add the input and output
    captureSession.addInput(deviceInput)
    captureSession.addOutput(videoOutput)


    // Start the session
    captureSession.startRunning()


    func setState(state: CurrentState){
        switch state
        {
        case .statePaused:
            // what goes here? Something like this?
            UIApplication.sharedApplication().idleTimerDisabled = false
        case .stateSampling:
            // what goes here? Something like this?
            UIApplication.sharedApplication().idleTimerDisabled = true  

        }
    }

    // sampling from the camera
    currentState = CurrentState.stateSampling


    // stop the app from sleeping
    UIApplication.sharedApplication().idleTimerDisabled = true

    // update our UI on a timer every 0.1 seconds
    NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)

    func stopCameraCapture() {
        captureSession.stopRunning()

    }


    // pragma mark Pause and Resume of detection
    func pause() {
        if currentState == CurrentState.statePaused {
            return
        }

        // switch off the torch
        if captureDevice!.isTorchModeSupported(AVCaptureTorchMode.On) {
            captureDevice!.lockForConfiguration(nil)
            captureDevice!.torchMode = AVCaptureTorchMode.Off
            captureDevice!.unlockForConfiguration()
        }
        currentState = CurrentState.statePaused
        // let the application go to sleep if the phone is idle
        UIApplication.sharedApplication().idleTimerDisabled = false
    }


    func resume() {
        if currentState != CurrentState.statePaused {
            return
        }

        // switch on the torch
        if captureDevice!.isTorchModeSupported(AVCaptureTorchMode.On) {
            captureDevice!.lockForConfiguration(nil)
            captureDevice!.torchMode = AVCaptureTorchMode.On
            captureDevice!.unlockForConfiguration()
        }
        currentState = CurrentState.stateSampling
        // stop the app from sleeping
        UIApplication.sharedApplication().idleTimerDisabled = true
}

}
}
Lola
  • 2,591
  • 6
  • 24
  • 49

1 Answers1

2

Looking at your code, you should really try to get out of the habit of force-unwrapping optionals using ! at any opportunity, especially just to “make it compile". Generally speaking, if you ever find yourself writing if something != nil, there’s probably a better way to write what you want. Try looking at the examples in this answer for some idioms to copy. You might also find this answer useful for a high-level explanation of why optionals are useful.

AVCaptureDeviceInput.deviceInputWithDevice returns an AnyObject, and you are force-casting it to a AVCaptureInput with this line:

var deviceInput = AVCaptureDeviceInput.deviceInputWithDevice(captureDevice, error: &err) as! AVCaptureInput

(you don’t need to state the type of deviceInput by the way, Swift can deduce it from the value on the right-hand side)

When you write as!, you are telling the compiler “don’t argue with me, force the result to be of type AVCaptureInput, no questions asked”. If it turns out what is returned is something of a different type, your app will crash with an error.

But then on the next line, you write:

if deviceInput == nil! {

I’m actually quite astonished this compiles at all! But it turns out it does, and it’s not surprising it crashes. Force-unwrapping a value that is nil will crash, and you are doing this in it’s purest form, force-unwrapping a nil literal :)

The problem is, you’ve already stated that deviceInput is a non-optional type AVCaptureInput. Force-casting the result is probably not the right thing to do. As the docs for state,

If the device cannot be opened because it is no longer available or because it is in use, for example, this method returns nil, and the optional outError parameter points to an NSError describing the problem.

The right way to handle this is to check is the result is nil, and act appropriately. So you want to do something like:

if let deviceInput = AVCaptureDeviceInput.deviceInputWithDevice(captureDevice, error: &err) as? AVCaptureInput
    // use deviceInput
}
else {
    println("error: \(err?.localizedDescription)")
}
Community
  • 1
  • 1
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • Thanks. I wrote `if let deviceInput = AVCaptureDeviceInput.deviceInputWithDevice(captureDevice, error: &err) as? AVCaptureInput { // Set the input device captureSession.addInput(deviceInput) } else { println("error: \(err?.localizedDescription)") }` and it got rid of that error but now I have this runtime error. http://i.imgur.com/X7RMcEx.png and http://i.imgur.com/as3MIRx.png. I looked to see if there were 2 inputs or 2 outputs at the same time but couldn't really see the problem. Should I write a new question? – Minor Stackford Apr 29 '15 at 06:36
  • Yes, once you’ve got past the nil-unwrap problems, try posting a new question. Try and make the code as short as possible to demonstrate the problem, that will make it more likely you’ll get people to answer. – Airspeed Velocity Apr 29 '15 at 06:52
  • Thanks Airspeed. If you have a chance please look at my new question. http://stackoverflow.com/questions/29951445/nsinvalidargumentexception-reason-multiple-audio-video-avcaptureinputs – Minor Stackford Apr 29 '15 at 19:36