WWDC Quick Look 💓 By SwiftGGTeam
Apple's privacy pillars in focus

Apple's privacy pillars in focus

观看原视频

Highlight

Apple 将隐私工程归纳为四个支柱:数据最小化(Data Minimization)、端侧处理(On-Device Processing)、透明与控制(Transparency and Control)、安全保护(Security Protections)。iOS 15 围绕这四个支柱推出了 CLLocationButton、CloudKit 加密字段、App Privacy Report 和 iCloud Private Relay 等具体技术,开发者可以直接采用这些能力来提升用户信任。

核心内容

数据最小化:让用户按需分享位置

03:03

很多 App 只有一两个功能需要位置信息,但传统做法是在启动时一次性请求「使用期间」位置权限。用户可能因为不信任而拒绝,导致核心功能无法使用。

iOS 15 引入了 CLLocationButton(共享当前位置按钮)。用户点击按钮后,App 获得一次性的位置访问权限,权限持续到用户离开 App。下次再点击同一按钮时,系统不再弹窗,直接返回位置。这个按钮可以自定义背景色、文字颜色和圆角,但必须保持足够的大小和对比度,确保 iOS 能确认是用户主动点击。

这种设计把位置权限从「安装时的一次大决定」变成了「使用时的多次小决定」,降低了用户的心理门槛。

端侧处理:Siri 语音识别完全本地化

07:01

语音助手的隐私顾虑主要集中在「我的语音会不会被上传到服务器」。Apple 从 iOS 12 开始在芯片中加入 Neural Engine,逐步将 Siri 的组件搬到设备端。iOS 13 实现了 Siri 语音合成的端侧化,iOS 14 实现了键盘听写的端侧化。

iOS 15 完成了最后一步:所有 Siri 的自动语音识别(ASR)模型都在设备端运行。默认情况下,语音请求的音频不会离开 iPhone 或 iPad。带来的额外好处是响应速度更快,部分请求在离线状态下也能完成。

对于开发者,iOS 15 的 Create ML 框架也支持在 iPhone 和 iPad 上直接训练模型。敏感数据(如照片)可以全程留在设备上,训练出的个性化模型也只服务于当前用户。

透明与控制:App Privacy Report 和 Record App Activity

14:44

用户有权知道 App 在什么时候访问了他们的敏感数据。iOS 15 的「记录 App 活动」(Record App Activity)功能会生成一个 JSON 文件,记录 App 访问位置、照片、通讯录等敏感权限的时间点,以及 App 连接的所有域名。

开发者可以在 iOS 15 Beta 中开启这个功能,查看自己的 App 实际行为是否与预期一致。JSON 文件的结构如下:每个访问事件包含时间戳、被访问的数据类型、发起访问的 Bundle ID。域名连接部分会标记是 App 发起的还是网页(WebView)发起的——通过 SafariViewController 和 ASWebAuthenticationSession 的连接会自动标记为网页来源。对于其他第三方内容,开发者可以手动标记:

// 在 NSMutableURLRequest 中标记连接来源
let request = NSMutableURLRequest(url: url)
request.mainDocumentURL = mainDocumentURL

// 在 Network 框架的 NWConnection 中标记
// 或在 URLSession 的 convenience 方法中通过 LinkPresentation 和 AVFoundation 标记

关键点:

  • mainDocumentURL 用于区分主文档请求和第三方资源请求
  • 未标记的连接默认归类为 App 流量
  • WKWebView 新增了传递 NSURLRequest 的方法用于标记

App Privacy Report 将在后续软件更新中向用户开放。开发者现在用 Record App Activity 自查,可以避免上线后被用户发现意外行为。

安全保护:CloudKit 加密字段

23:03

CloudKit 之前只对 CKAsset 提供自动加密。iOS 15 扩展了这个能力:字符串、数字、日期、CLLocation 和数组都可以直接加密存储。

// 设备 1:加密后保存到 CloudKit
myRecord.encryptedValues["encryptedStringField"] = "Sensitive value"

// 设备 2:读取后自动解密
let decryptedString = myRecord.encryptedValues["encryptedStringField"] as? String

关键点:

  • encryptedValues 是 CKRecord 的新属性,用法和普通字段一样
  • 加密在设备端完成,密钥材料存储在 iCloud Keychain 中
  • 支持 CloudKit 分享功能,只有 CKShare 的参与者能解密相关字段
  • CKAsset 已经默认加密,不能再次设为 encryptedValue
  • CKReference 不能被加密,因为服务器需要看到引用关系

iCloud Private Relay:网络层面的隐私保护

24:29

Private Relay 是 iCloud+ 的一项新服务,解决的是「网站和网络提供商追踪用户浏览行为」的问题。它的核心设计是:没有任何单一实体(包括 Apple)能同时知道「你是谁」和「你访问了哪个网站」。

实现方式依赖两个随机选择的中继服务器:

  • 入口代理(Ingress Proxy):知道用户的真实 IP 地址,但看不到目标网站。它把 IP 地址转换为大致地理区域(如”Bay Area, California”)。
  • 出口代理(Egress Proxy):知道目标网站,但看不到用户的真实 IP。它从入口代理提供的地理区域中随机选择一个 IP 地址。

