iOS开发中权限再度梳理
前言
上篇文章iOS开发中的这些权限,你搞懂了吗?介绍了一些常用权限的获取和请求方法,知道这些方法的使用基本上可以搞定大部分应用的权限访问的需求。但是,这些方法并不全面,不能涵盖住所有权限访问的方法。
So,笔者在介绍一下剩下的几种权限的访问方法和一些使用上的注意事项,希望能给大家的开发过程带来一丝便利。
最后,笔者将经常使用的权限请求方法封装开源库JLAuthorizationManager送给大家,欢迎大家pull request 和 star~~
权限
语音识别
媒体资料库/Apple Music
Siri
健康数据共享
蓝牙
住宅权限(HomeKit)
社交账号体系权限
活动与体能训练记录
广告标识
语音识别
引入头文件: @import Speech;
首先判断当前应用所处的权限状态,若当前状态为NotDetermined(未确定),此时,需要调用系统提供的请求权限方法,同时也是触发系统弹窗的所在点;
该权限涉及到的类为 SFSpeechRecognizer,具体代码如下:
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
|
- (void)p_requestSpeechRecognizerAccessWithAuthorizedHandler:(void(^)())authorizedHandler unAuthorizedHandler:(void(^)())unAuthorizedHandler{ SFSpeechRecognizerAuthorizationStatus authStatus = [SFSpeechRecognizer authorizationStatus]; if (authStatus == SFSpeechRecognizerAuthorizationStatusNotDetermined) { //调用系统提供的权限访问的方法 [SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus status) { if (status == SFSpeechRecognizerAuthorizationStatusAuthorized) { dispatch_async(dispatch_get_main_queue(), ^{ //授权成功后 authorizedHandler ? authorizedHandler() : nil; }); } else { dispatch_async(dispatch_get_main_queue(), ^{ //授权失败后 unAuthorizedHandler ? unAuthorizedHandler() : nil; }); } }]; } else if (authStatus == SFSpeechRecognizerAuthorizationStatusAuthorized){ authorizedHandler ? authorizedHandler() : nil; } else { unAuthorizedHandler ? unAuthorizedHandler() : nil; } } |
需要注意的是,调用requestAuthorization方法的block回调是在任意的子线程中进行的,如果你需要在授权成功后刷新UI的话,需要将对应的方法置于主线程中进行,笔者将上述方法默认在主线程中进行。后续权限请求方法与此类似,不再赘述。
在info.plist添加指定的配置信息,如下所示:
Speech Recognizer
媒体资料库/Apple Music
导入头文件@import MediaPlayer;
使用类MPMediaLibrary进行权限访问,代码如下;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
- (void)p_requestAppleMusicAccessWithAuthorizedHandler:(void(^)())authorizedHandler unAuthorizedHandler:(void(^)())unAuthorizedHandler{ MPMediaLibraryAuthorizationStatus authStatus = [MPMediaLibrary authorizationStatus]; if (authStatus == MPMediaLibraryAuthorizationStatusNotDetermined) { [MPMediaLibrary requestAuthorization:^(MPMediaLibraryAuthorizationStatus status) { if (status == MPMediaLibraryAuthorizationStatusAuthorized) { dispatch_async(dispatch_get_main_queue(), ^{ authorizedHandler ? authorizedHandler() : nil; }); } else { dispatch_async(dispatch_get_main_queue(), ^{ unAuthorizedHandler ? unAuthorizedHandler() : nil; }); } }]; } else if (authStatus == MPMediaLibraryAuthorizationStatusAuthorized){ authorizedHandler ? authorizedHandler() : nil; } else { unAuthorizedHandler ? unAuthorizedHandler() : nil; } } |
在info.plist添加指定的配置信息,如下所示:
Media
Siri
导入头文件@import Intents;;
与其他权限不同的时,使用Siri需要在Xcode中Capabilities打开Siri开关,Xcode会自动生成一个xx.entitlements文件,若没有打开该开关,项目运行时会报错。
实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
- (void)p_requestSiriAccessWithAuthorizedHandler:(void(^)())authorizedHandler unAuthorizedHandler:(void(^)())unAuthorizedHandler{ INSiriAuthorizationStatus authStatus = [INPreferences siriAuthorizationStatus]; if (authStatus == INSiriAuthorizationStatusNotDetermined) { [INPreferences requestSiriAuthorization:^(INSiriAuthorizationStatus status) { if (status == INSiriAuthorizationStatusAuthorized) { dispatch_async(dispatch_get_main_queue(), ^{ authorizedHandler ? authorizedHandler() : nil; }); } else { dispatch_async(dispatch_get_main_queue(), ^{ unAuthorizedHandler ? unAuthorizedHandler() : nil; }); } }]; } else if (authStatus == INSiriAuthorizationStatusAuthorized){ authorizedHandler ? authorizedHandler() : nil; } else { unAuthorizedHandler ? unAuthorizedHandler() : nil; } } |
健康数据共享
导入头文件@import HealthKit;
健康数据共享权限相对其他权限相对复杂一些,分为写入和读出权限.
在Xcode 8中的info.plist需要设置以下两种权限:
1
2
|
1、Privacy - Health Update Usage Description 2、Privacy - Health Share Usage Description |
具体实现代码:
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
|
//设置写入/共享的健康数据类型 - (NSSet *)typesToWrite { HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]; HKQuantityType *distanceType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]; return [NSSet setWithObjects:stepType,distanceType, nil]; } //设置读写以下为设置的权限类型: - (NSSet *)typesToRead { HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]; HKQuantityType *distanceType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]; return [NSSet setWithObjects:stepType,distanceType, nil]; } //需要确定设备支持HealthKit if ([HKHealthStore isHealthDataAvailable]) { return ; } HKHealthStore *healthStore = [[HKHealthStore alloc] init]; NSSet * typesToShare = [self typesToWrite]; NSSet * typesToRead = [self typesToRead]; [healthStore requestAuthorizationToShareTypes:typesToShare readTypes:typesToRead completion:^(BOOL success, NSError * _Nullable error) { if (success) { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@ "Health has authorized!" ); }); } else { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@ "Health has not authorized!" ); }); } }]; |
蓝牙
需要导入头文件@import CoreBluetooth;
蓝牙的权限检测相对其他会复杂一些,需要在代理中检测蓝牙状态;
获取蓝牙权限:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
- (void)checkBluetoothAccess { CBCentralManager *cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; CBManagerState state = [cbManager state]; if (state == CBManagerStateUnknown) { NSLog(@ "Unknown!" ); } else if (state == CBManagerStateUnauthorized) { NSLog(@ "Unauthorized!" ); } else { NSLog(@ "Granted!" ); } } - (void)centralManagerDidUpdateState:(CBCentralManager *)central { //这个代理方法会在蓝牙权限状态发生变化时被调用,并且可以根据不同的状态进行相应的修改UI或者数据访问的操作。 } |
请求蓝牙权限
1
2
3
4
5
|
- (void)requestBluetoothAccess { CBCentralManager *cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; //该方法会显示用户同意的弹窗 [cbManager scanForPeripheralsWithServices:nil options:nil]; } |
住宅权限(HomeKit)
需导入头文件@import HomeKit;
HomeKit请求权限的方法如下:
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
|
- (void)requestHomeAccess { self.homeManager = [[HMHomeManager alloc] init]; //当设置该代理方法后,会请求用户权限 self.homeManager.delegate = self; } - (void)homeManagerDidUpdateHomes:(HMHomeManager *)manager { if (manager.homes.count > 0) { // home的数量不为空,即表示用户权限已通过 } else { __weak HMHomeManager *weakHomeManager = manager; // Prevent memory leak [manager addHomeWithName:@ "Test Home" completionHandler:^(HMHome *home, NSError *error) { if (!error) { //权限允许 } else { if (error.code == HMErrorCodeHomeAccessNotAuthorized) { //权限不允许 } else { //处理请求产生的错误 } } if (home) { [weakHomeManager removeHome:home completionHandler:^(NSError * _Nullable error) { //移除Home }]; } }]; } } |
社交账号体系权限
导入头文件@import Accounts;
获取对应的权限:
1
2
3
4
5
6
7
8
9
10
|
- (void)checkSocialAccountAuthorizationStatus:(NSString *)accountTypeIndentifier { ACAccountStore *accountStore = [[ACAccountStore alloc] init]; ACAccountType *socialAccount = [accountStore accountTypeWithAccountTypeIdentifier:accountTypeIndentifier]; if ([socialAccount accessGranted]) { NSLog(@ "权限通过了" ); } else { NSLog(@ "权限未通过!" ); } } |
accountTypeIndentifier 可以是以下类型:
1
2
3
4
5
|
ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierTwitter NS_AVAILABLE(NA, 5_0); ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierFacebook NS_AVAILABLE(NA, 6_0); ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierSinaWeibo NS_AVAILABLE(NA, 6_0); ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierTencentWeibo NS_AVAILABLE(NA, 7_0); ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierLinkedIn NS_AVAILABLE(NA, NA); |
请求对应的权限:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
- (void)requestTwitterAccess { ACAccountStore *accountStore = [[ACAccountStore alloc] init]; ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:accountTypeIdentifier]; [accountStore requestAccessToAccountsWithType: accountType options:nil completion:^(BOOL granted, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (granted){ NSLog(@ "授权通过了" ); } else { NSLog(@ "授权未通过" ); } }); }]; } |
活动与体能训练记录
导入头文件@import CoreMotion;
具体实现代码:
1
2
3
4
5
6
7
|
//访问活动与体能训练记录 CMMotionActivityManager *cmManager = [[CMMotionActivityManager alloc] init]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [cmManager startActivityUpdatesToQueue:queue withHandler:^(CMMotionActivity *activity) { //授权成功后,会进入Block方法内,授权失败不会进入Block方法内 }]; |
广告标识
导入头文件@import AdSupport;
获取广告标识的权限状态:
1
|
BOOL isAuthorizedForAd = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]; |
在使用advertisingIdentifier属性前,必须调用上述方法判断是否支持,如果上述方法返回值为NO,则advertising ID访问将会受限。
小结一下
通过以上两篇文章的整理,有关iOS系统权限问题的处理基本上涵盖完全了;
并不是所有的权限访问都有显式的调用方法,有些是在使用过程中进行访问的,比如定位权限、蓝牙共享权限、Homekit权限、活动与体能训练权限,这些权限在使用时注意回调方法中的权限处理;
HomeKit、HealthKit、Siri需要开启Capabilities中的开关,即生成projectName.entitlements文件;
开源库JLAuthorizationManager支持集成大部分常用的权限访问,便捷使用 welcome to pull request or star
iOS开发中权限再度梳理的更多相关文章
- 再续iOS开发中的这些权限
前言 上篇文章iOS开发中的这些权限,你搞懂了吗?介绍了一些常用权限的获取和请求方法,知道这些方法的使用基本上可以搞定大部分应用的权限访问的需求.但是,这些方法并不全面,不能涵盖住所有权限访问的方法. ...
- ios开发中的小技巧
在这里总结一些iOS开发中的小技巧,能大大方便我们的开发,持续更新. UITableView的Group样式下顶部空白处理 //分组列表头部空白处理 UIView *view = [[UIViewal ...
- fir.im Weekly - iOS开发中的Git流程
本期 fir.im Weekly 收集了微博上的热转资源,包含 Android.iOS 开发工具.源码等好用的轮子,还有一些 APP 设计的 Tips,希望对你有用. 精仿知乎日报 iOS 端 @我偏 ...
- 在iOS开发中使用FMDB
在iOS开发中使用FMDB 前言 SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库.iOS SDK 很早就支持了 SQLite,在使用时,只需 ...
- 【转】在iOS开发中使用FMDB
本文转载自:唐巧的博客 在iOS开发中使用FMDB APR 22ND, 2012 前言 SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库.iO ...
- 总结iOS开发中的断点续传那些事儿
前言 断点续传概述 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始.当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很 ...
- iOS开发中静态库之".framework静态库"的制作及使用篇
iOS开发中静态库之".framework静态库"的制作及使用篇 .framework静态库支持OC和swift .a静态库如何制作可参照上一篇: iOS开发中静态库之" ...
- iOS开发中静态库制作 之.a静态库制作及使用篇
iOS开发中静态库之".a静态库"的制作及使用篇 一.库的简介 1.什么是库? 库是程序代码的集合,是共享程序代码的一种方式 2.库的类型? 根据源代码的公开情况,库可以分为2种类 ...
- IOS 开发中 Whose view is not in the window hierarchy 错误的解决办法
在 IOS 开发当中经常碰到 whose view is not in the window hierarchy 的错误,该错误简单的说,是由于 "ViewController" ...
随机推荐
- 利用网络Socket和多线程实现一个双向聊天
接收键盘输入然后向对方发送消息的线程 package cn.com.chat; import java.io.BufferedReader; import java.io.BufferedWriter ...
- NOIP 2013 T2 火柴排队 ---->求逆序对
[NOIP2013T2]火柴排队 背景 noip2013day1 描述 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度. 现在将每盒中的火柴各 自 排成一列, 同一列火柴的高度互不相同, ...
- ShowDialog函数与Form的Activated函数同时使用的陷阱
当我们需要在form启动之时,焦点显示在特定的控件“btn”上,我们可以先将btn的TabIndex设为0,然后要确保它visible=true,最后在Form的Activated事件方法中btn.G ...
- React安装 脚手架create-react-app安装步骤及问题
create-react-app 是来自于 Facebook的脚手架,通过该命令我们无需配置就能快速构建 React 开发环境. 安装步骤: 1.先装脚手架 PS:第一次装直接在打开CMD默认 ...
- asp.net URL传递中文参数System.Web.HttpUtility.UrlEncode与Server.UrlEncode的区别
asp.net URL传递中文参数System.Web.HttpUtility.UrlEncode与Server.UrlEncode的区别(一) HttpUtility.UrlEncode 方法: 对 ...
- 转载:SoapUI之接口数据传递
SoapUI之接口数据传递(TestCase.TestSuite传递) SoapUI之接口数据传递(step传递) SoapUI+Groovy做接口自动化测试 SoapUI中使用Conditional ...
- npm一点点
写在开头 要抓紧学习了,不然要遭... 月底之前有大量东西要学习,干 npm 包管理工具 允许用户从NPM服务器下载别人编写的第三方包到本地使用. 允许用户从NPM服务器下载并安装别人编写的命令行程序 ...
- 插入排序InsertSort
插入排序:从第二个数开始 一直和前面的数组比较 获得排序定位 代码 /** *插入排序 */ public class InsertSort { public static void inser ...
- js脚本捕获页面 GET 方式请求的参数?其实直接使用 window.location.search 获得
js脚本捕获页面 GET 方式请求的参数?其实直接使用 window.location.search 获得
- 爬虫工具--Beautifusoup
import requests from bs4 import BeautifulSoup s=requests.Session() r=s.get('https://www.tumblr.com/l ...