Var init in do-catch - swift

Following code:
// Setup components
do {
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
let deviceInput = try AVCaptureDeviceInput(device: captureDevice)
let output = AVCaptureMetadataOutput()
let session = AVCaptureSession()
} catch {
return false
}
After this you can't access the initialized variables. The error is "Use of unresolved identifier" if I want to access e.g. deviceInput. But why? Either AVCaptureDeviceInput() crashes and the catch-Block returns or all is right and the variables are successfully initialized. What's the best solution to solve this ?

Vacawama's answer is perfectly correct, but just for educational purposes, here's a simplified version. You don't need anything but the initialization of deviceInput to happen inside a do block:
func test() {
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
let deviceInput : AVCaptureDeviceInput
do { deviceInput = try AVCaptureDeviceInput(device: captureDevice) } catch {return}
let output = AVCaptureMetadataOutput()
let session = AVCaptureSession()
// ... other stuff here
print("got to here")
}
If the try fails, "got to here" never prints; we have exited the function in good order.
Still another approach might be to let your surrounding function throw and just go for it, with no do...catch at all:
func test() throws {
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
let deviceInput = try AVCaptureDeviceInput(device: captureDevice)
let output = AVCaptureMetadataOutput()
let session = AVCaptureSession()
// ... other stuff here
print("got to here")
}
This moves the onus of error-checking onto the caller of test().

The do block defines a new scope. If you declare the variables with let or var inside of the do {}, then they are only accessible within that block. If you want to use them after the do {}, then declare them before the do statement. Note, that you don't have to give them initial values, even if they are declared with let because you will only be setting them once before using them:
func foo() -> Bool {
// Setup components
let deviceInput: AVCaptureDeviceInput
let captureDevice: AVCaptureDevice
let output: AVCaptureMetadataOutput
let session: AVCaptureSession
do {
captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
deviceInput = try AVCaptureDeviceInput(device: captureDevice)
output = AVCaptureMetadataOutput()
session = AVCaptureSession()
} catch {
return false
}
// Do something to demo that the variables are accessible
print(deviceInput.description)
print(output.description)
print(session.description)
return false
}

Related

Error=Extra trailing closure passed in call,

//creates the session
let session = AVCaptureSession()
//Defines the capture device
AVCaptureDevice.default(for: AVMediaType.video)
{
let input = AVCaptureDeviceInput(device: captureDevice)
session.addInput(input)
}
Not sure how to solve this error any help would be greatly appreciated
AVCaptureDevice.default has a return value. You have to assign it to a variable.
And since the value is optional unwrap it safely
guard let captureDevice = AVCaptureDevice.default(for: AVMediaType.video),
let input = try? AVCaptureDeviceInput(device: captureDevice) else { return }
session.addInput(input)

Value of optional type 'AVCaptureDevice?' must be unwrapped to a value of type 'AVCaptureDevice'

I was following a tutorial on how to capture a barcode scanner from an ipad camera and this is the code that was written. The tutorial was written in xcode 8 and I am using Xcode 10. I am getting an error that
"Value of optional type 'AVCaptureDevice?' must be unwrapped to a
value of type 'AVCaptureDevice'"
in the do try catch statement. Can someone please tell me the correct way to deal with the optional value in this situation? When I change the line to read "let input = try AVCaptureDeviceInput(device: captureDevice!)"
the app crashes.
import AVFoundation
import UIKit
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var video = AVCaptureVideoPreviewLayer() //contains what the camera is pointing at
override func viewDidLoad() {
super.viewDidLoad()
//creates session
let session = AVCaptureSession()
//define capture device
let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
do{
let input = try AVCaptureDeviceInput(device: captureDevice)//Error occurs here
session.addInput(input) //input coming from camera
}
catch{
print("Error")
}
let output = AVCaptureMetadataOutput()
session.addOutput(output)
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
output.metadataObjectTypes = [AVMetadataObject.ObjectType.code93, AVMetadataObject.ObjectType.code39] //may need to change this based on barcode type
video = AVCaptureVideoPreviewLayer(session: session)
video.frame = view.layer.bounds
view.layer.addSublayer(video)
session.startRunning()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
When I searched answer for this issue, it first showed your question only. Then I sought help from my colleague as well as finally it run without any error. Try below code.
let captureDevice = AVCaptureDevice.default(for: .video)
do{
let input = try AVCaptureDeviceInput(device: captureDevice!)
if self.session.canAddInput(input) {
self.captureSession.addInput(input)
}
}
catch{
print("Error")
}

AVCaptureDeviceInput Unexpectedly found nil while unwrapping an Optional value

I'm trying to get the image of the capturing device but it keeps returning nil by if let input... and I can't find a solution.
It tries to catch the error but it instantly cashes..
let backCamera = AVCaptureDevice.default(for: AVMediaType.video)
do {
if let input = try AVCaptureDeviceInput(device: backCamera!) as? AVCaptureInput {
if (captureSession?.canAddInput(input))! {
captureSession?.addInput(input)
stillImageOutput = AVCapturePhotoOutput()
let settings = AVCapturePhotoSettings()
let settingsFormat = [AVVideoCodecKey : AVVideoCodecType.jpeg]
settings.previewPhotoFormat = settingsFormat
stillImageOutput?.capturePhoto(with: settings, delegate: self as! AVCapturePhotoCaptureDelegate)
if (captureSession?.canAddOutput(stillImageOutput!))! {
captureSession?.addOutput(stillImageOutput!)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspect
previewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer!)
captureSession?.startRunning()
}
}
}
} catch let error as NSError {
print("Error: \(error)")
}
It's not your error throwing that's causing the crash - it's all your force unwrapping. backCamera, captureSession, stillImageOutput, and previewLayer are all Optionals, but you're force unwrapping all of them every time. Why not use guard statements or optional binding to avoid those crashes? Ultimately you can't run any of that code if those items are nil, so I'd refactor your code like this:
//Use guard to make sure you have a non-nil captureSession and a default device for .video
guard let captureSession = captureSession, let backCamera = AVCaptureDevice.default(for: AVMediaType.video) else { return }
do {
if let input = try AVCaptureDeviceInput(device: backCamera) as? AVCaptureInput {
if (captureSession.canAddInput(input)) {
captureSession.addInput(input)
stillImageOutput = AVCapturePhotoOutput()
//Since stillImageOutput is an Optional I'm putting in another guard just to avoid having to deal with it as an Optional
guard let stillImageOutput = stillImageOutput else { return }
let settings = AVCapturePhotoSettings()
let settingsFormat = [AVVideoCodecKey : AVVideoCodecType.jpeg]
settings.previewPhotoFormat = settingsFormat
stillImageOutput.capturePhoto(with: settings, delegate: self as! AVCapturePhotoCaptureDelegate)
if (captureSession.canAddOutput(stillImageOutput)) {
captureSession.addOutput(stillImageOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
//Same as above - since you declared previewLayer as Optional I'll guard so I don't have to deal with it as an Optional
guard let previewLayer = previewLayer else { return }
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect
previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer)
captureSession.startRunning()
}
}
}
} catch let error {
print("Error: \(error)")
}
Honestly some of this is a bit overkill, so it's down to a bit of personal preference, but with something like AVCaptureDevice.default... where it truly may or may not be nil depending on the actual device you probably don't want to let it crash if it doesn't exist.

