其他
用 GroupActivities/SharePlay…
在 iOS/macOS/tvOS/visionOS 上通过 FaceTime、Messages、AirDrop 或 visionOS 附近共享构建同步的媒体播放、协作功能或自定义实时数据共享时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 38/100 ×0.15
适用场景
用 GroupActivities/Sh特定
解决思路
- 在 Xcode 中为 app target 添加 Group Activities capability,Xcode 会自动添加 entitlement 并更新 provisioning profile:<key>com.apple.developer.group-session</key><true/>;仅对 app target 配置,widgets、extensions、App Clips 不可用。
- 用 GroupStateObserver 检查是否有活跃的 FaceTime 通话或 Messages 会话:observer.isEligibleForGroupSession;并通过 for await isEligible in observer.$isEligibleForGroupSession.values 响应式观察变化来显示/隐藏 SharePlay 按钮。
- 定义 GroupActivity:遵循 GroupActivity 协议并提供 metadata(title、type、fallbackURL 等);activity 类型可选 .generic、.watchTogether、.listenTogether、.createTogether、.exploreTogether、.learnTogether、.readTogether、.shopTogether、.workoutTogether。
- GroupActivity 本身是 Codable,存储的数据必须可编码;仅在需要 SwiftUI ShareLink、AirDrop SharePlay 或 AppKit/UIKit 分享面板时才添加 Transferable;payload 保持最小,用标识符或 URL 而非大数据。
- 在长生命周期对象中监听会话:用 for await session in WatchTogetherActivity.sessions() 接收他人发起的活动,然后配置 session、创建 GroupSessionMessenger、观察 $state 和 $activeParticipants,最后调用 session.join()。
- 处理会话状态:.waiting(尚未加入)、.joined(已加入,加载活动)、.invalidated(reason:)(会话结束,执行清理);同时观察 activeParticipants 变化。
- 离开与结束:session?.leave() 让其他参与者继续;session?.end() 为所有参与者结束会话。
- 用 GroupSessionMessenger 同步小型、时间敏感的应用状态:消息必须 Codable,每条消息小于 256 KB;用 try await messenger.send(message, to: .all) 或 .only(participant) 发送;用 for await (message, context) in messenger.messages(of: SyncMessage.self) 接收,context.source 为发送者。
- 选择投递模式:.reliable(默认,有校验和重试)用于改变状态的操作如选择、回合;.unreliable(低延迟、无投递保证)用于高频临时数据如光标位置、绘画笔迹、反应。
- 媒体播放用 AVPlaybackCoordinator 协调:player.playbackCoordinator.coordinateWithSession(session);连接后 AVFoundation 自动同步播放/暂停、seek、速率、播放速度和时间;不要把 AVPlayer 传输字段放进 messenger 消息或 late-joiner 快照,自定义消息只用于播放之外的状态。
- 从 app 启动 SharePlay:调用 activity.prepareForActivation(),根据结果处理 .activationPreferred(有活跃会话且用户选择共享,调用 try await activity.activate())、.activationDisabled(用户选择本地播放或共享不可用,启动本地体验)、.cancelled。
- 当没有活跃会话(isEligibleForGroupSession 为 false)时,用 GroupActivitySharingController(activity) 让用户先选择联系人再展示。
- 自定义控件使用 shareplay SF Symbol;GroupActivityMetadata 作为发现文案:简洁的 title、subtitle、image 和与入口一致的 type;职责边界:GameKit 负责认证、匹配、排行榜、成就、语音/聊天;TabletopKit 负责座位、棋盘设备、空间摆放、回合、规则和权威桌面状态;AVKit 负责播放 UI;SharePlay 负责邀请、生命周期、参与者和协调交接。
- 大文件、非时间敏感附件用 GroupSessionJournal 而非 GroupSessionMessenger:journal items 必须遵循 Transferable,late joiner 也可获取,上限 100 MB,需要 iOS/iPadOS/tvOS 17+、macOS 14+ 或 visionOS 1+;用 try await journal.add(sharedImageItem) 上传,用 for await attachments in journal.attachments 观察并用 try await attachment.load(Data.self) 加载;更大或受保护的资源改为共享指针/manifest 并使用服务器存储或 app 管理的文件传输。
- 不要忘记在配置 session、messenger 和观察者后调用 session.join()。
- 不要忘记离开或结束会话:视图消失时调用 session?.leave() 并置空 session 和 messenger,避免会话泄漏。
- 不要假设所有参与者状态一致:在 handleParticipants 中计算新参与者(participants.subtracting(knownParticipants)),向每个新参与者单独发送完整当前状态。
- 不要用 SharePlay 传输大文件或受保护资源:messenger 用于小型时间敏感数据,journal 用于 Transferable 且不超过 100 MB,更大资源用服务器存储或 app 管理传输。
- 不要为媒体播放发送冗余消息:使用 AVPlaybackCoordinator 后直接 player.play() 即可自动同步,无需手动发送 PlayMessage。
- 不要在会被重建的视图中观察会话:sessions() 监听器应放在长生命周期 manager 中,而非 SwiftUI 视图 body;在 invalidation 时取消其子任务。
- 对照 Review Checklist 检查:capability 仅加在 app target;GroupActivity 是 Codable 且有意义的 metadata;使用 ShareLink/AirDrop/分享面板时添加 Transferable;sessions() 在长生命周期对象中观察;收到并配置 session 后调用 join();用户离开或关闭时调用 leave();messenger 消息小于 256 KB 且 deliveryMode 合适;late joiner 连接时收到当前状态;观察 $state 和 $activeParticipants;非时间敏感 Transferable 附件用 GroupSessionJournal;媒体同步用 AVPlaybackCoordinator;显示 SharePlay UI 前检查 isEligibleForGroupSession;无活跃会话时用 GroupActivitySharingController;会话失效时清理 messenger、journal 和任务。
适用边界 · 注意事项
- 仅适用于 Apple 平台(iOS、macOS、tvOS、visionOS)并使用 GroupActivities 框架,不适用于其他平台或非 Apple 生态。
- Group Activities 不可用于 widgets、extensions 或 App Clips,只能配置在 app target 上。
- GroupSessionJournal 需要 iOS/iPadOS/tvOS 17+、macOS 14+ 或 visionOS 1+,且附件上限 100 MB;更大或受保护资源需改用服务器存储或 app 管理传输。