权限类目
- 照片
- 相机
- 麦克风
- 定位服务
- 通讯录
- 运动与健身
- Siri服务
- 消息推送
- 健康
苹果一直以来对用户的隐私极其注重,因此权限这块也是非常严谨,几乎每一项权限都需要在项目里的plist文件新增对应的权限key,否则审核会直接打回(二进制文件无效等)。
这里采用对所有权限统一管理等方式,按项目需要在适当的位置向用户申请授权权限。首先需要定义一个具体权限的接口协议类BKPermissionInterface,协议内容为:
// MARK: - 权限协议
protocol BKPermissionInterface {
/// 是否允许
var isAuthorized: Bool { get }
/// 是否拒绝
var isDenied: Bool { get }
/// 是否每次询问
var isNotDetermined: Bool { get }
/// 请求权限
func request(completion callback: ((BKAuthorizationStatus) -> Void)?)
}
其中协议里的request方法里的回调枚举为:
enum BKAuthorizationStatus {
// 未知状态
case unknown
// 用户未选择
case notDetermined
// 用户没有权限
case restricted
// 拒绝
case denied
// 允许
case authorized
// 临时允许
case provisional
// 设备不支持
case notSupport
/// 是否可以访问
var isAuthorized: Bool {
return self == .authorized || self == .provisional
}
/// 是否不支持
var isNotSupport: Bool {
return self == .notSupport
}
}
定义完协议及枚举,接下来就是将具体的权限一一遵循该协议即可。
定位服务
定义定位服务权限结构体,并遵循权限协议,实现协议属性及方法,fullAccuracy属性是为适配iOS14推出的精确定位和模糊定位,可以拿到用户选择的定位精度。
/// 全局变量定位授权才能正常弹窗
fileprivate var _locationManager = CLLocationManager()
// MARK: - 定位
/// 定位权限
struct BKLocationPermission: BKPermissionInterface {
var isAuthorized: Bool {
if #available(iOS 14.0, *) {
return _locationManager.authorizationStatus == .authorizedWhenInUse || _locationManager.authorizationStatus == .authorizedAlways
} else {
return CLLocationManager.authorizationStatus() == .authorizedWhenInUse || CLLocationManager.authorizationStatus() == .authorizedAlways
}
}
var isDenied: Bool {
if #available(iOS 14.0, *) {
return _locationManager.authorizationStatus == .denied
} else {
return CLLocationManager.authorizationStatus() == .denied
}
}
var isNotDetermined: Bool {
if #available(iOS 14.0, *) {
return _locationManager.authorizationStatus == .notDetermined
} else {
return CLLocationManager.authorizationStatus() == .notDetermined
}
}
@available(iOS 14.0, *)
var fullAccuracy: Bool {
return _locationManager.accuracyAuthorization == .fullAccuracy
}
func request(completion callback: ((BKAuthorizationStatus) -> Void)?) {
DispatchQueue.global().async {
if CLLocationManager.locationServicesEnabled() {
var authStatue: BKAuthorizationStatus = .unknown
if #available(iOS 14.0, *) {
switch _locationManager.authorizationStatus {
case .authorizedWhenInUse, .authorizedAlways: authStatue = .authorized
case .notDetermined: authStatue = .notDetermined
case .restricted: authStatue = .restricted
case .denied: authStatue = .denied
default: authStatue = .unknown
}
} else {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse, .authorizedAlways: authStatue = .authorized
case .notDetermined: authStatue = .notDetermined
case .restricted: authStatue = .restricted
case .denied: authStatue = .denied
default: authStatue = .unknown
}
}
if authStatue == .notDetermined {
_locationManager.requestWhenInUseAuthorization()
}
callback?(authStatue)
} else {
callback?(.notSupport)
}
}
}
}
健康
定义健康权限结构体,并遵循权限协议,实现协议属性及方法
// MARK: - 健康
/// 健康权限
struct BKHealthKitPermission: BKPermissionInterface {
private let healthStore = HKHealthStore()
private func getHealthKitAuthStatus() -> HKAuthorizationStatus {
return healthStore.authorizationStatus(for: .workoutType)
}
var isAuthorized: Bool {
return self.getHealthKitAuthStatus() == .sharingAuthorized
}
var isDenied: Bool {
return self.getHealthKitAuthStatus() == .sharingDenied
}
var isNotDetermined: Bool {
return self.getHealthKitAuthStatus() == .notDetermined
}
func request(completion callback: ((BKAuthorizationStatus) -> Void)?) {
if HKHealthStore.isHealthDataAvailable() {
var authStatue: BKAuthorizationStatus = .unknown
switch self.getHealthKitAuthStatus() {
case .sharingAuthorized: authStatue = .authorized
case .notDetermined: authStatue = .notDetermined
case .sharingDenied: authStatue = .denied
default: authStatue = .unknown
}
if authStatue == .notDetermined {
healthStore.requestAuthorization(toShare: HKT.dataTypesToWrite(), read: HKT.dataTypesToRead()) { authorized, error in
callback?(authorized ? .authorized : .denied)
}
} else {
callback?(authStatue)
}
} else {
callback?(.notSupport)
}
}
}
调用权限申请
有了先前的权限协议接口,现在就可以定义权限管理类BKPermission,内部则使用枚举来一一拿到对应的权限结构体即可。
class BKPermission: NSObject {
enum BKPermissionType: String {
case photoLibrary = "照片"
case camera = "相机"
case microphone = "麦克风"
case location = "定位服务"
case contacts = "通讯录权限"
case cmMotion = "运动与健身"
case siri = "Siri服务"
case userNotifi = "消息推送"
case health = "健康"
var msg: String {
switch self {
case .photoLibrary: return "请前往「设置—隐私—照片」中打开开关。"
case .camera: return "请前往「设置—隐私—相机」中打开开关。"
case .microphone: return "请前往「设置—隐私—麦克风」中打开开关。"
case .location: return "想要更准确地记录你的运动记录。点击“设置”,开启定位服务。"
case .contacts: return "请前往「设置—隐私—通讯录」中打开开关。"
case .cmMotion: return "想要获取步数等数据。点击“设置”,开启运动与健身。"
case .siri: return "想要通过Siri控制跑步。点击“设置”,开启Siri。"
case .userNotifi: return "想要及时获取消息。点击“设置”,开启通知。"
case .health: return "想要同步健康中体能训练等数据。请前往「设置—隐私—健康」中打开开关。"
}
}
}
/// 是否允许权限
static func isAllowed(_ type: BKPermissionType) -> Bool {
let manager = self.getManagerForPermission(type)
return manager.isAuthorized
}
/// 是否拒绝权限
static func isDenied(_ type: BKPermissionType) -> Bool {
let manager = self.getManagerForPermission(type)
return manager.isDenied
}
/// 是否是【下次询问或在我共享时】【允许一次】【每次询问】
static func isNotDetermined(_ type: BKPermissionType) -> Bool {
let manager = self.getManagerForPermission(type)
return manager.isNotDetermined
}
/// 定位权限的精度是否是精确位置
@available(iOS 14.0, *)
static func isFullAccuracy() -> Bool {
let manager = self.getManagerForPermission(.location) as! BKLocationPermission
return manager.fullAccuracy
}
/// 请求权限
static func request(_ type: BKPermissionType, completion callback: ((BKAuthorizationStatus) -> Void)? = nil) {
let manager = self.getManagerForPermission(type)
manager.request { status in
DispatchQueue.main.async {
if status.isNotSupport {
BPM.showAlert(.warning, msg: "当前设备不支持\(type.rawValue)!")
}
callback?(status)
}
}
}
static func requests() {
self.request(.location)
self.request(.cmMotion)
self.request(.siri)
}
}
// MARK: - Private
extension BKPermission {
private static func getManagerForPermission(_ type: BKPermissionType) -> BKPermissionInterface {
switch type {
case .photoLibrary: return BKPhotoLibraryPermission()
case .camera: return BKCameraPermission()
case .microphone: return BKMicrophonePermission()
case .location: return BKLocationPermission()
case .contacts: return BKContactsPermission()
case .cmMotion: return BKCMMotionPermission()
case .siri: return BKSiriPermission()
case .userNotifi: return BKUserNotifiPermission()
case .health: return BKHealthKitPermission()
}
}
}
这样后续管理新增的权限,只需要新增对应的权限结构体并遵循权限协议即可,实现规范化统一管理。
联系方式:kim77895pl@gmail.com
作者Kim,Hope it helps for you。2022.9.27