iOS开发中权限再度梳理

前言

  • 上篇文章iOS开发中的这些权限,你搞懂了吗?介绍了一些常用权限的获取和请求方法,知道这些方法的使用基本上可以搞定大部分应用的权限访问的需求。但是,这些方法并不全面,不能涵盖住所有权限访问的方法。
  • So,笔者在介绍一下剩下的几种权限的访问方法和一些使用上的注意事项,希望能给大家的开发过程带来一丝便利。
  • 最后,笔者将经常使用的权限请求方法封装开源库JLAuthorizationManager送给大家,欢迎大家pull requeststar~~

权限

  • 语音识别;
  • 媒体资料库/Apple Music;
  • Siri;
  • 健康数据共享;
  • 蓝牙;
  • 住宅权限(HomeKit);
  • 社交账号体系权限;
  • 活动与体能训练记录;
  • 广告标识;

语音识别

  • 引入头文件: @import Speech;
  • 首先判断当前应用所处的权限状态,若当前状态为NotDetermined(未确定),此时,需要调用系统提供的请求权限方法,同时也是触发系统弹窗的所在点;
  • 该权限涉及到的类为** SFSpeechRecognizer**,具体代码如下:
- (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进行权限访问,代码如下;
- (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文件,若没有打开该开关,项目运行时会报错。
  • 实现代码如下:
- (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、Privacy - Health Update Usage Description
2、Privacy - Health Share Usage Description
  • 具体实现代码:
//设置写入/共享的健康数据类型
- (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;
  • 蓝牙的权限检测相对其他会复杂一些,需要在代理中检测蓝牙状态;
  • 获取蓝牙权限:
- (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或者数据访问的操作。
}
  • 请求蓝牙权限
- (void)requestBluetoothAccess {
    CBCentralManager *cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
//该方法会显示用户同意的弹窗
    [cbManager scanForPeripheralsWithServices:nil options:nil];
}

住宅权限(HomeKit)

  • 需导入头文件@import HomeKit;
  • HomeKit请求权限的方法如下:
- (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;
  • 获取对应的权限:
- (void)checkSocialAccountAuthorizationStatus:(NSString *)accountTypeIndentifier {

    ACAccountStore *accountStore = [[ACAccountStore alloc] init];
    ACAccountType *socialAccount = [accountStore accountTypeWithAccountTypeIdentifier:accountTypeIndentifier];
    if ([socialAccount accessGranted]) {
        NSLog(@"权限通过了");
    }else{
         NSLog(@"权限未通过!");
 }
}
  • accountTypeIndentifier 可以是以下类型:
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);
  • 请求对应的权限:
- (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;
  • 具体实现代码:
//访问活动与体能训练记录
    CMMotionActivityManager *cmManager = [[CMMotionActivityManager alloc] init];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [cmManager startActivityUpdatesToQueue:queue withHandler:^(CMMotionActivity *activity) {
        
        //授权成功后,会进入Block方法内,授权失败不会进入Block方法内
    }];

广告标识

  • 导入头文件@import AdSupport;
  • 获取广告标识的权限状态:
 BOOL isAuthorizedForAd = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled];
  • 在使用advertisingIdentifier属性前,必须调用上述方法判断是否支持,如果上述方法返回值为NO,则advertising ID访问将会受限。

小结一下

  • 通过以上两篇文章的整理,有关iOS系统权限问题的处理基本上涵盖完全了;
  • 并不是所有的权限访问都有显式的调用方法,有些是在使用过程中进行访问的,比如定位权限蓝牙共享权限Homekit权限活动与体能训练权限,这些权限在使用时注意回调方法中的权限处理;
  • HomeKitHealthKitSiri需要开启Capabilities中的开关,即生成projectName.entitlements文件;
  • 开源库JLAuthorizationManager支持集成大部分常用的权限访问,便捷使用 welcome to pull request or star

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

XSS的原理分析与解剖:第三章(技巧篇)

作者 Black-Hole 0×01 前言: 关于前两节url: 第一章:http://www.freebuf.com/articles/web/40520....

1987
来自专栏mukekeheart的iOS之旅

iOS学习——获取iOS设备的各种信息

  不管是在Android开发还是iOS开发过程中,有时候我们需要经常根据设备的一些状态或信息进行不同的设置和性能配置,例如横竖屏切换时,电池电量低时,内存不够...

7017
来自专栏Jacklin攻城狮

[续]iOS开发中的这些权限,你搞懂了吗?

1992
来自专栏知识分享

八,ESP8266 文件保存数据(基于Lua脚本语言)

应该是LUA介绍8266的最后一篇,,,,,,下回是直接用SDK,,然后再列个12345.......不过要等一两个星期,先忙完朋友的事情 前面几篇 用AT指令...

3967
来自专栏進无尽的文章

扒虫篇-崩溃日志解读及Crash收集

崩溃是让发人员比较头痛的事情,app崩溃了,说明代码写的有问题,这时如何快速定位到崩溃的地方很重要。调试阶段是比较容易找到出问题的地方的,但是已经上线的app并...

2281
来自专栏菩提树下的杨过

老生常谈:利用Membership实现SSO(单点登录)

虽然有一些现成的第三方解决方案比如:OpenID,Passport,SpaceCard等都还不错,但是要么就是收费的(passport),要么就是有点用不习惯(...

2025
来自专栏李蔚蓬的专栏

VMware Workstation14.1.3 & Ubuntu18.04从安装到实用的填坑之路

本机VMware Workstation环境是上学期安装的,拿的老师给的安装包,版本10.0.1。顺便装了个Ubuntu15.10。

1451
来自专栏移动端开发

个推你应该这样用的

前言:     说到这个远程推送,大家知道的应该都挺多的,但用到的估计极光和个推要占一很大部分,这篇博客重点说的就是个推的使用,个推官网的链接在这里,它的集成是...

6009
来自专栏拂晓风起

Loader拉取图片,由于redirect重定向,导致策略文件无效 设置checkPolicyFile后还是无效:需要一个策略文件,但在加载此媒体时未设置 checkPolicyFile 标志

1096
来自专栏写写代码吃吃瓜

Android调试神器stetho使用详解和改造

3606

扫码关注云+社区

领取腾讯云代金券