iOS14 隐私适配及部分解决方案

在刚刚结束的线上 WWDC 2020 发布会上苹果向我们展示了新的 iOS14 系统。iOS14 的适配,很重要的一环就集中在用户隐私和安全方面。
在 iOS13 及以前,当用户首次访问应用程序时,会被要求开放大量权限,比如相册、定位、联系人,实际上该应用可能仅仅需要一个选择图片功能,却被要求开放整个照片库的权限,这确实是不合理的。对于相册,在 iOS14 中引入了 “LimitedPhotos Library” 的概念,用户可以授予应用访问其一部分的照片,对于应用来说,仅能读取到用户选择让应用来读取的照片,让我们看到了 Apple 对于用户隐私的尊重。这仅仅是一部分,在iOS14 中,可以看到诸多类似的保护用户隐私的措施,也需要我们升级适配。
最近在调研 iOS14的适配方案,本文主要分享一下 iOS14 上对于隐私授权的变更和部分适配方案,欢迎补充指正。

适配点

相册

✎ iOS14 新增了“Limited Photo Library Access” 模式,在授权弹窗中增加了 Select Photo 选项。用户可以在 App 请求调用相册时选择部分照片让 App 读取。从 App 的视⻆来看,你的相册里就只有这几张照片,App 无法得知其它照片的存在。

✎ iOS14 中当用户选择“PHAuthorizationStatusLimited” 时,如果未进行适配,有可能会在每次触发相册功能时都进行弹窗询问用户是否需要修改照片权限。

✎ 对于这种情况可通过在 Info.plist 中设置 “PHPhotoLibraryPreventAutomaticLimitedAccessAlert”的值为 YES 来阻止该弹窗反复弹出,并且可通过下面这个 API 来主动控制何时弹出PHPickerViewController 进行照片选择。-

[[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];

✎ 在 iOS14 中官方推荐使用 PHPicker 来替代原 API 进行图片选择。PHPicker 为独立进程,会在视图最顶层进行展示,应用内无法对其进行截图也无法直接访问到其内的数据。

  • UIImagePickerController -> PHPickerViewController, UIImagePickerViewController 功能受限,每次只能选择一张图片,将逐渐被废弃。

  • PHPicker 支持多选,支持搜索,支持按 image,video,livePhotos 等进行选择。

✎ 新API及迁移demo:

@interface ViewController () <PHPickerViewControllerDelegate>

  @property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (nonatomic, strong) NSArray<NSItemProvider *> *itemProviders;

@end

@implementation ViewController

  - (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view.
}

  - (IBAction)button:(id)sender {
      // 以下 API 仅为 iOS14 only
      PHPickerConfiguration *configuration = [[PHPickerConfiguration alloc] init];
      configuration.filter = [PHPickerFilter videosFilter]; // 可配置查询用户相册中文件的类型,支持三种
    configuration.selectionLimit = 0; // 默认为1,为0时表示可多选。

      PHPickerViewController *picker = [[PHPickerViewController alloc] initWithConfiguration:configuration];
      picker.delegate = self;
      // picker vc,在选完图片后需要在回调中手动 dismiss
    [self presentViewController:picker animated:YES completion:^{

      }];
  }

#pragma mark - Delegate

  - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results {
      [picker dismissViewControllerAnimated:YES completion:nil];
      if (!results || !results.count) {
          return;
      }
      NSItemProvider *itemProvider = results.firstObject.itemProvider;
      if ([itemProvider canLoadObjectOfClass:UIImage.class]) {
          __weak typeof(self) weakSelf = self;
          [itemProvider loadObjectOfClass:UIImage.class completionHandler:^(__kindof id<NSItemProviderReading>  _Nullable object, NSError * _Nullable error) {
              if ([object isKindOfClass:UIImage.class]) {
                  __strong typeof(self) strongSelf = weakSelf;
                  dispatch_async(dispatch_get_main_queue(), ^{
                      strongSelf.imageView.image = (UIImage *)object;
                  });
              }
          }]; 
      }
    }

✎ 需要注意的是,在 limit Photo 模式下,AssetsLibrary 访问相册会失败;在 writeOnly 模式下,AssetLibrary 也会有显示问题。建议还在使用 AssetsLibrary 的同学尽快迁移到新 API。

✎ 授权相关:旧 API 废弃,增加 PHAccessLevel 参数。如果再使用以前的API来获取权限状态,

PHAuthorizationStatusLimited 状态下也会返回

PHAuthorizationStatusAuthorized

typedef NS_ENUM(NSInteger, PHAccessLevel) {
  PHAccessLevelAddOnly = 1, // 仅允许添加照片
  PHAccessLevelReadWrite = 2, // 允许访问照片,limitedLevel 必须为 readWrite
} API_AVAILABLE(macos(10.16), ios(14), tvos(14));

// 查询权限
PHAccessLevel level = PHAccessLevelReadWrite;
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatusForAccessLevel:level];
  switch (status) {
      case PHAuthorizationStatusLimited:
          NSLog(@"limited");
          break;
      case PHAuthorizationStatusDenied:
          NSLog(@"denied");
          break;
      case PHAuthorizationStatusAuthorized:
          NSLog(@"authorized");
          break;
      default:
          break;
}

