如何优雅地处理 AVCaptureDeviceInput.init 中的拒绝授权?

How can I gracefully handle denied authorization in AVCaptureDeviceInput.init?

The documentation for AVCaptureDeviceInput.init(device:) 将其参数记录为:

device The device from which to capture input.

outError If an error occurs during initialization, upon return contains an NSError object describing the problem.

outError 输出参数在 Swift 中表示为抛出的 Error。我可以像这样捕捉和显示它:

do {
    let deviceInput = try AVCaptureDeviceInput(device: device)
    // ...
}
catch {
    print("Error: \(error)")
}

我想优雅地处理一种特殊情况:当用户拒绝授权应用程序使用相机时。在这种情况下,我得到以下输出:

Error: Error Domain=AVFoundationErrorDomain Code=-11852 "Cannot use FaceTime HD Camera (Built-in)" UserInfo={NSLocalizedFailureReason=This app is not authorized to use FaceTime HD Camera (Built-in)., AVErrorDeviceKey=<AVCaptureDALDevice: 0x100520a60 [FaceTime HD Camera (Built-in)][0x8020000005ac8514]>, NSLocalizedDescription=Cannot use FaceTime HD Camera (Built-in)}

我需要将此错误类型与其他意外错误区分开来,例如:

do {
    let deviceInput = try AVCaptureDeviceInput(device: device)
    // ...
}
catch AVError.Code.applicationIsNotAuthorizedToUseDevice {
  // Expected error, handle gracefully
  errorMessageBox(errorText: "You have denied authorization to access your camera. Fix this in System Preferences > Security & Privacy.")
}
catch {
  // Unexpected errors
  errorMessageBox("Error: \(error)")
}

这是伪代码,无法编译。我知道错误代码 -11852AVError.Code.applicationIsNotAuthorizedToUseDevice。但是,我不知道如何从不透明的 error 对象中获取错误代码以便对其进行测试。

AVCaptureDeviceInput.init(device:)抛出的具体错误类型是什么?如何从中提取 AVError.Code 以处理此特定错误?

有两种可能的方法。一种是在尝试之前进行检查,例如

if AVCaptureDevice.authorizationStatus(for: .video) == .denied {
    offerToOpenSettings()
    return
}

另一种方法是catch未授权错误:

let input: AVCaptureDeviceInput
do {
    input = try AVCaptureDeviceInput(device: camera)
} catch AVError.applicationIsNotAuthorizedToUseDevice {
    offerToOpenSettings()
    return
} catch {
    print("some other error", error)
    return
}

注意,这是捕捉 AVError.applicationIsNotAuthorizedToUseDevice,而不是 AVError.Code.applicationIsNotAuthorizedToUseDevice

例如,如果这是一个 iOS 应用程序,您可以提供将用户重定向到设置应用程序的功能:

func offerToOpenSettings() {
    guard
        let settings = URL(string: UIApplication.openSettingsURLString),
        UIApplication.shared.canOpenURL(settings)
    else { return }

    let alert = UIAlertController(title: nil, message: "Would you like to open Settings to enable permission to use the camera?", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "Settings", style: .default) { _ in
        UIApplication.shared.open(settings)
    })
    alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))

    present(alert, animated: true)
}

请注意,由于这可能会发出警报,因此您不想在 viewDidLoad 中触发它(这在过程中还为时过早),而是 viewDidAppear.

或者,在 macOS 上,可能是这样的:

func offerToOpenSettings() {
    let preferences = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Camera")!
    let alert = NSAlert()
    alert.messageText = #"The camera is disabled. Please go to the “Camera” section in Security System Preferences, and enable this app."#
    alert.addButton(withTitle: "System Preferences")
    alert.addButton(withTitle: "Cancel")
    if alert.runModal() == .alertFirstButtonReturn {
        NSWorkspace.shared.open(preferences)
    }
}