Highlight
iOS 17 新增的
GroupSessionJournalAPI 让 SharePlay 应用可以传输最大 100MB 的附件(图片、PDF、语音等),利用 Apple 云端基础设施实现端到端加密的快速传输,并自动处理迟到者的状态同步。
核心内容
GroupSessionMessenger 的局限
SharePlay 应用之前用 GroupSessionMessenger 同步状态,但它只适合传输小型消息数据。想象一下协作画图应用:画笔轨迹可以走 Messenger,但用户往画布上拖了一张照片,这条路就走不通了。
(00:58)GroupSessionJournal 填补了这个空白。它使用与 FaceTime 相同的 Apple 云端基础设施,多层技术叠加减少传输数据量和等待时间,而且全程端到端加密。
Journal 的工作方式
(02:14)GroupSessionJournal 绑定到一个 GroupSession,所有参与者共享同一个 Journal 实例。一个人在本地调用 add,所有人的 attachments AsyncSequence 都会收到新附件事件;调用 remove,所有人同步移除。
附件类型只需遵循 Transferable 协议,系统会处理剩下的传输逻辑。
两个关键设计约束
(03:37)附件大小上限 100MB。这迫使开发者区分内容类型:用户生成的照片、PDF、语音备忘录适合走 Journal;电影等大文件应该走应用自己的服务器。
(04:18)生命周期与 GroupSession 绑定。只要还有人在线,附件就保留;所有人都离开后自动清除。这意味着重要数据需要在 Session 结束前持久化到本地。
迟到者问题自动解决
(04:47)以前新加入的人需要其他参与者重新发送状态数据。如果状态里包含大附件,每个人都得重新上传一遍。Journal 现在自动把已有附件同步给新加入者,不需要其他人重新上传。
详细内容
在 DrawTogether 中集成 Journal
Session 以 DrawTogether 应用为例,演示了四步集成:
第一步:定义数据模型
创建 CanvasImage 结构体来跟踪 Journal 中的图片:
struct CanvasImage: Identifiable {
let id: UUID
let image: UIImage
var position: CGPoint
}
第二步:在 Canvas 模型中创建 Journal
class Canvas: ObservableObject {
@Published var images: [CanvasImage] = []
@Published var selectedImageData: Data?
private var journal: GroupSessionJournal?
func configureGroupSession(_ session: GroupSession<DrawTogether>) {
// 创建 Journal
journal = GroupSessionJournal(session: session)
// 监听附件变化
Task {
for await attachment in journal!.attachments {
await handleAttachment(attachment)
}
}
}
func addImageToJournal(_ imageData: Data) {
guard let journal = journal else { return }
// 创建 Transferable 附件并上传
let attachment = TransferableAttachment(data: imageData, type: .image)
journal.add(attachment)
}
}
关键点:
GroupSessionJournal(session:)用 GroupSession 初始化attachments是 AsyncSequence,用for await监听新附件add()上传附件后,所有参与者都会收到
第三步:添加图片选择按钮
import PhotosUI
struct ControlBar: View {
@ObservedObject var canvas: Canvas
var body: some View {
HStack {
// 其他按钮...
PhotosPicker(selection: $canvas.selectedImageData,
matching: .images) {
Image(systemName: "photo")
}
}
}
}
第四步:在画布上显示图片
struct CanvasView: View {
@ObservedObject var canvas: Canvas
var body: some View {
ZStack {
// 绘制笔画...
// 显示所有图片
ForEach(canvas.images) { canvasImage in
Image(uiImage: canvasImage.image)
.position(canvasImage.position)
.gesture(
DragGesture()
.onChanged { value in
canvasImage.position = value.location
}
)
}
}
}
}
关键点:
PhotosPicker是 SwiftUI 内置组件,iOS 16+ 可用- 图片位置用
DragGesture实现拖拽 - 所有图片状态通过 Journal 同步给所有参与者
附件处理流程
func handleAttachment(_ attachment: GroupSessionJournal.Attachment) async {
do {
let data = try await attachment.load(Data.self)
if let image = UIImage(data: data) {
let canvasImage = CanvasImage(
id: attachment.id,
image: image,
position: .center
)
await MainActor.run {
images.append(canvasImage)
}
}
} catch {
print("Failed to load attachment: \(error)")
}
}
关键点:
attachment.load(Data.self)异步加载附件数据- 加载完成后切回主线程更新 UI
- 附件 ID 可用于后续移除操作
核心启发
-
做带图片协作的白板应用
- 做什么:多人可以在同一块画布上贴图、标注、讨论
- 为什么值得做:Journal 自动处理图片传输和同步,开发者只需要关注画布交互逻辑
- 怎么开始:用
GroupSessionJournal传输图片附件,用GroupSessionMessenger同步画笔轨迹
-
做共享相册的聚会应用
- 做什么:一群人在活动或旅行中实时共享照片,所有人都能看到彼此刚拍的照片
- 为什么值得做:Journal 的端到端加密和自动同步让照片共享既安全又即时
- 怎么开始:照片选择后用
journal.add()上传,监听attachments序列接收他人照片
-
做协作标注的文档审阅工具
- 做什么:团队成员一起审阅 PDF 文档,各自添加批注和高亮
- 为什么值得做:PDF 文件通常小于 100MB,适合走 Journal;批注数据走 Messenger,两者结合实现完整体验
- 怎么开始:PDF 文件作为 Journal 附件传输,批注坐标和文本通过 Messenger 实时同步
-
做共享播放列表的音乐应用
- 做什么:一群人共同编辑一个播放列表,每个人都可以添加歌曲
- 为什么值得做:虽然音频文件本身不走 Journal,但专辑封面、用户备注等元数据可以走 Journal 快速同步
- 怎么开始:用 Journal 同步封面图片和播放列表变更,用 Messenger 同步播放状态
关联 Session
- Add SharePlay to your app — SharePlay 入门,了解 GroupActivities 框架基础
- Build custom experiences with GroupActivities — 使用 GroupSessionMessenger 构建自定义共享体验
- Make a great SharePlay experience — SharePlay 体验设计最佳实践
评论
GitHub Issues · utterances