// 请求权限,需注意 limited 权限尽在 accessLevel 为 readAndWrite 时生效
[PHPhotoLibrary requestAuthorizationForAccessLevel:level handler:^(PHAuthorizationStatus status) {
  switch (status) {
      case PHAuthorizationStatusLimited:
          NSLog(@"limited");
          break;
      case PHAuthorizationStatusDenied:
          NSLog(@"denied");
          break;
      case PHAuthorizationStatusAuthorized:
          NSLog(@"authorized");
          break;
      default:
          break; 
  }
}];

定位

✎ 在 iOS13 及以前,App 请求用户定位授权时为如下形态:一旦用户同意应用获取定位信息,当前应用就可以获取到用户的精确定位。

✎ iOS14 新增用户大致位置选项可供用户选择,原因是大多数 App 实际上并不需要获取用户到用户最准确的定位信息。iOS14 授权弹窗新增的 Precise的开关默认会选中精确位置。用户通过这个开关可以进行更改,当把这个值设为 On 时,地图上会显示精确位置;切换为Off时,将显示用户的大致位置。

✎ 对于对用户位置敏感度不高的 App 来说,这个似乎无影响,但是对于强依赖精确位置的 App 适配工作就显得非常重要了。可以通过用户在 “隐私设置” 中设置来开启精确定位,但是可能用户宁可放弃使用这个应用也不愿意开启。这个时候,iOS14 在 CLLocationManager 新增两个方法可用于向用户申请临时开启一次精确位置权限。

使用方式也很简单,需要首先在 Info.plist 中配置“NSLocationTemporaryUsageDescriptionDictionary”字典中需要配置 key 和 value 表明使用位置的原因,以及具体的描述。

在本例中,key 即为获取用户权限时传的 "purposeKey",最终呈现给用户的就是左图,右图为当App主动关闭精确定位权限申请。

✎ 对于地理位置不敏感的App 来说,iOS14 也可以通过直接在 info.plist 中添加 NSLocationDefaultAccuracyReduced 为 true 默认请求大概位置。

✎ 这样设置之后,即使用户想要为该 App 开启精确定位权限,也无法开启。

✎ 也可以直接通过API来根据不同的需求设置不同的定位精确度。

✎ 需要注意的是,当 App 在 Background 模式下,如果并未获得精确位置授权,那么 Beacon 及其他位置敏感功能都将受到限制。

▐ Local Network

