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
关键点:
- 使用
encryptedValuesAPI 而不是普通字段 - 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
- Discover Calendar and EventKit — 日历权限改进与 EventKit 集成
- Embed the Photos picker in your app — Photos Picker 嵌入详解
- What’s new in ScreenCaptureKit — 屏幕录制与共享
评论
GitHub Issues · utterances