Read the feature's view and the code that does the work (the view model, the
function, the
network call), plus any shared loading, error or freshness views and modifiers the app already has.
First list what starts the work, and every place it can start from (a button and a cell can both
add the same thing, and two entry points are how races happen). A user action (a tap on Save) is
one trigger. A screen load often
has several: opening the screen, coming back to the app, a timer, changing a filter, pull to refresh,
a Refresh or Try again button. Feedback differs by trigger, so keep them apart.
Then list every state the feature can actually reach, not just the ones it draws today:
- Working, nothing to show yet: the first load, or a user action in progress. Note how long it
can take: under a second, a few seconds, or longer.
- Working, saved data on screen: showing what was there before while fresh data loads. For any
data screen this is the most important state.
- Success: it worked. Is there a next step, or does the user stay put?
- Partial success: the main thing worked but a side effect failed (saved, but not exported).
- Empty: it worked but there is nothing to show.
- Error: it failed. Which errors are possible (offline, server, validation, permission), and is
saved data still on screen?
- Retry: what happens when they try again, and when that fails too.
- Interrupted: they leave, background the app, change the filter, or tap again while it works.
States inside a system screen you cannot restyle (the photo picker, the camera, a share sheet) are
out of scope: design what happens after it closes.
If the action replaces or deletes something, check in the code that the old data survives a failure
(is it removed before the new data is safely saved?). Losing data is worse than any missing state.
For each, note the file and line and what the user sees today. Note feedback code that can never run
(an overlay inside a sheet that is already closed) so it can be removed. Also check that every promise in the
words is true in the code: "we will refresh when you are back online" needs something that watches
the network. If you cannot tell a state's look without running the app, say "not verified".
If a state is already handled well, mark it Keep and move on. The report is about the gaps.
阅读该功能的视图和执行工作的代码(view model、
函数、网络调用),以及 app 已有的共享加载、错误或数据新鲜度视图及 modifier。
首先列出是什么启动这项工作,以及它可能从哪些位置启动(按钮和 cell 都能添加同一种内容,而两个入口正是竞态发生的原因)。用户操作(点击
)是一种触发方式。屏幕加载通常有多种触发方式:打开屏幕、返回 app、计时器、更改筛选器、下拉刷新、
或
按钮。反馈因触发方式而异,所以应将它们分开。
然后列出该功能实际可能到达的所有状态,而不只是当前已经绘制出来的状态:
- 执行中,尚无内容可显示:首次加载,或用户操作正在执行。记录可能耗时多久:不到一秒、几秒,还是更久。
- 执行中,屏幕上已有保存的数据:在加载新数据时显示之前已有的内容。对于任何数据屏幕,这是最重要的状态。
- 成功:操作成功。是否还有下一步,还是用户可以停留在当前界面?
- 部分成功:主要操作成功,但某个附带操作失败(已保存,但未导出)。
- 空状态:操作成功,但没有内容可显示。
- 错误:操作失败。可能出现哪些错误(离线、服务器、验证、权限),屏幕上是否仍保留已保存的数据?
- 重试:用户再次尝试时会发生什么,如果再次失败又会怎样?
- 中断:用户离开、将 app 切到后台、更改筛选器,或在工作进行期间再次点击。
无法重新设计样式的系统屏幕内部状态不在范围内,例如照片选择器、相机、分享面板。只需设计它们关闭后发生什么。
如果操作会替换或删除内容,检查代码中旧数据能否在失败后保留(是否在新数据安全保存前就将其删除?)。丢失数据比缺少任何状态都严重。
对每种状态,记录对应文件和行号,以及用户目前看到的内容。标出永远无法执行的反馈代码,例如已经关闭的 sheet 内的 overlay,以便将其删除。也要检查文案中的每个承诺是否符合代码事实:“恢复网络后我们会刷新”需要相应的网络监听实现。如果不运行 app 就无法确定某个状态的外观,请写明“未验证”。
如果某个状态已经处理妥当,请标记为 Keep 并继续检查。报告应重点说明存在的差距。