✎ iOS14 当 App 要使用 Bonjour 服务时或者访问本地局域网,使用 mDNS 服务等,都需要授权,开发者需要在 Info.plist 中详细描述使用的为哪种服务以及用途。下图为需要无需申请权限与需要授权的服务:

✎ 在 "隐私设置" 中也可以查看和修改具体有哪些 App 正在使用 LocalNetwork

✎ 如果应用中需要使用 LocalNetwork 需要在 Info.plist 中配置两个选项,详细描述为什么需要使用该权限,以及需要列出具体使用 LocalNetwork 的服务列表。

✎ 对于使用了下列包含 Bonjour 的 framework,都需要更新描述.

Wi-Fi Address

✎ iOS8 - iOS13 ,用户在不同的网络间切换和接入时,mac 地址都不会改变,这也就使得网络运营商还是可以通过 mac 地址对用户进行匹配和用户信息收集,生成完整的用户信息。iOS14 提供 Wifi 加密服务,每次接入不同的 WiFi 使用的 mac 地址都不同。每过 24 小时,mac 地址还会更新一次。需要关注是否有使用用户网络 mac 地址的服务。

✎ 下图为 iOS13 及之前用户接入网络时 mac 地址并不会进行改变

✎ 下图为 iOS14 用户接入 Wi-Fi 时 mac 地址的变化情况

✎ 并且用户也可以自行选择是否开启 private Wi-Fi address

剪切板

✎ 在 iOS14 中,读取用户剪切板的数据会弹出提示。

✎ 弹出提示的原因是使用 UIPasteboard 访问用户数据,访问以下数据都会弹出 toast 提示。

✎ 兼容方案:如果应用访问剪切板仅仅用于判断是否为URL格式,则 iOS14 新增了两个 API 可以用于规避该提示。如果应用想直接访问剪切板的数据,暂时可能无法做到规避该提示。iOS14 新增两种

UIPasteboardDetectionPattern。

✎ 上面的两个 API 可用于规避提示,但只能用于判断剪切板中是否有 URL,并不是真正的访问剪贴板数据,也拿不到剪切板的真实数据。下面两个 API 可以获得具体的 URL 信息,但是会触发剪切板提示。并且实测当用户剪切板中包含多个 URL 时只会返回第一个。

✎ 使用示例

NSSet *patterns = [[NSSet alloc] initWithObjects:UIPasteboardDetectionPatternProbableWebURL, nil];
[[UIPasteboard generalPasteboard] detectPatternsForPatterns:patterns completionHandler:^(NSSet<UIPasteboardDetectionPattern> * _Nullable result, NSError * _Nullable error) {
    if (result && result.count) {
            // 当前剪切板中存在 URL
    }
}];

相机和麦克风

✎ iOS14 中 App 使用相机和麦克风时会有图标提示以及绿点和黄点提示,并且会显示当前是哪个 App 在使用此功能。我们无法控制是否显示该提示。

✎ 会触发录音小黄点的代码示例:

AVAudioRecorder *recorder = [[AVAudioRecorder alloc] initWithURL:recorderPath settings:nil error:nil];
[recorder record];

✎ 触发相机小绿点的代码示例:

AVCaptureDeviceInput *videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:videoCaptureDevice error:nil];
AVCaptureSession *session = [[AVCaptureSession alloc] init];
if ([session canAddInput:videoInput]) {
    [session addInput:videoInput];
}
[session startRunning];

IDFA

✎ IDFA 全称为 Identity for Advertisers ,即广告标识符。用来标记用户,目前最广泛的用途是用于投放广告、个性化推荐等。

✎ 在 iOS13 及以前,系统会默认为用户开启允许追踪设置,我们可以简单的通过代码来获取到用户的 IDFA 标识符。

