公司动态
从AI辅助到硬件联动:用Grok 4.6开发Apple Watch水温监测应用
这两天有一条开发圈的新闻很有意思Elon Musk 转发了一个用户做的 Apple Watch 应用用途非常生活化——监测宝宝洗澡水温而开发过程据说大量使用了 Grok 4.6 辅助完成。先说一下我的判断这个案例能引起关注不是因为代码本身有多难也不是单纯因为某个 AI 工具有多强而是它把“从生活痛点到可穿戴应用”的路径极大缩短了。过去一个没有 iOS 开发经验的普通用户想做一款 Apple Watch 应用要先迈过 Mac、Xcode、开发者账号、SwiftUI、传感器数据接入等一堆门槛而现在AI 助手可以把其中很大一部分“工具链知识”代劳掉。所以本文不打算只做吃瓜式解读而是把这个案例拆开看这类“AI 辅助开发的硬件联动应用”真实的技术链路是什么Apple Watch 能不能直接测水温如果想自己复刻一个最小原型代码上应该怎么组织真正决定成败的又是什么如果你最近也在关注 Grok 4.6、AI 编程助手或 watchOS 开发这篇文章会给你一个相对完整的参考。1. 为什么一个“婴儿洗澡水温应用”值得被围观先还原一下场景。家里有婴儿的人都知道给婴儿洗澡时水温是一件很敏感的事。成年人觉得“温热刚好”的水对婴儿来说可能已经偏烫而婴儿皮肤对温度的调节能力又不如成人一旦水温不适轻则哭闹重则存在烫伤或着凉的风险。传统做法是什么家长用手腕内侧试温或者买一个普通水温计丢在澡盆里然后一边抱着孩子一边低头看温度计。这个过程不是不行但确实不方便尤其在一个人带娃、另一个手还要托着婴儿头部的时候。如果在 Apple Watch 上做一个应用能持续显示澡盆里的实时水温并在水温过高或过低时通过震动提醒家长这个体验就会好很多。但问题在于谁来做放在五年前这个需求大概率只能停留在“想法”阶段。因为 Apple Watch 应用不是网页或小程序它需要原生开发环境需要处理 watchOS 的界面约束、后台运行机制、传感器数据来源还要考虑真机调试和签名。一个白天上班、晚上带娃的普通用户很难有时间把 SwiftUI 和 CoreBluetooth 从头学一遍。这个案例真正值得围观的地方是它展示了一种新分工想法仍然来自用户技术细节可以交给 AI。从需求描述、生成代码框架、补齐界面到调试错误信息Grok 4.6 这类对话式编程助手可以承担大量“翻译”工作——把自然语言翻译成 watchOS 工程结构把“水温太高就提醒我”翻译成温度阈值判断代码。对没有编程背景的用户来说这一步是质变。当然围观归围观要判断这个案例是不是真的有参考价值需要从三点来看第一它有没有解决一个真实高频场景中的痛点答案是有的婴儿洗澡水温监测是很多家庭都会遇到的问题。第二它的技术链路是否可复制答案是需要具体分析Apple Watch 本身并不能直接测澡盆水温这里面还有传感器和连接方式的问题。第三它的工程边界在哪里AI 生成的代码能跑通 UI 演示不等于它能可靠地在真实浴盆环境中工作。这篇文章的后续内容就围绕这三点展开。2. 先划清概念边界Apple Watch 不能直接测量洗澡水温很多人看到“Apple Watch 水温监测应用”第一反应是手表不是有温度传感器吗直接放水里不就行了这个直觉需要纠正。Apple Watch 内部的温度相关传感器主要用于测量佩戴者的手腕温度、辅助生理周期跟踪以及监测设备自身发热状态。它设计的对象是人体不是液体。虽然部分 Apple Watch 型号支持游泳场景防水等级也足够应付日常洗手或游泳但这不代表开发者可以顺手调用一个“水温传感器”接口来读取澡盆温度。从 watchOS 公开的传感器能力来看并没有一个标准 API 能直接返回“当前浸泡液体的温度值”。如果你只是在 App Store 看到一个应用号称能测水温它更可能是在读取外部设备的数据或者只提供了手动输入和粗略估算功能。那么在工程上更稳妥的做法是什么通常是这样一条链路澡盆中的蓝牙水温计 → BLE 数据上报 → Apple Watch / iPhone 接收 → 应用判断温度区间 → 震动/声音/通知提醒这里最关键的物理测量环节通常交给一个独立的防水蓝牙温度传感器。它可能是一个很小的浮漂式温度计也可以是带探头的婴儿浴盆专用测温设备。Apple Watch 应用负责的是接收数据、展示数值、判断阈值并发提醒。也就是说手表端 app 是“接收端”和“提醒端”不是“测量端”。把这条边界划清楚非常重要否则你可能会被演示效果误导看到手表上显示 37.2℃就以为手表自己感知到了水温。实际上如果没有外部传感器或可靠的数据源这个数字就是无源之水。尤其在做技术分析时一定要先判断“数据从哪里来”再看“界面怎么渲染”和“提醒怎么触发”。后者是 AI 比较擅长生成的代码但前者决定了产品能不能真正落地。从材料看这个事件里最受关注的是应用创意和 AI 开发过程并没有展示完整的外设数据链路细节。因此在复刻时我会优先采用模拟数据源加外部传感器替换的思路先把应用逻辑跑通再接入真实硬件。这也是一个比较安全、不容易走弯路的做法。3. 把一个“生活小应用”拆成四个工程模块如果不考虑营销叙事只看工程实现这个婴儿洗澡水温应用其实可以拆成四个非常清晰的模块模块职责风险点温度采集通过外部蓝牙传感器拿到水温数据硬件兼容性、连接稳定性、防水状态判断根据温度区间判断“偏冷/合适/偏热”安全阈值设置是否合理界面展示在手表屏幕上显示当前温度与状态小屏信息密度、可读性提醒通知温度越界时触发震动或声音通知权限、后台状态这四个模块里真正复杂的不在 AI 生成的那部分而在采集链路。先说状态判断。婴儿洗澡水的安全温度网上能查到各种说法比较常见的操作参考范围大约在 36℃ 到 38℃ 之间很多育儿建议会强调 37℃ 左右比较舒适超过 40℃ 就需要警惕。需要特别说明的是这属于生活经验层面的参考值不同季节、不同室温、不同婴儿体质都会有差异不能把它当作严格的医学标准。代码实现时更好的方式是不要把阈值写死而是让用户可以在设置里调整比如下限 35℃、上限 39℃。这样既灵活也避免“一刀切”带来的误判。再说界面展示。Apple Watch 的屏幕非常小不适合放复杂的曲线图或大段文字。最优的交互就是“一眼看懂”一个大数字显示当前温度背景颜色随状态变化绿色代表正常红色代表偏高蓝色代表偏低。如果温度越界再配合震动和声音提醒。这正好是 Grok 这类 AI 编码助手最容易生成的部分因为 SwiftUI 的代码结构很模式化界面也不复杂。最后是提醒通知。watchOS 应用要实现可靠提醒不能只看应用是否在前台。如果用户切到别的应用或者手表处于息屏状态温度越界时能不能及时弹通知取决于你有没有申请通知权限以及是否通过合适的框架发送本地通知。这是新手最容易忽略的一个点模拟器里跑通界面不等于真机上能收到提醒。4. Grok 4.6 这类工具在开发流程中的真实作用很多讨论把焦点放在“Grok 4.6 能不能写代码”上我觉得这个讨论维度太窄了。从这次案例看Grok 4.6 的真实作用是承接“模糊需求”和“结构化代码”之间的翻译工作。用户不一定懂 watchOS 的视图生命周期也不一定了解 BLE 连接该用哪个框架但他能描述清楚“宝宝洗澡时水太烫要提醒我”。AI 要做的是把这个描述拆解成可以落地的代码模块。一个典型的 AI 辅助开发流程大致可以分成四步需求描述用户告诉 AI我要做一个 Apple Watch 应用监测婴儿洗澡水温显示当前温度太热或太冷时提醒。基础脚手架生成AI 生成 watchOS App 的基本结构包括入口、主界面、温度模型。迭代修改用户把遇到的编译错误、界面样式问题反馈给 AI让它继续修改。真机验证用户把代码跑到真机上发现实际效果和预期不一致时再让 AI 调整。在这个流程里要注意AI 生成代码的速度确实很快但如果你没有基本的代码阅读能力就很容易被表面正确的代码误导。比如 AI 可能生成一个定时器每秒钟把温度数值随机加减 0.1℃从界面看是一个“动态温度”但它并不是真实传感器数据。又比如 AI 可能把通知代码写在主界面里忽略了用户切到后台后的生命周期。这些问题靠 AI 自己是看不出来的它只会根据你当前给的上下文生成“看起来正确”的代码。我并不是说 AI 辅助开发不可靠而是想说清楚它的边界AI 擅长生成代码片段、解释报错、补全结构但它不负责验证物理世界的真实性也不负责做产品决策。如果你能理解每一段生成代码在做什么AI 就是效率放大器如果完全黑盒使用那它生成的可能只是一个“演示级原型”。这也是这个案例最有意思的地方它给普通用户展示了 AI 的可能性也给开发者提了一个醒——懂工程判断的人才能真正用好这些工具。5. 动手复刻一个最小可运行的原型这一节我们回到能落地的事情上。虽然我无法拿到事件中那个应用的真实源码但从技术角度完全可以自己搭一个结构相近的最小原型。下面这套设计既可以用来理解案例里的应用是怎么工作的也可以作为你练习 Grok、Claude、Copilot 等 AI 编程助手的实验工程。需要说明的是这里使用模拟温度源真实水温测量需要额外接入蓝牙温度计不展开硬件细节。5.1 准备环境与工程结构本文示例基于 Apple 官方推荐的 watchOS SwiftUI 开发方式环境要求如下一台安装 Xcode 的 Mac版本以你实际安装为准。在 Xcode 中新建一个 watchOS App 工程语言选择 Swift界面框架选择 SwiftUI。本文代码是核心逻辑不是完整工程截图你需要根据 Xcode 生成的模板把下面的文件放到对应位置。在实际开发中你当然可以打开 Grok 4.6 或同类助手用对话方式生成基础脚手架。但既然要理解原理我们先手写一遍再看哪些环节可以交给 AI。5.2 定义安全温度区间与状态判断温度判断是整个应用的核心逻辑。建议不要把这个逻辑散落在界面代码里而是独立成一个模型文件方便测试和修改。// 文件路径WaterSafety.swift import Foundation enum WaterSafety { /// 默认温度下限单位摄氏度 static let defaultLower: Double 36.0 /// 默认温度上限单位摄氏度 static let defaultUpper: Double 38.0 enum Level { case unknown case tooCold case normal case tooHot } static func level( for temperature: Double?, lower: Double WaterSafety.defaultLower, upper: Double WaterSafety.defaultUpper ) - Level { guard let temperature else { return .unknown } if temperature lower { return .tooCold } if temperature upper { return .tooHot } return .normal } }这里的lower和upper不是写死的魔数而是作为参数传入。后续如果需要做用户设置可以直接把这两个值从设置页读取出来从而避免改代码。默认的 36℃ 和 38℃ 只是演示区间真实使用时请按具体育儿建议和环境调整。5.3 定义数据源协议与模拟数据为了不依赖硬件也能调试 UI我们抽象一个WaterTemperatureSource协议。真实硬件接入时只要实现同一个协议界面的其他部分不用改。这是工程上的一个经典手法面向协议编程而不是把某一种硬件实现绑死在界面上。// 文件路径WaterTemperatureSource.swift import Foundation import Combine /// 水温数据源协议 protocol WaterTemperatureSource: AnyObject { /// 发送当前水温nil 表示暂时没有有效数据 var currentTemperaturePublisher: AnyPublisherDouble?, Never { get } } /// 模拟数据源用于开发和 UI 调试 final class SimulatedTemperatureSource: WaterTemperatureSource { private let subject CurrentValueSubjectDouble?, Never(nil) private var timer: Timer? private var temperature: Double 37.0 var currentTemperaturePublisher: AnyPublisherDouble?, Never { subject.eraseToAnyPublisher() } func start() { // 每 1 秒产生一次小幅波动模拟真实温度变化 timer Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in guard let self else { return } let delta Double.random(in: -0.05...0.05) self.temperature delta self.subject.send(self.temperature) } } func stop() { timer?.invalidate() timer nil } }在实际的项目中如果接入了真实蓝牙温度计你只需要创建一个新的类比如BLETemperatureSource也实现WaterTemperatureSource协议即可。界面和状态判断代码都不需要改动。这个设计思路同样适用于温度之外的其他传感器数据源很多 AI 生成的代码不一定能意识到这一层抽象但尽早做抽象能显著降低后续切换硬件的成本。5.4 实现 ViewModel 与状态订阅ViewModel 的作用是把数据源产生的温度值转换成界面可以展示的状态。在 SwiftUI watchOS 的环境里使用ObservableObject和Published是很常见的组合。// 文件路径WaterTempMonitorViewModel.swift import SwiftUI import Combine final class WaterTempMonitorViewModel: ObservableObject { /// 当前温度 Published var temperature: Double? /// 界面提示文案 Published var statusText: String 等待读数 private let source: WaterTemperatureSource private var cancellables SetAnyCancellable() init(source: WaterTemperatureSource) { self.source source source.currentTemperaturePublisher .receive(on: DispatchQueue.main) .sink { [weak self] temperature in guard let self else { return } self.temperature temperature self.updateStatus(temperature: temperature) } .store(in: cancellables) } private func updateStatus(temperature: Double?) { switch WaterSafety.level(for: temperature) { case .unknown: statusText 等待读数 case .tooCold: statusText 水温偏低 case .normal: statusText 水温合适 case .tooHot: statusText 水温偏高 } } var displayTemperatureText: String { guard let temperature else { return --.- } return String(format: %.1f, temperature) } var color: Color { switch WaterSafety.level(for: temperature) { case .unknown: return .gray case .tooCold: return .blue case .normal: return .green case .tooHot: return .red } } }这里有两点值得注意receive(on: DispatchQueue.main)保证 UI 更新一定发生在主线程这是 SwiftUI 开发中很常规也容易踩坑的点。color由状态计算出来而不是在外层用 if 判断后再赋值这样代码更集中便于维护。在代码里温度越界的触发逻辑目前只改变了文字和颜色。如果要加入震动或声音提醒可以在tooCold和tooHot分支中触发WKInterfaceDevice或本地通知。这部分请根据你的 watchOS 部署版本选择合适的 API并记得申请通知权限。5.5 主界面与 App 入口接下来写一个简单的主界面。Apple Watch 屏幕窄所以界面层级要尽量简单顶部是大号温度数字下面是状态文字用背景色或文字色作为状态提示。// 文件路径WaterTempMonitorView.swift import SwiftUI struct WaterTempMonitorView: View { StateObject var viewModel: WaterTempMonitorViewModel var body: some View { VStack(spacing: 8) { Text(viewModel.displayTemperatureText) .font(.system(size: 52, weight: .bold, design: .rounded)) .foregroundColor(viewModel.color) Text(℃) .font(.footnote) .foregroundColor(.secondary) Text(viewModel.statusText) .font(.body) } .padding() } }// 文件路径BabyBathWaterApp.swift import SwiftUI main struct BabyBathWaterApp: App { var body: some Scene { WindowGroup { let source SimulatedTemperatureSource() WaterTempMonitorView( viewModel: WaterTempMonitorViewModel(source: source) ) .onAppear { source.start() } } } }上面的代码中我直接在App入口创建了SimulatedTemperatureSource并在界面出现时启动模拟数据。这样在模拟器上就能看到温度数字在小范围内变化UI 状态也会随之切换。当你接入真实蓝牙传感器时只需要替换let source SimulatedTemperatureSource()为对应的真实数据源实例。这里不修改任何 UI 代码也是前面做协议抽象的主要目的。6. 运行验证与常见问题排查工程写完后需要验证它是否真的能跑起来。我建议按下面的顺序做验证在 Xcode 中选择模拟器运行 watch App确认能够看到主界面。检查温度数字是否在 36.5℃ 到 37.5℃ 之间波动。手动调整模拟数据范围比如把SimulatedTemperatureSource中的初始温度改成 39.5℃确认界面变红并显示“水温偏高”。再改成 35℃确认界面变蓝并显示“水温偏低”。如果项目在编译阶段报错优先检查两个地方一是部署版本是否支持Timer.scheduledTimer和StateObject二是 target 是否选择了 watchOS App。Xcode 自动生成的模板一般不会有问题但如果是从 iOS App 工程改造过来很容易出现“framework not found”或“watch app has no main entry”的错误。问题现象可能原因排查方向解决方案温度数字不变化计时器没有启动或数据源没有被持有查看 onAppear 是否执行确认模拟源生命周期把 source 存为属性而不是局部变量界面始终显示“等待读数”数据源没有发送有效值检查 subject 是否有 send 调用在 start 方法里先 send 一次当前值编译报错 Unknown classApp 入口或 target 配置错误检查 target membership确认文件都属于 watchOS App target字体或间距不适配小屏没有考虑 watch 屏幕尺寸在预览中切换不同尺寸减少内边距使用自适应字体真机上看不到应用签名或安装问题查看 Xcode 设备日志重新配对 Apple Watch检查开发者签名这里特别提醒一个容易忽略的问题如果模拟器已经运行过一次修改代码后直接 Run偶尔会出现界面没更新的情况。比较稳妥的做法是先把 App 从模拟器删除再重新 Run。类似的缓存问题在 Apple Watch 真机上更常见不要让 AI 生成的代码背锅先把环境清干净再排查。7. 从 AI 辅助开发到可用产品还差这几步代码能跑通UI 能展示只是完成了 10%。从“演示原型”到“真实使用”中间还有一个巨大的工程鸿沟这里帮大家梳理几条关键建议。第一设备防水与佩戴安全要放在最前面。给婴儿洗澡时水和蒸汽几乎无处不在Apple Watch 本身虽然支持游泳场景但第三方外接温度计的防水等级参差不齐。建议选择有明确防水标识的婴儿浴盆温度计并仔细阅读工作温度范围。不要为了“省事”把普通非防水设备丢进澡盆。第二报警机制必须有多层冗余。水温过高是安全风险如果只依赖 Apple Watch 的震动提醒而家长刚好把通知关了那这个应用就形同虚设。建议同时考虑手表震动、声音提示以及手机端本地通知。更稳妥的是在澡盆附近放一个独立的机械温度计作为参照应用作为辅助手段而不建议作为唯一判断依据。第三注意应用与真实传感器之间的延迟和断连。蓝牙设备靠近水面时信号传输非常容易受干扰一旦连接断开手表显示的可能是最后一次有效读数而不是实时值。这一点需要在 UI 上明确暴露“连接状态”否则家长看到 37℃ 可能误以为已经加热完成而实际上当前水温已经升到 40℃ 以上了。一个最小实现也应该在数据源协议中加上“delta 时间超过 5 秒就视为失效”的逻辑。第四所有“AI 生成 人工采纳”的代码都应该走审查流程。让 AI 生成代码没问题但代码审查人必须存在。尤其是涉及安全场景的代码建议把阈值、报警逻辑、失败处理单独抽出来用核心逻辑尽可能简单不要把它藏在几十行 SwiftUI 布局代码里。一旦出了问题排查范围越小越容易定位。第五理解这类应用的定位。它不是医疗器械也不应承担医疗判断责任。水温监测应用的价值在于提供参考数据和及时提醒最终决策仍然是家长自己。不建议通过夸大宣传把它描述成“防烫伤系统”否则一旦发生误报或漏报产品口碑和法律责任都会成为问题。8. 这个案例给开发者留下的三个提醒回到文章开头的事件。我不打算把 Grok 4.6 或 Apple Watch 捧成什么“颠覆性组合”但这次传播有一个值得记住的观察点AI 正在把以前需要“团队协作数月”才能完成的应用原型压缩成“一个人一个周末”的尝试。这个压缩过程中成本下降最快的是 UI 脚手架和常见业务逻辑代码而成本几乎不变的是你对物理世界的理解、对安全边界的判断以及对异常情况的处理能力。如果你也想试试这条路建议从一件小事开始找一个自己每天都会遇到的痛点用自然语言把需求描述清楚让 Grok 或同类工具先帮你生成第一版代码然后一项一项验证数据流。一次不用做太多先把“温度显示”跑通再考虑“蓝牙接入”再处理“通知提醒”。真正动手跑一遍比看一百篇 AI 编程的讨论都有价值。更重要的是理解“端到端”三个字的分量会调用 AI 生成代码的人越来越多但能把一个想法从自然语言变成可运行、可维护、可安全交付的产品这种工程能力在未来反而会更加值钱。AI 负责把代码生成的成本打下来但你要负责把“能用”变成“可靠”。这个案例里最值得学习的不是某个应用本身而是这套新的开发协作方式——以及它在真实场景中的边界。