Swift - 权限请求封装

注意到工程里面关于权限请求这块的逻辑没有很好地管理起来,一些是用 OC 的工具类,一些是用 Swift 代码在一些 ViewController 里面单独调用,不能复用,就想封装一个 Swift 版关于权限请求的工具类,虽然这个需求在 GitHub 上搜索也有很多现成的轮子,但使用起来感觉太重,而且有些久不维护,Swift 版本更新的又快,有些接口就不好使了,索性就自己封装一个。

一开始的思路也是想通过枚举来创建不同权限字段,通过静态方法来实现请求,但是蓝牙权限和定位权限需要实例化对象,设置代理。当使用 static 关键字来创建蓝牙和定位对象,当应用内有其他的业务也要使用蓝牙和定位对象时,代理响应就会有点问题。后面索性就用单例方法创建一个整体的权限管理对象,然后创建类方法,在类方法中自己调用自己。

过程中也参考了以下开源仓库的一些逻辑:

当前封装的库仍存在的一个问题是,暂时未做到对所有权限的海陆空全方位覆盖,只是针对当前我们应用业务所使用到的权限的封装。

好了,话不多说上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
//MARK: 权限状态枚举
public enum PermissionStatus: String {
case authorized = "Authorized"
case denied = "Denied"
case disabled = "Disabled"
case notDetermined = "Not Determined"

init?(string: String?) {
guard let string = string else { return nil }
self.init(rawValue: string)
}
}

extension PermissionStatus: CustomStringConvertible {
/// The textual representation of self.
public var description: String {
return rawValue
}
}

extension KKPermissions {
public typealias Callback = (PermissionStatus) -> Void
}

//MARK:权限请求方法
extension KKPermissions{
//MARK:蓝牙权限
public static func requestBluetooth(_ callback: @escaping Callback){
KKPermissions.provide.bluetoothManager = KKPermissions.provide.createBluetoothManager()

KKPermissions.provide.bluetoothCallBack = callback
KKPermissions.provide.bluetoothManager.startAdvertising(nil)
KKPermissions.provide.bluetoothManager.stopAdvertising()
}

//MARK:应用内定位权限
public static func requestLocationWhenInUse(_ callback: @escaping Callback){
KKPermissions.provide.LocationManager = KKPermissions.provide.createLocationManager()
KKPermissions.provide.locationCallBack = callback
KKPermissions.provide.LocationManager.requestWhenInUseAuthorization()
}

//MARK:持续使用定位权限
public static func requestLocationAlways(_ callback: @escaping Callback){
KKPermissions.provide.LocationManager = KKPermissions.provide.createLocationManager()
KKPermissions.provide.locationCallBack = callback
KKPermissions.provide.LocationManager.requestAlwaysAuthorization()
}

//MARK:麦克风权限
public static func requestMicrophone(_ callback: @escaping Callback){
let audioSession = AVAudioSession.sharedInstance()
audioSession.requestRecordPermission { (allowed) in
if allowed {
callback(.authorized)
}else{
callback(.denied)
}
}
}

//MARK:通讯录权限
public static func requestContacts(_ callback: @escaping Callback) {
//1.获取授权状态
//CNContactStore 通讯录对象
let status = CNContactStore.authorizationStatus(for: .contacts)

var phoneStatus: PermissionStatus

//2.判断如果是未决定状态,则请求授权
if status == .notDetermined {

//创建通讯录对象
let store = CNContactStore()

phoneStatus = .notDetermined
//请求授权
store.requestAccess(for: .contacts, completionHandler: {(isRight : Bool,error : Error?) in

if isRight {
print("授权成功")
phoneStatus = .authorized
} else {
phoneStatus = .denied
print("授权失败")
}

callback(phoneStatus)
})
}else if status != .authorized {
phoneStatus = .denied
callback(phoneStatus)
}else{
phoneStatus = .authorized
callback(phoneStatus)
}
}

//MARK: 相册权限
static func requestPhoto(_ callback: @escaping Callback) {
let granted = PHPhotoLibrary.authorizationStatus()
switch granted {
case PHAuthorizationStatus.authorized:
callback(.authorized)
case PHAuthorizationStatus.denied, PHAuthorizationStatus.restricted:
callback(.denied)
case PHAuthorizationStatus.notDetermined:
PHPhotoLibrary.requestAuthorization({ status in
DispatchQueue.main.async {
if status == .authorized{
callback(.authorized)
}else{
callback(.denied)
}
}
})
@unknown default:
callback(.denied)
}
}

//MARK: 相机权限
static func requestCamera(_ callback: @escaping Callback) {
let granted = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
switch granted {
case .authorized:
callback(.authorized)
case .denied,.restricted:
callback(.denied)
case .notDetermined:
AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (granted: Bool) in
DispatchQueue.main.async {
if granted {
callback(.authorized)
}else{
callback(.denied)
}
}
})
@unknown default:
callback(.denied)
}
}

//MARK: 跳转到APP系统设置权限界面
public static func jumpToSystemPrivacySetting() {
guard let appSetting = URL(string: UIApplication.openSettingsURLString) else {
return
}
if #available(iOS 10, *) {
UIApplication.shared.open(appSetting, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(appSetting)
}
}
}

class KKPermissions: NSObject {

private func createBluetoothManager() -> CBPeripheralManager{
let bluetoothManager = CBPeripheralManager(
delegate: KKPermissions.provide,
queue: nil,
options: nil)
return bluetoothManager
}

private func createLocationManager() -> CLLocationManager{
let locManager = CLLocationManager()
locManager.delegate = KKPermissions.provide
return locManager
}

private var LocationManager:CLLocationManager!

private static var locationState: PermissionStatus?

private var locationCallBack: Callback?

private var bluetoothManager: CBPeripheralManager!

private var bluetoothCallBack: Callback?

private static var bluetoothState: PermissionStatus?

private static let provide:KKPermissions = KKPermissions()

private override init() {
super.init()
}
}

//MARK: 定位/蓝牙 代理处理
extension KKPermissions: CLLocationManagerDelegate,CBPeripheralManagerDelegate{
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

guard KKPermissions.provide.LocationManager != nil else {
return
}

switch status {
case .authorizedAlways, .authorizedWhenInUse:
KKPermissions.locationState = .authorized
break
case .restricted, .denied:
KKPermissions.locationState = .denied
break
case .notDetermined:
KKPermissions.locationState = .notDetermined
break
@unknown default:
KKPermissions.locationState = .notDetermined
break
}

self.locationCallBack?(KKPermissions.locationState!)

self.locationCallBack = nil
}

func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
print(">>>>>权限状态测试 KKpermissions \(peripheral.state)")

guard KKPermissions.provide.bluetoothManager != nil else {
return
}

switch peripheral.state {
case .unsupported, .poweredOff:
KKPermissions.bluetoothState = .disabled
break
case .unauthorized:
KKPermissions.bluetoothState = .denied
break
case .poweredOn:
KKPermissions.bluetoothState = .authorized
break
case .resetting, .unknown:
KKPermissions.bluetoothState = .notDetermined
break
@unknown default:
KKPermissions.bluetoothState = .notDetermined
break
}

self.bluetoothCallBack?(KKPermissions.bluetoothState!)

//响应一次后不再持有
self.bluetoothCallBack = nil
}
}

附上一个简单的 UI 效果: