注意到工程里面关于权限请求这块的逻辑没有很好地管理起来,一些是用 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 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 { public var description: String { return rawValue } } extension KKPermissions { public typealias Callback = (PermissionStatus ) -> Void } extension KKPermissions { 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() } public static func requestLocationWhenInUse (_ callback : @escaping Callback ){ KKPermissions .provide.LocationManager = KKPermissions .provide.createLocationManager() KKPermissions .provide.locationCallBack = callback KKPermissions .provide.LocationManager .requestWhenInUseAuthorization() } public static func requestLocationAlways (_ callback : @escaping Callback ){ KKPermissions .provide.LocationManager = KKPermissions .provide.createLocationManager() KKPermissions .provide.locationCallBack = callback KKPermissions .provide.LocationManager .requestAlwaysAuthorization() } public static func requestMicrophone (_ callback : @escaping Callback ){ let audioSession = AVAudioSession .sharedInstance() audioSession.requestRecordPermission { (allowed) in if allowed { callback(.authorized) }else { callback(.denied) } } } public static func requestContacts (_ callback : @escaping Callback ) { let status = CNContactStore .authorizationStatus(for: .contacts) var phoneStatus: PermissionStatus 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) } } 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) } } 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) } } 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 () } } 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 效果: