WWDC Quick Look 💓 By SwiftGGTeam
What's new in privacy

What's new in privacy

观看原视频

Highlight

iOS 17 引入 Photos Picker 完全嵌入模式、macOS Sonoma 新增 SCContentSharingPicker 屏幕共享选择器、Oblivious HTTP 协议保护用户 IP 地址、Sensitive Content Analysis 框架用设备端 ML 检测敏感内容;visionOS 的空间输入模型通过进程隔离确保 App 无法获取眼动数据。

核心内容

Photos Picker:让用户只分享想分享的照片

以前 App 要获取用户照片,要么请求整个相册权限,要么自己实现一套照片选择 UI。iOS 17 的 Photos Picker 可以完全嵌入 App 内,看起来像 App 的一部分,但实际由系统渲染。用户选择的照片才会共享给 App,其余照片始终在用户控制之下。

三种嵌入模式:

  • 无装饰全屏:最简洁,没有任何系统 chrome
  • 单行横向滚动:适合空间有限的位置
  • 内联完整选择器:带选项菜单,可控制是否共享标题、位置等元数据

iOS 17 还重新设计了完整相册权限对话框,展示照片数量和样本预览。系统会定期提醒用户哪些 App 有完整相册访问权限。

Screen Capture Picker:macOS 屏幕共享不再过度授权

以前 macOS 上的视频会议 App 需要请求完整屏幕录制权限,用户一旦授权,App 可以录制整个屏幕。macOS Sonoma 引入了 SCContentSharingPicker,App 不需要获取录屏权限,系统代为展示窗口选择器,用户选择具体要共享的窗口或屏幕后才分享。

macOS Sonoma 还在菜单栏新增了屏幕共享图标,提醒用户有 App 在录屏。点击可以预览共享内容、添加或移除窗口、结束共享。

日历权限:最小必要原则

如果 App 只需要创建日历事件,使用 EventKitUI 不需要任何权限。如果要用自定义 UI,可以申请新的 write-only 权限,只能添加事件不能读取现有事件。需要完整访问时再请求升级。

已授权 Calendar 访问的 App,升级到 iOS 17 后默认降级为 write-only。用旧版 EventKit 链接的 App,请求权限时系统只给 write-only,尝试读取时自动弹窗请求升级。

Oblivious HTTP:隐藏用户 IP 地址

网络运营商可以观察用户连接了哪些服务器,从而推断个人应用使用模式。Oblivious HTTP(OHTTP)是一种标准化协议,通过中继服务器把”谁”和”访问了什么”分开。

中继服务器知道用户的 IP 地址和目标服务器名,但看不到加密内容。目标服务器收到的是中继转发的请求,看不到原始 IP。没有单一节点同时拥有源 IP、目标 IP 和内容。iCloud Private Relay 已经用 OHTTP 保护所有 DNS 查询。

Sensitive Content Analysis:设备端保护儿童

Communication Safety 从 Messages 扩展到 AirDrop、FaceTime 留言、电话联系人海报和 Photos Picker。同时推出了 Sensitive Content Warning,面向所有年龄段用户。

新的 SensitiveContentAnalysis 框架让开发者也能在 App 中检测敏感内容。使用系统提供的设备端 ML 模型,不需要把内容上传到任何服务器。

macOS 应用数据保护

macOS Sonoma 增加了对应用数据容器的保护。一个开发者团队内的 App 可以互相访问数据,但其他开发者的 App 访问时需要用户授权。App Sandbox 的 App 自动获得这项保护。

可以用 NSDataAccessSecurityPolicy 在 Info.plist 中配置更严格的策略,指定哪些进程可以访问你的 App 数据。

CloudKit 端到端加密

Advanced Data Protection 为 iCloud 中大部分数据提供端到端加密。用 CloudKit 的 App 只需使用加密数据类型(如 EncryptedString),通过 encryptedValues API 读写数据,就能自动获得端到端加密保护。

Safari Private Browsing 增强

Safari 17 的隐私浏览模式新增两项保护:

  • 阻止已知的跟踪和指纹资源加载
  • 自动移除 URL 中的跟踪参数,同时保留非识别部分

Private Click Measurement 现在也支持隐私浏览模式,用于广告归因而不追踪个人。

visionOS 空间输入的隐私设计

眼动和手势数据在隔离的系统进程中处理,App 只收到最终的 tap 事件。系统结合屏幕内容和眼动位置计算 hover 高亮,这个高亮在渲染引擎中添加,App 看不到用户在看哪里。不需要任何新权限,现有 UIKit/SwiftUI/RealityKit App 直接可用。

详细内容

Photos Picker 嵌入

03:58

import PhotosUI

struct PhotoPickerView: View {
    @State private var selectedItems: [PhotosPickerItem] = []
    
    var body: some View {
        PhotosPicker(
            selection: $selectedItems,
            matching: .images,
            photoLibrary: .shared()
        ) {
            Text("Select Photos")
        }
    }
}

关键点:

  • PhotosPicker 是 SwiftUI 原生视图,可以完全嵌入
  • 不需要请求相册权限
  • matching 参数可以过滤照片、视频或 Live Photo
  • 选中的内容通过 PhotosPickerItem 异步加载

Screen Capture Picker(macOS)

06:18

import ScreenCaptureKit

let picker = SCContentSharingPicker.shared

// 配置 picker
let configuration = SCContentSharingPickerConfiguration()
configuration.allowsChangingSelectedContent = true

picker.setConfiguration(configuration, for: myAppBundleID)
picker.add(myContentSharingPickerObserver)
picker.isActive = true