if ([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) {
    NSString *idfaString = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
    NSLog(@"%@", idfaString);
}

✎ 但是在 iOS14 中,这个判断用户是否允许被追踪的方法已经废弃。

✎ iOS14 中,系统会默认为用户关闭广告追踪权限。

✎ 对于这种情况,我们需要去请求用户权限。首先需要在 Info.plist 中配置" NSUserTrackingUsageDescription " 及描述文案,接着使用 AppTrackingTransparency 框架中的 ATTrackingManager 中的 requestTrackingAuthorizationWithCompletionHandler 请求用户权限,在用户授权后再去访问 IDFA 才能够获取到正确信息。

#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <AdSupport/AdSupport.h>

- (void)testIDFA {
    if (@available(iOS 14, *)) {
        [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
            if (status == ATTrackingManagerAuthorizationStatusAuthorized) {
                NSString *idfaString = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
            }
        }];
    } else {
        // 使用原方式访问 IDFA
    }
}

对于用户拒绝授权 UserTracking 的情况,可以考虑接入苹果的 SKAdNetwork 框架进行广告分析。感兴趣的同学可进一步了解:https://developer.apple.com/documentation/storekit/skadnetwork

上传 AppStore

更加严格的隐私审核,可以让用户在下载 App 之前就知道此 App 将会需要哪些权限。目前苹果商店要求所有应用在上架时都必须提供一份隐私政策。如果引入了第三方收集用户信息等SDK,都需要向苹果说明是这些信息的用途。


总结


对于这次 iOS14 的隐私权限大升级和新尝试,体现了苹果对于用户隐私的尊重。

从用户角度来说,近年来越来越精准的广告投放让我们越来越感觉自己被”监视“着,此次升级后,我们有了更多保护自己隐私的方式以及避免广告骚扰的方法,苹果此举无疑会加大我们对其的好感度和信任感。但从另一个角度来说,对于 IDFA 的限制,可能会导致之前许多依靠广告投放收入的免费 App 难以继续维持生计,也可能也会导致免费 App 的数量有所降低。从开发者的角度来说,除了对 iOS14 隐私升级的积极适配外,也让我们感受到了 iOS14 中对于用户隐私的重视无疑会提高获取用户行为信息的成本。

冲击最大的应该就是广告行业,对于目前的推荐算法和用户拉新都会受到影响,如何在充分尊重用户隐私的前提下进行广告的精准投放对于开发者和广告商来说都是一个不小的机遇和挑战。


https://mp.weixin.qq.com/s/CJML-2YUv6sqEEnI8rI0Bw

iOS 14 苹果对 Objective-C Runtime 的优化

Objective-C 是一门古老的语言,诞生于 1984 年,跟随 Apple 一路浮沉,见证了乔布斯创建了 NeXT,也见证了乔布斯重回 Apple 重创辉煌,它用它特立独行的语法,堆砌了 UIKit,AppKit, Foundation 等一个个基石,时间来到 2020 年,面对汹涌的"后浪" Swift,"老前辈" Objective-C 也在发挥着自己的余热,即使面对越来越多阵地失守,唯有“老兵不死,只会慢慢凋亡"才能体现的悲壮。今年,Apple 给 Objective-C Runtime 带来了新的优化,接下来,让我们深入理解这些变化。

发布于:17天以前  |  139次阅读  |  详细内容 »

iOS14 隐私适配及部分解决方案

在刚刚结束的线上 WWDC 2020 发布会上苹果向我们展示了新的 iOS14 系统。iOS14 的适配,很重要的一环就集中在用户隐私和安全方面。 在 iOS13 及以前,当用户首次访问应用程序时,会被要求开放大量权限,比如相册、定位、联系人,实际上该应用可能仅仅需要一个选择图片功能,却被要求开放整个照片库的权限,这确实是不合理的。对于相册,在 iOS14 中引入了 “LimitedPhotos Library” 的概念,用户可以授予应用访问其一部分的照片,对于应用来说,仅能读取到用户选择让应用来读取的照片,让我们看到了 Apple 对于用户隐私的尊重。这仅仅是一部分,在iOS14 中,可以看到诸多类似的保护用户隐私的措施,也需要我们升级适配。 最近在调研 iOS14的适配方案,本文主要分享一下 iOS14 上对于隐私授权的变更和部分适配方案,欢迎补充指正。

