示例代码均为匿名化改写,用于说明模式;数据类型和错误呈现可按页面需求扩展。
详情页通常组合多种数据
资料页可能同时加载主资料、相关条目、收藏状态、相册与分享信息。若其中一个辅助列表加载失败,不应让整页都变为空白;主资料和次级区块可以分别拥有加载与错误状态。
将外部请求作为可替换依赖注入后,测试可以控制返回时间。主资料与项目列表可以并行请求,但每次加载都要记录请求代次。用户切换到另一条资料后,旧响应即使晚到也不能覆盖新内容。
快速切换资料时,请求代次可以阻止旧结果覆盖新状态;取消仍是协作式的,底层请求应检查 Task.isCancelled 并尽早退出。
导航返回与重复加载
@MainActor
final class DetailModel<Profile: Sendable, Related: Sendable> {
private(set) var profile: Profile?
private(set) var related: [Related] = []
private var generation = 0
func load(
id: UUID,
fetchProfile: @Sendable (UUID) async throws -> Profile,
fetchRelated: @Sendable (UUID) async throws -> [Related]
) async throws {
generation += 1
let requestGeneration = generation
async let loadedProfile = fetchProfile(id)
async let loadedRelated = fetchRelated(id)
let (nextProfile, nextRelated) = try await (loadedProfile, loadedRelated)
guard generation == requestGeneration else { return }
profile = nextProfile
related = nextRelated
}
}
还要区分系统重建视图和用户主动刷新:前者可以复用已有快照,后者应创建新请求代次,避免把过期数据当成刷新结果。
SwiftUI 的导航返回可能再次触发页面任务。如果相同资料已经完整加载,可以复用现有状态,避免页面跳闪和滚动位置重置;用户主动重试仍应重新加载。
完成标记只能在所有必要请求结束后更新。请求取消或主要资料失败时,不能错误地把页面标记为已完整加载。
路由状态和业务数据分开
详情页的弹层、子页面目标、确认框属于暂态路由状态;主资料和操作结果属于业务状态。把两者分开放置,可减少大型视图的条件分支,也让页面流程测试不需要构造完整 UI。
异步回归检查
- 新请求先完成、旧请求后完成时,页面保留新数据。
- 切换账号后旧资料不会继续显示。
- 从子页面返回不无故清空已加载内容。
- 显式重试能重新执行失败的数据请求。