// 用户选择内容后,通过 delegate 接收回调
func contentSharingPicker(
    _ picker: SCContentSharingPicker,
    didUpdateWith filter: SCContentFilter,
    forStreamID streamID: String
) {
    // 使用 filter 创建 SCStream 开始录制
}

关键点:

  • SCContentSharingPicker.shared 获取系统共享选择器
  • App 不需要请求屏幕录制权限
  • 用户显式选择要共享的内容后,App 才能录制
  • 菜单栏图标持续提醒用户有 App 在录屏

Sensitive Content Analysis

16:00

import SensitiveContentAnalysis

// 分析图片
let analyzer = SCSensitivityAnalyzer()
let policy = analyzer.analysisPolicy

// 通过文件 URL 分析
let imageResult = try await analyzer.analyzeImage(at: imageURL)

// 或通过 CGImage 分析
let cgImageResult = try await analyzer.analyzeImage(image.cgImage!)

// 分析视频
let handler = analyzer.videoAnalysis(forFileAt: videoURL)
let videoResult = try await handler.hasSensitiveContent()

if videoResult.isSensitive {
    // 根据 policy 决定干预方式
    intervene(based: policy)
}

func intervene(based policy: SCSensitivityAnalysisPolicy) {
    switch policy {
    case .communicationSafety:
        // 儿童保护模式:显示警告和资源
        showCommunicationSafetyIntervention()
    case .sensitiveContentWarning:
        // 敏感内容警告:模糊处理,用户可选择查看
        showSensitiveContentWarning()
    default:
        break
    }
}

关键点:

  • SCSensitivityAnalyzer 使用系统设备端 ML 模型
  • analysisPolicy 返回当前系统设置的策略类型
  • analyzeImage(at:)analyzeImage(_:) 分析静态图片
  • videoAnalysis(forFileAt:) 返回 handler,可以跟踪进度和取消
  • isSensitive 为 true 时,App 应提供干预 UI(模糊 + 可选查看)

Calendar Write-only 权限

08:30

import EventKit

let store = EKEventStore()

// iOS 17 新增 write-only 权限
do {
    let granted = try await store.requestWriteOnlyAccessToEvents()
    if granted {
        // 只能添加事件,不能读取
        let event = EKEvent(eventStore: store)
        event.title = "Team Meeting"
        event.startDate = Date()
        event.endDate = Date().addingTimeInterval(3600)
        try store.save(event, span: .thisEvent)
    }
} catch {
    print("Access denied: \(error)")
}

关键点:

  • requestWriteOnlyAccessToEvents() 是 iOS 17 新增
  • 只能创建新事件,不能读取、修改或删除现有事件
  • 已授权 App 升级到 iOS 17 后默认降级为 write-only
  • 需要完整访问时,系统会自动弹窗请求升级

CloudKit 端到端加密

22:15

import CloudKit

// 创建使用加密字段的记录
let record = CKRecord(recordType: "Note")

// 使用 encryptedValues API 写入加密数据
record.encryptedValues["title"] = "My Secret Note"
record.encryptedValues["content"] = "This is encrypted end-to-end"

// 保存到 CloudKit
let database = CKContainer.default().privateCloudDatabase
try await database.save(record)

// 读取时自动解密
let fetchedRecord = try await database.record(for: record.recordID)
let decryptedTitle = fetchedRecord.encryptedValues["title"] as? String
let decryptedContent = fetchedRecord.encryptedValues["content"] as? String

关键点:

  • 使用 encryptedValues API 而不是普通字段
  • CloudKit schema 中使用加密数据类型,如 EncryptedString
  • 用户开启 Advanced Data Protection 后自动获得端到端加密
  • 不需要管理密钥、加密操作或恢复流程

App Sandbox 数据保护(macOS)

<!-- Info.plist -->
<key>NSDataAccessSecurityPolicy</key>
<dict>
    <key>NSAllowList</key>
    <array>
        <string>com.mycompany.messenger</string>
        <string>com.mycompany.notes</string>
    </array>
</dict>

关键点:

  • 默认情况下,同一 Team ID 的 App 可以互相访问数据容器
  • NSDataAccessSecurityPolicy 可以替换为显式允许列表
  • 列表外的 App 访问数据时需要用户授权
  • 权限在 App 退出后重置

核心启发

1. 隐私优先的照片分享 App

  • 做什么:让用户选择特定照片分享给朋友,App 永远看不到其他照片
  • 为什么值得做:Photos Picker 完全嵌入且无需相册权限,用户信任度极高
  • 怎么开始:用 PhotosPicker 嵌入选择器,通过 PhotosPickerItem 加载选中内容,完全不走 PHPhotoLibrary

2. 安全的匿名用户反馈系统

  • 做什么:收集 App 使用统计和崩溃报告,但服务器无法关联到具体用户
  • 为什么值得做:OHTTP 把用户 IP 和内容分开,实现真正的匿名分析
  • 怎么开始:搭建 OHTTP 中继服务器,App 通过中继发送分析数据,服务器只收到脱敏后的统计信息

3. 儿童安全的社交 App

  • 做什么:在聊天或分享图片前自动检测敏感内容,保护未成年用户
  • 为什么值得做:SensitiveContentAnalysis 框架设备端处理,无需上传内容到服务器,符合隐私法规
  • 怎么开始:在图片发送前调用 analyzer.analyzeImage(),根据 analysisPolicy 显示适当的干预 UI

4. 零权限日程管理插件

  • 做什么:从网页或邮件中识别事件信息,一键添加到系统日历
  • 为什么值得做:write-only 日历权限让用户放心——App 只能添加不能偷看
  • 怎么开始:解析网页中的事件信息,用 requestWriteOnlyAccessToEvents() 添加事件,不需要读取用户现有日程

关联 Session

评论

GitHub Issues · utterances