发布于:1月以前  |  173次阅读  |  详细内容 »

Metal新特性:大幅度提升iOS端性能

Metal 是一个和 OpenGL ES 类似的面向底层的图形编程接口,通过使用相关的 api 可以直接操作 GPU ,最早在 2014 年的 WWDC 的时候发布。Metal 是 iOS 平台独有的,意味着它不能像 OpenGL ES 那样支持跨平台,但是它能最大的挖掘苹果移动设备的 GPU 能力,进行复杂的运算,像 Unity 等游戏引擎都通过 Metal 对 3D 能力进行了优化, App Store 还有相应的运用 Metal 技术的游戏专题。 闲鱼团队是比较早在客户端侧选择Flutter方案的技术团队,当前的闲鱼工程里也是一个较为复杂的Native-Flutter混合工程。作为一个2C的应用,性能和用户体验一直是闲鱼技术团队在开发中比较关注的点。而Metal这样的直接操作GPU的底层接口无疑会给闲鱼技术团队突破性能瓶颈提供一些新的思路。 下面会详细阐述一下这次大会Metal相关的新特性,以及对于闲鱼技术和整个淘系技术来说,这些新特性带来了哪些技术启发与思考。

发布于:1月以前  |  125次阅读  |  详细内容 »

Core Image:iOS图像处理技术追踪

Core Image是苹果官方提供的图像处理框架,通过丰富的built-in(内置)或自定义Filter(过滤器)高效处理静态图片、动态图片或视频。开发者还可以通过构造Filter链或自定义Core Image Kernel来实现更丰富的效果。 在WWDC20中,苹果官方针对Core Image技术在以下三方面做了优化:Core Image对视频/动图的支持、基于Metal构建Core Image (CI) Kernel以及Core Image的Debug支持。 这三方面会在下文逐一提到,文末笔者也会浅谈Core Image在手淘图片库中的应用可能以及对Core Image技术的展望。

发布于:1月以前  |  139次阅读  |  详细内容 »

闲鱼如何解决iOS环境搭建与APP打包速度问题

随着Flutter等跨端框架的出现,业务开发同学经常需要在Android/IOS上跨端进行业务开发,问题定位等。新的不熟悉的环境的搭建总会遇到各种各样的问题,导致搭建失败,特别是IOS开发环境,是最复杂的,不仅环境搭建繁琐,而且切分支后的打包速度很慢,所以我们设计实现了两个工具,用于优化闲鱼IOS开发体验。

发布于:1月以前  |  276次阅读  |  详细内容 »

iOS14 隐私适配及部分解决方案

在刚刚结束的线上 WWDC 2020 发布会上苹果向我们展示了新的 iOS14 系统。iOS14 的适配,很重要的一环就集中在用户隐私和安全方面。 在 iOS13 及以前,当用户首次访问应用程序时,会被要求开放大量权限,比如相册、定位、联系人,实际上该应用可能仅仅需要一个选择图片功能,却被要求开放整个照片库的权限,这确实是不合理的。对于相册,在 iOS14 中引入了 “LimitedPhotos Library” 的概念,用户可以授予应用访问其一部分的照片,对于应用来说,仅能读取到用户选择让应用来读取的照片,让我们看到了 Apple 对于用户隐私的尊重。这仅仅是一部分,在iOS14 中,可以看到诸多类似的保护用户隐私的措施,也需要我们升级适配。 最近在调研 iOS14的适配方案,本文主要分享一下 iOS14 上对于隐私授权的变更和部分适配方案,欢迎补充指正。

发布于:1月以前  |  292次阅读  |  详细内容 »

iOS 隐形水印之 LSB 实现

