103

Trying to write this:

if usergavepermissiontousercamera  
  opencamera
else 
  showmycustompermissionview

Couldn't find a current way to do this simple task.
Note: Should also work iOS7 even if it requires a different method

Esqarrouth
  • 38,543
  • 21
  • 161
  • 168
  • You should check this link. http://stackoverflow.com/questions/25803217/presenting-camera-permission-dialog-in-ios-8 Should not be to hard to translate to Swift. – Mehdi.Sqalli Dec 25 '14 at 09:54
  • AVCaptureDevice and AVAuthorizationStatus are both not recognized by xcode maybe a bug or something but yeah this makes it pretty hard. – Esqarrouth Dec 25 '14 at 09:58
  • checked that answer. its for the camera roll, not the camera it self – Esqarrouth Dec 25 '14 at 10:13

6 Answers6

206

You can use the following code for doing the same:

if AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo) ==  AVAuthorizationStatus.Authorized {
    // Already Authorized
} else {
    AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { (granted: Bool) -> Void in
       if granted == true {
           // User granted
       } else {
           // User rejected
       }
   })
}

NOTE:

  1. Make sure that you add the AVFoundation Framework in the Link Binary section of build phases
  2. You should write import AVFoundation on your class for importing AVFoundation

SWIFT 3

if AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) ==  AVAuthorizationStatus.authorized {
   // Already Authorized
} else {
   AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (granted: Bool) -> Void in
      if granted == true {
         // User granted
      } else {
         // User Rejected
      }
   })
}

Swift 4

if AVCaptureDevice.authorizationStatus(for: .video) ==  .authorized {
    //already authorized
} else {
    AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in
        if granted {
            //access allowed
        } else {
            //access denied
        }
    })
}
LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174
Midhun MP
  • 103,496
  • 31
  • 153
  • 200
  • thanks. importing worked without adding to link binary. but i do have Foundation in the frameworks. is that related? – Esqarrouth Dec 25 '14 at 11:00
  • 1
    @Esq: You don't need to link binary because, according to Swift Imports Any Objective-C framework (or C library) that’s accessible as a module can be imported directly into Swift. This includes all of the Objective-C system frameworks—such as Foundation, UIKit, and SpriteKit—as well as common C libraries supplied with the system. Merry X'mas... – Midhun MP Dec 25 '14 at 11:12
  • thanks, merry xmas to you too :) btw in ios7 this method enters already authorized even though it never got the request. why is that and how can i fix it? – Esqarrouth Dec 25 '14 at 12:28
  • @Esq: If your app already had the permission and set in the settings app, then it won't ask again. Check your settings app whether you have the permission or not. – Midhun MP Dec 25 '14 at 22:44
  • i reset the simulator settings before trying this. went and checked the settings nothing about the app is in there. trying it on ios7.1 simulator – Esqarrouth Dec 26 '14 at 08:18
  • ok it seems like ios7 doesnt need camera permission at all lol – Esqarrouth Dec 26 '14 at 20:40
  • How to do the same for location? – Karanveer Singh May 31 '19 at 10:31
26

The following is a cleaned up answer updated for Swift 4.x:

Starting with iOS 10, you must also request permission in the info.plist file to avoid a crash:

enter image description here

Privacy - Camera Usage Description

You must provide a string that is presented to the user with this key. Failure to do so will result in a crash when attempting to access the camera.

import AVFoundation

func checkCameraAccess() {
    switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .denied:
        print("Denied, request permission from settings")
        presentCameraSettings()
    case .restricted:
        print("Restricted, device owner must approve")
    case .authorized:
        print("Authorized, proceed")
    case .notDetermined:
        AVCaptureDevice.requestAccess(for: .video) { success in
            if success {
                print("Permission granted, proceed")
            } else {
                print("Permission denied")
            }
        }
    }
}

func presentCameraSettings() {
    let alertController = UIAlertController(title: "Error",
                                  message: "Camera access is denied",
                                  preferredStyle: .alert)
    alertController.addAction(UIAlertAction(title: "Cancel", style: .default))
    alertController.addAction(UIAlertAction(title: "Settings", style: .cancel) { _ in
        if let url = URL(string: UIApplicationOpenSettingsURLString) {
            UIApplication.shared.open(url, options: [:], completionHandler: { _ in
                // Handle
            })
        }
    })

    present(alertController, animated: true)
}

This will test for the four possible answers, and then either request permission if it is notDetermined, or direct the user to settings to enable it if it is denied. If it is restricted, the current user may not be able to enable it, but you should provide some form of guidance to them.

CodeBender
  • 35,668
  • 12
  • 125
  • 132
20

Swift 3.0 Updated Solution

func callCamera(){
    let myPickerController = UIImagePickerController()
    myPickerController.delegate = self;
    myPickerController.sourceType = UIImagePickerControllerSourceType.camera

    self.present(myPickerController, animated: true, completion: nil)
    NSLog("Camera");
}
func checkCamera() {
    let authStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
    switch authStatus {
    case .authorized: callCamera() // Do your stuff here i.e. callCameraMethod()
    case .denied: alertPromptToAllowCameraAccessViaSetting()
    case .notDetermined: alertToEncourageCameraAccessInitially()
    default: alertToEncourageCameraAccessInitially()
    }
}

