swift小知识之常用权限检测
最近在Swift项目实践中,用到了系统的一些权限请求,如定位、相机、相册、日历.....,为了更方便的查询各种权限状态以及使用这些功能,我简单的整理了一些常用的系统权限的状态以及授权。
特别说明:在使用这些功能时,需要在info.plist中添加对应的描述,否则将导致项目崩溃,在添加定位权限描述以及相册权限描述时,需要添加以下描述,否则可能会导致包上传到itunesConnect时找不到包,当然,苹果也会在你包上传之后发邮件提醒你
* Privacy - Photo Library Additions Usage Description
* Privacy - Photo Library Usage Description
* Privacy - Location When In Use Usage Description
* Privacy - Location Always and When In Use Usage Description
1.导入头文件、自定义权限的各种状态,方便各种的权限状态的统一处理
import CoreLocation import ContactsUI import PhotosUI import AssetsLibrary import EventKitUI import CoreTelephony import AVFoundation //回调处理 typealias BWPrivacyAuthorizerCompletionClosure = (_ granted: Bool)->Void enum BWPrivacyAuthorizerStatus { case notDetermined //尚未授权 case restricted //家长控制 case denied //拒绝 case authorized //已授权 }
2.常用权限状态,将各种权限状态转化成统一的自定义的权限状态,方便统一处理
// 1.定位授权状态 func bw_locationAuthorizationStatus() -> BWPrivacyAuthorizerStatus { let status = CLLocationManager.authorizationStatus() switch status { case .denied: return .denied case .notDetermined: return .notDetermined case .restricted: return .restricted case .authorized: return .authorized default: return .authorized } } // 2.通讯录授权状态 func bw_contactAuthorizationStatus() -> BWPrivacyAuthorizerStatus { let status = CNContactStore.authorizationStatus(for: .contacts) switch status { case .notDetermined: return .notDetermined case .restricted: return .restricted case .denied: return .denied case .authorized: return .authorized default: return .authorized } } // 3.相册授权状态 func bw_photoLibraryAuthorizationStatus() -> BWPrivacyAuthorizerStatus { if #available(iOS 9.0, *) { let status = PHPhotoLibrary.authorizationStatus() switch status { case .notDetermined: return .notDetermined case .restricted: return .restricted case .denied: return .denied default: return .authorized } } else { let status = ALAssetsLibrary.authorizationStatus() switch status { case .notDetermined: return .notDetermined case .restricted: return .restricted case .denied: return .denied default: return .authorized } } } // 4.相机授权状态 func bw_cameraAuthorizationStatus() -> BWPrivacyAuthorizerStatus { let status = AVCaptureDevice.authorizationStatus(for: AVMediaType.video) switch status { case .notDetermined: return .notDetermined case .restricted: return .restricted case .denied: return .denied case .authorized: return .authorized default: return .authorized } } // 5.日历授权状态 func bw_calendarAuthorizationStatus() -> BWPrivacyAuthorizerStatus { let status = EKEventStore.authorizationStatus(for: EKEntityType.event) switch status { case .notDetermined: return .notDetermined case .restricted: return .restricted case .denied: return .denied case .authorized: return .authorized default: return .authorized } } // 6.麦克风权限 func bw_audioAuthorizationStatus() -> BWPrivacyAuthorizerStatus { let status = AVAudioSession.sharedInstance().recordPermission switch status { case .undetermined: return .notDetermined case .denied: return .denied case .granted: return .authorized } }
3.请求授权
//MARK: 通讯录相关权限 func bw_requestContactAuthorization(with completion: @escaping BWPrivacyAuthorizerCompletionClosure) { let status = bw_contactAuthorizationStatus() switch status { case .notDetermined: let store = CNContactStore.init() store.requestAccess(for: .contacts) { (granted, error) in DispatchQueue.main.async { if granted == false { bw_showAlertWithTitle("无法访问通讯录", message: bw_authorizationNotice(title: "通讯录")) } completion(granted) } } case .restricted,.denied: completion(false) bw_showAlertWithTitle("无法访问通讯录", message: bw_authorizationNotice(title: "通讯录")) case .authorized: completion(true) } } //MARK: 相册相关权限 func bw_requestPhotoLibraryAuthorization(with completion: @escaping BWPrivacyAuthorizerCompletionClosure) { let status = bw_photoLibraryAuthorizationStatus() switch status { case .notDetermined: if #available(iOS 9.0, *) { PHPhotoLibrary.requestAuthorization { (status) in DispatchQueue.main.async { switch status { case .authorized: completion(true) default: completion(false) bw_showAlertWithTitle("无法访问照片", message: bw_authorizationNotice(title: "照片")) } } } } case .restricted,.denied: bw_showAlertWithTitle("无法访问照片", message: bw_authorizationNotice(title: "照片")) case .authorized: completion(true) } } //MARK: 相机相关权限 func bw_requestCameraAuthorization(with completion: @escaping BWPrivacyAuthorizerCompletionClosure) { let status = bw_cameraAuthorizationStatus() switch status { case .notDetermined: AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in DispatchQueue.main.async { if granted == false { bw_showAlertWithTitle("无法访问相机", message: bw_authorizationNotice(title: "相机")) } completion(granted) } }) case .restricted,.denied: bw_showAlertWithTitle("无法访问相机", message: bw_authorizationNotice(title: "相机")) case .authorized: completion(true) } } //MARK: 日历相关权限 func bw_requestCalendarAuthorization(with completion: @escaping BWPrivacyAuthorizerCompletionClosure) { let status = bw_calendarAuthorizationStatus() switch status { case .notDetermined: let store = EKEventStore.init() store.requestAccess(to: .event) {(granted, error) in DispatchQueue.main.async { if granted == false { bw_showAlertWithTitle("无法访问日历", message: bw_authorizationNotice(title: "日历")) } completion(granted) } } case .authorized: completion(true) case .restricted,.denied: bw_showAlertWithTitle("无法访问日历", message: bw_authorizationNotice(title: "日历")) } } //MARK:定位相关权限 func bw_requestLocationAuthorization(with locationManager: CLLocationManager, completion: @escaping BWPrivacyAuthorizerCompletionClosure) { let status = bw_locationAuthorizationStatus() switch status { case .denied,.restricted: completion(false) bw_showAlertWithTitle("无法开启定位", message: "请在iPhone的\"设置-隐私-位置\"中允许\(BWAppDispalyName)开启位置权限") case .notDetermined: locationManager.requestWhenInUseAuthorization() // locationManager.startUpdatingLocation() case .authorized: completion(true) } } //MARK: 麦克风相关权限 func bw_requestAudioAuthorization(with completion: @escaping BWPrivacyAuthorizerCompletionClosure) { let status = bw_audioAuthorizationStatus() switch status { case .notDetermined: AVAudioSession.sharedInstance().requestRecordPermission { (granted) in DispatchQueue.main.async { if granted == false { bw_showAlertWithTitle("无法访问麦克风", message: bw_authorizationNotice(title: "麦克风")) } completion(granted) } } case .denied: completion(true) bw_showAlertWithTitle("无法访问麦克风", message: bw_authorizationNotice(title: "麦克风")) case .authorized: completion(true) default: break } } //MARK: 通知相关权限 func bw_notificationAuthorizationStatus(with completion: @escaping BWPrivacyAuthorizerCompletionClosure) { if #available(iOS 10.0, *) { UNUserNotificationCenter.current().getNotificationSettings { (settings) in switch settings.authorizationStatus { case .authorized,.provisional: completion(true) case .notDetermined: completion(false) case .denied: completion(false) @unknown default: completion(false) } } } else { let isNotificationEnabled = UIApplication.shared.currentUserNotificationSettings?.types.contains(UIUserNotificationType.alert) if isNotificationEnabled == true { completion(true) } else { completion(false) } } } //MARK: 弹窗展示 private func bw_authorizationNotice(title: String) -> String { return "请在iPhone的\"设置-隐私-\(title)\"中允许\(BWAppDispalyName)访问\(title)" } private func bw_showAlertWithTitle(_ title: String, message: String) { let alertController = UIAlertController.init(title: title, message: message, preferredStyle: .alert) let cancelAction = UIAlertAction.init(title: "取消", style: .cancel, handler: nil) let goAction = UIAlertAction.init(title: "前往设置", style: .default) { (action) in if let settingUrl = URL(string: UIApplication.openSettingsURLString) { if UIApplication.shared.canOpenURL(settingUrl) { UIApplication.shared.openURL(settingUrl) } } } alertController.addAction(cancelAction) alertController.addAction(goAction) UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil) }