连接使用多层加密,每层由对应的中继服务器剥除。设备是唯一能看到完整信息的节点。访问令牌通过 RSA Blinded Signatures 生成,设备可以兑换网络访问权限而不暴露账户信息。

用户可以在设置中选择两种 IP 地址精度:「保留大致位置」让网站提供本地化内容,或「仅使用国家和时区」获得更强的匿名性。

详细内容

使用 CLLocationButton

04:49

import CoreLocationUI

struct ContentView: View {
    @StateObject var locationManager = LocationManager()

    var body: some View {
        LocationButton(.shareCurrentLocation) {
            locationManager.requestLocation()
        }
        .cornerRadius(8)
        .labelStyle(.titleAndIcon)
    }
}

关键点:

  • LocationButton 是 SwiftUI 专用组件,UIKit 也有对应版本
  • .shareCurrentLocation 是系统定义的按钮语义
  • 按钮外观可自定义,但系统会验证点击的真实性(防止程序化触发)
  • 支持 watchOS、iOS、iPadOS 和 macOS Catalyst

验证 App 的网络行为

16:59

import Network

// 标记第三方内容连接
let connection = NWConnection(host: "cdn.example.com", port: .https, using: .tls)

// 在 NSMutableURLRequest 中手动标记
let request = NSMutableURLRequest(url: thirdPartyContentURL)
request.mainDocumentURL = mainPageURL

关键点:

  • mainDocumentURL 帮助系统区分核心功能和第三方内容
  • SafariViewController 和 ASWebAuthenticationSession 的连接自动标记为网页来源
  • 定期审查 Record App Activity 的 JSON 输出,确认没有意外连接

CloudKit 加密字段的完整流程

23:44

import CloudKit

// 保存加密数据
let record = CKRecord(recordType: "PatientData")
record.encryptedValues["ssn"] = "123-45-6789"
record.encryptedValues["notes"] = "Confidential medical notes"

let container = CKContainer.default()
let database = container.privateCloudDatabase

do {
    let savedRecord = try await database.save(record)
    print("Encrypted record saved")
} catch {
    print("Save failed: \(error)")
}

// 读取加密数据
let query = CKQuery(recordType: "PatientData", predicate: NSPredicate(value: true))
let (matchResults, _) = try await database.records(matching: query)

for (recordID, result) in matchResults {
    switch result {
    case .success(let record):
        let ssn = record.encryptedValues["ssn"] as? String
        print("Decrypted SSN: \(ssn ?? "N/A")")
    case .failure(let error):
        print("Fetch error: \(error)")
    }
}

关键点:

  • encryptedValues 的读写语法和普通字段完全一致
  • 加密/解密由 CloudKit 自动处理,开发者无需管理密钥
  • 需要有效的 iCloud 登录状态(CKAccountStatus.available
  • 新状态 temporarilyUnavailable 表示账户已登录但未就绪,应引导用户到设置验证

核心启发

1. 用 LocationButton 重构位置权限流程

如果你的 App 只在特定页面需要位置(如地图标注、附近商家),把启动时的位置权限请求替换为页面内的 CLLocationButton。用户点击按钮时才请求位置,授权率通常会显著提升。

开始方式:在需要位置的页面放置 LocationButton,移除 Info.plist 中的 NSLocationWhenInUseUsageDescription 弹窗依赖。测试用户首次点击和后续点击的流程差异。

2. 为 CloudKit 中的敏感数据启用加密字段

任何存储在 CloudKit 中的个人身份信息(PII)、医疗记录、财务数据都应该使用 encryptedValues。成本几乎为零,但隐私承诺提升一个量级。

开始方式:审计现有 CloudKit 数据模型,识别敏感字段。将 record["field"] 替换为 record.encryptedValues["field"]。在 CloudKit Console 中确认字段类型显示为 “Encrypted String” 等前缀。

3. 集成 Record App Activity 到 CI 流程

在每次发版前开启 Record App Activity,运行一遍核心用户流程,检查生成的 JSON 中是否有意外的权限访问或域名连接。这能提前发现 SDK 的隐蔽行为。

开始方式:在测试设备上开启「设置 > 隐私 > 记录 App 活动」,运行 App 的核心场景,导出 JSON 文件。检查 accessedDatanetworkActivity 部分,确认所有访问都在预期范围内。

4. 适配 Mail Privacy Protection 和 Hide My Email

如果你的 App 发送营销邮件,需要意识到 iOS 15 的 Mail Privacy Protection 会预加载远程图片,导致打开率统计失真。IP 地址和设备信息也不再可靠。同时,用户可能用 Hide My Email 提供的随机邮箱地址注册,不要假设邮箱地址可以关联到真实身份。

开始方式:审查邮件营销的数据依赖,减少对打开率像素追踪的依赖。用户注册流程中,如果需要真实姓名或联系方式,明确说明用途并单独请求。

关联 Session

评论

GitHub Issues · utterances