func alertToEncourageCameraAccessInitially() {
    let alert = UIAlertController(
        title: "IMPORTANT",
        message: "Camera access required for capturing photos!",
        preferredStyle: UIAlertControllerStyle.alert
    )
    alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
    alert.addAction(UIAlertAction(title: "Allow Camera", style: .cancel, handler: { (alert) -> Void in
        UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
    }))
    present(alert, animated: true, completion: nil)
}

func alertPromptToAllowCameraAccessViaSetting() {

    let alert = UIAlertController(
        title: "IMPORTANT",
        message: "Camera access required for capturing photos!",
        preferredStyle: UIAlertControllerStyle.alert
    )
    alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel) { alert in
        if AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo).count > 0 {
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { granted in
                DispatchQueue.main.async() {
                    self.checkCamera() } }
        }
        }
    )
    present(alert, animated: true, completion: nil)
}
mbonness
  • 1,612
  • 1
  • 18
  • 20
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78
  • 1
    I think you mixed names of methods alertToEncourageCameraAccessInitially and alertPromptToAllowCameraAccessViaSetting ;) – jonaszmclaren May 11 '17 at 11:56
10

This will open the camera when permission is given by the user. Otherwise show alert for asking permission.

func openCamera(){
        
        let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
        
        switch (authStatus){
            
        case .notDetermined, .restricted, .denied:
            showAlert(title: "Unable to access the Camera", message: "To enable access, go to Settings > Privacy > Camera and turn on Camera access for this app.")
        case .authorized:
            alert.dismiss(animated: true, completion: nil)
            if(UIImagePickerController .isSourceTypeAvailable(.camera)){
                picker.sourceType = .camera
                picker.showsCameraControls=true
                picker.allowsEditing=true
                self.viewController!.present(picker, animated: true, completion: nil)
            }
        }
}

after this call this function for showing alert

func showAlert(title:String, message:String) {
        let alert = UIAlertController(title: title,
                                      message: message,
                                      preferredStyle: UIAlertController.Style.alert)
        
        let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
        alert.addAction(okAction)
        
        let settingsAction = UIAlertAction(title: "Settings", style: .default, handler: { _ in
            // Take the user to Settings app to possibly change permission.
            guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else { return }
            if UIApplication.shared.canOpenURL(settingsUrl) {
                if #available(iOS 10.0, *) {
                    UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
                        // Finished opening URL
                    })
                } else {
                    // Fallback on earlier versions
                    UIApplication.shared.openURL(settingsUrl)
                }
            }
        })
        alert.addAction(settingsAction)
        
        self.viewController!.present(alert, animated: true, completion: nil)
    }
abhishekkharwar
  • 3,529
  • 2
  • 18
  • 31
Prashant Sharma
  • 1,357
  • 1
  • 21
  • 31
  • +1, as it Worked for me after a small change. All good except for the first time case .notDetermined will be true, so we can proceed to either doing what all we did in .authorised case, because its gonna ask for allow/deny or we can present user with "AVCaptureDevice.requestAccess(for: .video)". From next attempt switch case will follow based on first time response. Also until first time we deny/allow , we will not see this in privacy settings. – Lokesh Purohit Jun 20 '19 at 05:55
7

You can import the AVFoundation framework and use the authorizationStatus(for:) method shown below and handle the respective cases.

switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .authorized: // The user has previously granted access to the camera.
        self.setupCaptureSession()

    case .notDetermined: // The user has not yet been asked for camera access.
        AVCaptureDevice.requestAccess(for: .video) { granted in
            if granted {
                self.setupCaptureSession()
            }
        }

    case .denied: // The user has previously denied access.
        return
    case .restricted: // The user can't grant access due to restrictions.
        return
}
Anu Mittal
  • 181
  • 2
  • 9
2

I have modified the above answer and removed the initial prompt, since when we want to use device's camera the system is prompting for permissions itself:

func checkPermissions() {
    let authStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)

    switch authStatus {
    case .authorized:
        setupCamera()
    case .denied:
        alertPromptToAllowCameraAccessViaSetting()
    default:
        // Not determined fill fall here - after first use, when is't neither authorized, nor denied
        // we try to use camera, because system will ask itself for camera permissions
        setupCamera()
    }
}

func alertPromptToAllowCameraAccessViaSetting() {
    let alert = UIAlertController(title: "Error", message: "Camera access required to...", preferredStyle: UIAlertControllerStyle.alert)

    alert.addAction(UIAlertAction(title: "Cancel", style: .default))
    alert.addAction(UIAlertAction(title: "Settings", style: .cancel) { (alert) -> Void in
        UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
    })

    present(alert, animated: true)
}
jonaszmclaren
  • 2,459
  • 20
  • 30