I'm rebuilding the Google Mobile Vision "Googly Eyes" demo in Swift 3. I figured almost all of it out, but I'm stuck on translating a function from Objective C to Swift.
The Objective C function in the demo view controller is:
- (AVCaptureDeviceInput *)cameraForPosition:(AVCaptureDevicePosition)desiredPosition {
BOOL hadError = NO;
for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
if ([device position] == desiredPosition) {
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device
error:&error];
if (error) {
hadError = YES;
NSLog(@"Could not initialize for AVMediaTypeVideo for device %@", device);
} else if ([self.session canAddInput:input]) {
return input;
}
}
}
if (!hadError) {
NSLog(@"No camera found for requested orientation");
}
return nil;
}
I've translated that into the following:
func camera(for desiredPosition: AVCaptureDevicePosition) -> AVCaptureDeviceInput {
var hadError: Bool = false
for device: AVCaptureDevice in AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) { // ERROR ON THIS LINE
if device.position() == desiredPosition {
var error: Error? = nil
let input = try? AVCaptureDeviceInput(device: device)
if error != nil {
hadError = true
print("Could not initialize for AVMediaTypeVideo for device \(device)")
}
else if session.canAdd(input!) {
return input!
}
}
}
if !hadError {
print("No camera found for requested orientation")
}
}
The error I'm getting is on the 3rd line (for device: AVCaptureDevice in AVCaptureDevice.devices...
). The error is: 'Any' is not convertible to 'AVCaptureDevice'
. I'm not very familiar with Objective C and have never used AVCaptureSession before so I've been struggling to figure it out. Any suggestions on how I need to rewrite this "for device" statement?
Assuming you use the latest release version of Xcode. It's Xcode 8.3.3 at the time of this post.
I show you codes for some simplified cases.
Continue using deprecated method (not recommended):
func camera(for desiredPosition: AVCaptureDevicePosition) -> AVCaptureDeviceInput? {
var hadError: Bool = false
for device in AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as! [AVCaptureDevice] { //### You need to cast
if device.position == desiredPosition {
do {
let input = try AVCaptureDeviceInput(device: device)
if session.canAddInput(input) { //###<-
return input
}
} catch {
hadError = true
print("Could not initialize for AVMediaTypeVideo for device \(device) with \(error)")
}
}
}
if !hadError {
print("No camera found for requested orientation")
}
return nil
}
The return type of devices(withMediaType:)
is [Any]!
, so if you want to use each element as AVCaptureDevice
, you need to cast it.
Similar to your code, but I fixed some parts, canAdd
to canAddInput
and the error handling.
Use AVCaptureDeviceDiscoverySession
ignoring older versions of iOS than 10.0.
func camera(for desiredPosition: AVCaptureDevicePosition) -> AVCaptureDeviceInput? {
var hadError: Bool = false
for device in AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: desiredPosition).devices {
do {
let input = try AVCaptureDeviceInput(device: device)
if session.canAddInput(input) {
return input
}
} catch {
hadError = true
print("Could not initialize for AVMediaTypeVideo for device \(device) with \(error)")
}
}
if !hadError {
print("No camera found for requested orientation")
}
return nil
}
A simple example of using AVCaptureDeviceDiscoverySession
, .builtInWideAngleCamera
matches for front camera, normal back camera and wide angle camera on dual camera device.
NOTE
Many methods in above codes have changed their signatures in Swift 4/Xcode 9, including the return type. And in iOS SDK 11, there's a nice method: