Build custom swimming workouts with WorkoutKit
System Frameworks 进阶 20m

用 WorkoutKit 构建自定义游泳训练

Build custom swimming workouts with WorkoutKit

2024年6月10日

在 Apple 官方观看视频

一句话判断

WorkoutKit 终于在 watchOS 11 支持自定义游泳训练了,包括全新的”距离+时间”复合目标类型,这是游泳开发者等了整整一年的核心功能。

这场 Session 讲了什么

WorkoutKit 从 watchOS 10 推出以来,一直只能构建跑步、骑行等陆上训练的自定义方案。watchOS 11 补上了最大的一块短板——泳池游泳。所有之前自定义训练里的 steps、goals、alerts API,现在原封不动地适用于游泳活动类型。

除了游泳之外,Apple 还在这一年里陆续加了不少实用改进:watchOS 10.1 允许训练不再指定室内/外位置,交给用户自己决定;watchOS 10.4 给跑步和骑行新增了平均功率告警;watchOS 11 给室内跑步加了配速告警、给更多户外活动类型扩展了距离目标支持、给所有训练类型开放了自定义步骤名称。

这些改进组合起来,让 WorkoutKit 从”勉强能用”变成了一个比较完整的训练编排框架。

值得深挖的点

”距离+时间”复合目标:游泳训练的正确打开方式

游泳训练和跑步有一个根本区别:游泳者习惯用”在限定时间内完成指定距离”来衡量一组训练的强度。比如”1 分钟内游完 50 米”,距离和时间必须同时达标才算完成。

之前的 WorkoutKit 只有单一目标(单独的距离、时间或能量),无法表达这种训练逻辑。watchOS 11 引入了 poolSwimDistanceWithTime 目标类型,允许你在一个 step 上同时设置距离和时间目标,两个条件都满足后才会进入下一步。

这个设计的关键细节在于:如果距离先完成,界面上会显示一个勾号,表示正在等待时间目标。UI 上能看到”距离完成了,但时间还没到”的中间状态。这比简单地把两个独立目标串联在一起要合理得多——串联的话,距离完成后会有一个空的等待期,用户体验很差。

另一个贴心的细节是泳池长度适配。你在代码里用 25 米设计训练,但如果用户实际在一个 25 码的泳池里,Workout App 会自动按比例换算距离和时间,不会出现在泳池中间被强制换步的尴尬。

自定义步骤名称:小改动大影响

displayName 属性乍看只是给 step 起个名字,但它实际上解决了一个长期痛点:力量训练和 HIIT 训练的步骤之前只有”热身""工作""恢复”这种泛泛的标签,用户根本不知道当前这一步到底该做什么。

现在你可以写 "卧推 80kg × 8""波比跳 30 秒" 这样的具体描述。这个名称会出现在步骤切换时的全屏提示和滚动查看的详情视图中。对于做健身类 App 的开发者来说,这个改动虽然技术上微不足道,但在产品层面可能是这次更新里影响最大的一个。

代码片段

创建距离+时间的复合目标

场景:构建一组 50 米自由泳的间歇训练,每组限时 1 分钟。

// 距离 50 米,时间 1 分钟,两个目标同时设置
let distance = Measurement(value: 50, unit: UnitLength.meters)
let time = Duration.seconds(60)
let goal = WorkoutGoal(
    poolSwimDistanceWithTime: distance,
    time: time
)

坑:距离用 Measurement,时间用 Duration,两个不同的类型别搞混了。

构建完整的自定义游泳训练

场景:热身 200 米打腿板 + 6 组 50 米间歇 + 放松 200 米自由泳。

// 热身:8 趟 × 25m 打腿板
let warmupStep = WorkoutStep(.warmup)
warmupStep.displayName = "打腿板"
// 设置距离目标...

// 工作组:50m 自由泳 × 6 组
let workStep = WorkoutStep(.work, goal: goal)
workStep.displayName = "自由泳"
let interval = IntervalBlock(steps: [workStep], iterations: 6)

// 放松:8 趟 × 25m 自由泳
let cooldownStep = WorkoutStep(.cooldown)
cooldownStep.displayName = "自由泳放松"

// 组装游泳训练
let workout = CustomWorkout(
    activity: .swimming,
    displayName: "间歇训练",
    steps: [warmupStep, interval, cooldownStep]
)

// 调度到今天下午
WorkoutScheduler.shared.schedule(workout)

坑:swimming 活动类型是 pool swimming,open water swimming 是另一个类型,别选错。

检查目标是否被支持

场景:给户外活动设置距离目标前先检查兼容性。

// 检查目标+活动+位置的组合是否被支持
let supported = CustomWorkout.supportsGoal(
    .distance,
    activity: .outdoorRowing,
    location: .outdoor
)

坑:不是所有活动类型都支持所有目标,运行时检查比假设更安全。

最佳实践

新项目:如果你的 App 涉及任何训练调度功能,直接基于 watchOS 11 的新 API 设计。自定义步骤名称应该作为标配——不要让用户看到”Work Step 3”这种无意义的标签。游泳训练用 poolSwimDistanceWithTime 作为默认目标类型,这比单独的距离或时间更贴合游泳者的实际训练习惯。

已有项目:迁移路径很清晰。如果已经在用 WorkoutKit 的自定义训练 API,游泳支持的接入成本很低——API 结构完全复用。重点检查三件事:你的目标类型是否需要迁移到 poolSwimDistanceWithTime、是否需要给现有 step 补充 displayName、以及 supportsGoal 的运行时检查是否覆盖了新增的活动类型。用户可选位置的改动(不再强制 indoor/outdoor)建议也跟上,减少用户困惑。

还有什么值得关注

  • Apple Watch Ultra 在 watchOS 11 的游泳训练中新增了水温显示,对户外游泳场景很实用。
  • 户外划船和户外滑冰成为新的活动类型,距离目标对它们也开放了。
  • 所有自定义训练都有了刷新后的可视化目标视图,抬腕就能看到当前是否在目标范围内。
WWDC 2024