AVCaptureDevice.requestAccess presents unexpected

2019-08-21 12:50发布

问题:

Working with Xcode 10.1 and Swift 4.2

I have a complex app that uses a UINavigationController implemented in the AppDelegate.

The rootViewController of the navigationController is a DashboardController() class (subclass of UIViewController)

The DashboardController implements a left menu drawer using several ViewControllers (with self.addChild(viewController))

Everything works fine, except when I need to push a viewController to present a BarCodeScannerView().

The barebone barCodeScannerView can be pushed and popped as expected.

The problems arises when I request access to the camera (only the first time).

  1. As soon as I present the Device.requestAccess(for:) as follow: the viewController is popped and the previous view (rootViewController) is presented. (Still with the "App would like to access the camera" AlertView)

    func requestCameraAccess() { AVCaptureDevice.requestAccess(for: AVMediaType.video) { granted in if granted { self.launchScanner() } else { self.goBack() } } }

  2. If I click "OK" The system will register that the access was granted, but the applicationDidBecomeActive (in the AppDelegate) is called after aprox 1 second. I have some initializers in applicationDidBecomeActive, and they all are executed again. And after a quick delay, everything works fine.

BTW: applicationWillResignActive, applicationDidEnterBackground and applicationWillEnterForeground are NOT called. So it is clear that this is not part of an App LifeCycle.

Any idea what might me going on here? What can make the system call applicationDidBecomeActive within the app? and still keep everything running?

Thx in advance...

UPDATE After reading the comments, I was able to isolate the issue #2 as follows:

A simple/barebones project with a UINavigationController with a dashboardViewController as rootViewController. The dashboardViewController pushes a CameraViewController() in viewDidLoad(). The cameraViewController requests access to the camera. When clicking OK, the call to applicationDidBecomeActive is triggered.

The full project is attached. (except the "Privacy - Camera Usage Description" key in the .plist.

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow? = UIWindow()
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let dashboardViewController = DashboardViewController()
        window?.rootViewController = UINavigationController(rootViewController: dashboardViewController)
        window?.makeKeyAndVisible()
        return true
    }
    func applicationDidBecomeActive(_ application: UIApplication) {
        print("applicationDidBecomeActive")
    }
    func applicationWillResignActive(_ application: UIApplication) {}
    func applicationDidEnterBackground(_ application: UIApplication) {}
    func applicationWillEnterForeground(_ application: UIApplication) {}
    func applicationWillTerminate(_ application: UIApplication) {}
}
class DashboardViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        let cameraVC = CameraViewController()
        self.navigationController?.pushViewController(cameraVC, animated: true)
    }
}
import AVFoundation
class CameraViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        AVCaptureDevice.requestAccess(for: AVMediaType.video) { granted in
            if granted {
                print("Access granted")
            }
        }
    }   
}

回答1:

I'd say the problem is just with your testing procedure. When I run your code with a print statement in applicationWillResignActive, this is what I see:

applicationDidBecomeActive
applicationWillResignActive
Access granted
applicationDidBecomeActive

That seems completely in order and normal. It would have been weird to get a spurious didBecomeActive, but that is not what's happening; we resign active and then become active again, which is fine. You should expect that at any time your app can resign active and become active again. Many things in the normal lifecycle can cause that, and the presentation of an out-of-process dialog like the authorization dialog can reasonably be one of them. You should write your code in such a way as to cope with that possibility.