iOS APP审核比较严格,十分注重保护用户隐私。没有用户对APP授权,很多涉及数据采集的功能都无法使用。这里记录一些常用的权限配置及权限状态检查,后面会陆续补充。
一、常见隐私权限
1.基本格式 Privacy - xxxx :详细描述,如图:
ps:详细描述要写的与具体功能相关,否则APP审核可能会被打回。
2.隐私权限清单
Privacy - Camera Usage Description : 使用相机(拍照或录制)
Privacy - Face ID Usage Description : 使用FaceID
Privacy - NFC Scan Usage Description : 使用NFC扫描
Privacy - Microphone Usage Description :使用麦克风(录音)
Privacy - Bluetooth Always Usage Description :使用手机蓝牙
//定位
Privacy - Location When In Use Usage Description : APP使用期间获取定位信息(仅限应用在前台)
Privacy - Location Always and When In Use Usage Description :允许一直获取定位信息(包括前台和后台)
//图库
Privacy - Photo Library Usage Description :允许从图库读取图片或视频
Privacy - Photo Library Additions Usage Description :向图库添加图片或视频
二、检查权限状态,及主动申请
这一部分是最基本的容错,调用方法前去获取设备硬件状态或权限是否授权。进而去规避一些由于用户没有授权权限,直接调用对应方法而导致的崩溃。
1.相机
这里用到的是AVCaptureDevice来检查权限状态,后续会添加使用其他方法状态获取的方式。
private func isPrepareOfAVCaptureVideoDevice(_ completionHandler: @escaping (Bool) -> Void) {
let videoStatus = AVCaptureDevice.authorizationStatus(for: .video)
if videoStatus == .notDetermined{
AVCaptureDevice.requestAccess(for: .video) {
completionHandler($0)
}
return
}
completionHandler(videoStatus == .authorized)
}
2.麦克风
同相机获取权限状态的方式,后续也会添加。
private func isPrepareOfAVCaptureAudioDevice(_ completionHandler: @escaping (Bool) -> Void) {
let audioStatus = AVCaptureDevice.authorizationStatus(for: .audio)
if audioStatus == .notDetermined {
AVCaptureDevice.requestAccess(for: .audio) {
completionHandler($0)
}
return
}
completionHandler(audioStatus == .authorized)
}
3.图库
PhotoLibrary权限在iOS14.0进行了细化:
a)获取图库读写权限
if #available(iOS 14, *) {
let readWriteStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite)
print(readWriteStatus)
PHPhotoLibrary.requestAuthorization(for: .readWrite) { (status) in
self.handleRequestStatus(status: status)
}
} else {
let readWriteStatus = PHPhotoLibrary.authorizationStatus()
print(readWriteStatus)
PHPhotoLibrary.requestAuthorization { (status) in
self.handleRequestStatus(status: status)
}
}
}
func handleRequestStatus(status:PHAuthorizationStatus){
switch status {
case .notDetermined:
print("The user hasn't determined this app's access.")
case .restricted:
print("The system restricted this app's access.")
case .denied:
print("The user explicitly denied this app's access.")
case .authorized:
print("The user authorized this app to access Photos data.")
case .limited:
print("The user authorized this app for limited Photos access.")
@unknown default:
fatalError()
}
}
b)仅向图库添加,为了对称添加了iOS14版本以下的
func checkPhotosLibraryAddtionStatus(){
if #available(iOS 14, *) {
let readWriteStatus = PHPhotoLibrary.authorizationStatus(for: .addOnly)
print(readWriteStatus)
PHPhotoLibrary.requestAuthorization(for: .addOnly) { [self] (status) in
handleRequestStatus(status: status)
}
} else {
let readWriteStatus = PHPhotoLibrary.authorizationStatus()
print(readWriteStatus)
PHPhotoLibrary.requestAuthorization { (status) in
self.handleRequestStatus(status: status)
}
}
}
func handleRequestStatus(status:PHAuthorizationStatus){
switch status {
case .notDetermined:
print("The user hasn't determined this app's access.")
case .restricted:
print("The system restricted this app's access.")
case .denied:
print("The user explicitly denied this app's access.")
case .authorized:
print("The user authorized this app to access Photos data.")
case .limited:
print("The user authorized this app for limited Photos access.")
@unknown default:
fatalError()
}
}
4.蓝牙
//蓝牙状态获取是通过delegate方法centralManagerDidUpdateState来实现的
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .resetting:
print("服务连接中断,正在重连")
case .unsupported:
print("设备不支持")
case .unauthorized:
if #available(iOS 13.0, *) {
switch central.authorization {
case .restricted:
print("设备蓝牙有问题,被限制使用")
case .denied:
print("被用户拒绝了")
default:
print("啥也不是~~~~~")
}
} else {
print("用户未授权")
}
case .poweredOff:
print("蓝牙未打开")
case .poweredOn:
print("蓝牙已打开")
default:
print("啥也不是~~~~~")
}
5.定位
func checkLocationStatus(){
let locationManager = CLLocationManager()
if #available(iOS 14.0, *) {
let status = locationManager.authorizationStatus
self.requestLocationAuthor(status: status)
} else {
let status = CLLocationManager.authorizationStatus()
self.requestLocationAuthor(status: status)
}
}
func requestLocationAuthor(status:CLAuthorizationStatus){
let locationManager = CLLocationManager()
switch status {
case .authorizedAlways:
print("The user authorized this app is authorizedAlways.")
case .authorizedWhenInUse:
print("The user authorized this app is authorizedWhenInUse.")
case .denied:
print("The user authorized this app is denied.")
case .notDetermined://根据需求二选一
// locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
case .restricted:
print("The user authorized this app is restricted.")
default:
fatalError()
}
}
6.NFC
要使用NFC功能开发需要在证书中添加NFC开发选项,配置证书后然后在项目中添加Capability。
获取状态是否可用比较简单,内部封装好的api直接调用即可。
//MARK: NFC相关
import CoreNFC
extension ViewController{
func checkNFCReaderAvailable() -> Bool{
return NFCNDEFReaderSession.readingAvailable
}
}
7.FaceID
请求设备本地解锁认证(密码,faceID,TouchID),LAPolicy有两种枚举类型,deviceOwnerAuthenticationWithBiometrics仅调用设备设置的touchid或faceID进行认证,deviceOwnerAuthentication会根据设备设置的解锁方式进行请求,默认是设备密码认证,即使在没有设置生物学识别信息(指纹、面部特征)的情况下也可以使用
import LocalAuthentication
var context = LAContext()
extension ViewController{
func requetAuthor(){
/*
canEvaluatePolicy()方法用来检查认证方式是否可用,也可用来标记状态
evaluatePolicy()方法用来进行认证方法调用
*/
context = LAContext()
context.localizedCancelTitle = "设置title"
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "设置具体描述"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason ) { success, error in
if success {
DispatchQueue.main.async { [unowned self] in
print("认证成功")
}
} else {
print(error?.localizedDescription ?? "认证失败")
}
}
} else {
print(error?.localizedDescription ?? "无法调用系统认证方式")
}
}
}
8.消息通知
检查消息通知状态
func checkNotificationAvailable(){
if #available(iOS 10, *) {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
print(settings.authorizationStatus)
switch settings.authorizationStatus{
case .authorized:
print("已授权")
case .denied:
print("用户拒绝消息通知")
case .notDetermined:
print("未确定消息权限")
//这两个是新的,没有做深入探究
case .ephemeral:
print("ephemeral")
case .provisional:
print("provisional")
default:
break
}
}
}else{
let isRegistered = UIApplication.shared.isRegisteredForRemoteNotifications
print(isRegistered)
}
}