Use of unresolved identifier when referencing variable in do/catch block

I’m assigning a variable in a do / catch block, and then trying to reference that variable further down in my file. But when I do, I get the following error in Xcode:
Use of unresolved identifier 'captureDeviceInput'
This is my code:
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice) as AVCaptureDeviceInput
} catch let error {
print("\(error)")
return
}
captureSession = AVCaptureSession()
captureSession?.addInput(input: captureDeviceInput as AVCaptureDeviceInput)
It seems Xcode’s not recognising the captureDeviceInput variable. What can I do to resolve this?
captureDeviceInput is declared locally that means it's visible only in the do scope.
It's good habit to put all good code also in the do scope.
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice) as AVCaptureDeviceInput
captureSession = AVCaptureSession()
captureSession?.addInput(input: captureDeviceInput as AVCaptureDeviceInput)
} catch {
print("\(error)")
return
}

use of unresolved identifier

The input has been be defined as try AVCaptureDeviceInput(device: captureDevice) but it still says input is an unresolved identifier.
Please see my code below, I have tried multiple methods but not success.
import UIKit
import AVFoundation
class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var qrCodeFrameView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
// Get an instance of AVCaptureDevice class to initialize a device object and provide the video as the media type parameter.
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
do {
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
let input = try AVCaptureDeviceInput(device: captureDevice)
// Do the rest of your work...
} catch let error as NSError {
// Handle any errors
print(error)
}
// Initialize the captureSession object
captureSession = AVCaptureSession()
captureSession?.addInput(input as! AVCaptureInput)
// Set the input device on the capture session.
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOuput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOuput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOuput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOuput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
// Start video capture
captureSession?.startRunning()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
How do I fix this?
As already explained in the other answers, your input
variable is limited to the scope of the do block.
An alternative solution – if you want to keep the do/catch blocks
smaller and localized – is to declare the variable outside of the block:
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
let input: AVCaptureDeviceInput
do {
input = try AVCaptureDeviceInput(device: captureDevice)
} catch let error as NSError {
print(error)
return // Must return from method here ...
}
// `input` is defined and initialized now ...
captureSession = AVCaptureSession()
captureSession?.addInput(input)
// ...
Note that this requires that your return immediately in the error
case, as input would be undefined then.
Or, if the error message is not important, use try? in a guard
statement:
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
guard let input = try? AVCaptureDeviceInput(device: captureDevice) else {
return
}
// `input` is defined and initialized now ...
captureSession = AVCaptureSession()
captureSession?.addInput(input)
// ...
It's a scope issue. Your captureDevice and input constants are only usable inside the do block. Update your code to something like this:
override func viewDidLoad() {
super.viewDidLoad()
// Get an instance of AVCaptureDevice class to initialize a device object and provide the video as the media type parameter.
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
do {
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
let input = try AVCaptureDeviceInput(device: captureDevice)
// Initialize the captureSession object
captureSession = AVCaptureSession()
captureSession?.addInput(input as! AVCaptureInput)
// Set the input device on the capture session.
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOuput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOuput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOuput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOuput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
// Start video capture
captureSession?.startRunning()
} catch let error as NSError {
// Handle any errors
print(error)
}
}
input is in the scope of the do block, it's not visible outside.
Basically it's a very bad idea to just print the error and continue as if nothing happened. Always put the entire good code into the do block:
do {
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
let input = try AVCaptureDeviceInput(device: captureDevice)
// Initialize the captureSession object
captureSession = AVCaptureSession()
captureSession?.addInput(input as! AVCaptureInput)
// Initialize a AVCaptureMetadataOutput object and set it as the output
...
// Start video capture
captureSession?.startRunning()
// Do the rest of your work...
} catch let error as NSError {
// Handle any errors
print(error)
}