在音视频的领域里,其涵盖的知识点繁多,学习方向也很多。而本篇就是一篇比较入门的文章它简单地介绍如何在 iOS 上读取图片 RGB 数据,并通过修改最后一位 bit 来记录数字水印的信息下面就介绍《隐形水印之 iOS 实现》

发布于:3月以前  |  405次阅读  |  详细内容 »

声明式 UIKit 在有赞美业的实践

随着 Flutter 的出现,UI 开发形式也越来越趋向相同,Flutter,SwiftUI,RN,Weex 等新兴UI框架无一意外都使用了声明式的 UI 开发模式,和支持了FlexBox的布局系统。

发布于:3月以前  |  389次阅读  |  详细内容 »

iOS 架构谈:剖析 Uber 的 RIB 架构

加入 UBER 是我的 iOS 工程师职业的新篇章,所有这一切都始于称为 RIB 的新架构。该架构背后的主要思想是,应用程序应由业务逻辑而不是视图驱动。展示 RIB 的最佳方法是一棵树:每个 RIB 都是一个节点,并且它可以不包含子节点,也可以包括一个或多个子节点。

发布于:3月以前  |  351次阅读  |  详细内容 »

如何调试支付宝(iOS)

最近在做的一件事情,从代码层面分析下各家小程序(微信、头条、支付宝、百度)的启动性能,探究各家小程序的实现细节和差异。

发布于:3月以前  |  375次阅读  |  详细内容 »

iOS GPUImage源码解读(一)

最近在不断学习、使用的过程中,有了更深刻的理解,特来写一篇源码解读的文章详细介绍下核心代码的具体实现。至于括号里的“一”,主要是觉得GPUImage还有很多值得深入学习和分享的内容,后续的学习和使用过程中有新的心得体会还会继续给大家分享。

发布于:3月以前  |  421次阅读  |  详细内容 »

iOS开发之Masonry框架源码解析

Masonry是iOS在控件布局中经常使用的一个轻量级框架,Masonry让NSLayoutConstraint使用起来更为简洁。Masonry简化了NSLayoutConstraint的使用方式,让我们可以以链式的方式为我们的控件指定约束。本篇博客的主题不是教你如何去使用Masonry框架的,而是对Masonry框架的源码进行解析,让你明白Masonry是如何对NSLayoutConstraint进行封装的,以及Masonry框架中的各个部分所扮演的角色是什么样的。在Masonry框架中,仔细的品味干货还是很多的。Masonry框架是Objective-C版本的,如果你的项目是Swift语言的,那么就得使用SnapKit布局框架了。SnapKit其实就是Masonry的Swift版本,两者虽然实现语言不同,但是实现思路大体一致。

发布于:3月以前  |  400次阅读  |  详细内容 »

最多阅读

快速配置 Sign In with Apple 1年以前  |  2974次阅读
给数组NSMutableArray排序 1年以前  |  2254次阅读
开篇 关于iOS越狱开发 1年以前  |  2203次阅读
在越狱的iPhone设置上使用lldb调试 1年以前  |  2144次阅读
APP适配iOS11 1年以前  |  2124次阅读
UITableViewCell高亮效果实现 1年以前  |  2093次阅读
App Store 审核指南[2017年最新版本] 1年以前  |  1932次阅读
使用 GPUImage 实现一个简单相机 1年以前  |  1905次阅读
所有iPhone设备尺寸汇总 1年以前  |  1884次阅读
关于Xcode不能打印崩溃日志 1年以前  |  1834次阅读
使用ssh访问越狱iPhone的两种方式 1年以前  |  1800次阅读
使用ssh 访问越狱iPhone的两种方式 1年以前  |  1699次阅读
UIDevice的简单使用 1年以前  |  1589次阅读
为对象添加一个释放时触发的block 1年以前  |  1535次阅读
使用最高权限操作iPhone手机 1年以前  |  1477次阅读