示例代码均为匿名化改写,用于说明模式;不包含原项目标识或专属接口。
审查范围
本文根据一个匿名化的 SwiftUI 业务客户端案例整理,不包含产品名、公司名、服务端域名、仓库路径或接口地址。审查重点放在网络、登录、支付、定位、订单和测试边界;构建与自动化测试未运行。
总体评价
项目结构按应用基础能力、业务功能和通用界面组件划分,网络请求集中处理,共享状态有明确所有者。登录凭据使用系统安全存储;账号变更后通过会话标记丢弃过期响应;支付回跳后查询服务端状态;定位变化期间会合并请求并保留已有页面内容。这些设计能减少异步竞态和跨页面状态污染。
主要维护成本来自模块边界执行不一致:目录职责清楚,但仍在同一个应用 target 中;部分依赖可替换,部分功能直接访问共享实例;不同页面根据复杂度使用不同状态模式。与其强行统一所有 ViewModel,不如把有并发风险、外部依赖多或回归成本高的流程先切出可测试边界。
优先处理的风险
发布环境必须与构建类型一致
当前工作区存在一处环境配置与测试预期不一致的情况,Release 包有请求到测试服务的风险。应在发布前确认目标环境,并通过配置测试保护正式构建;不要让 Debug 偏好或临时地址影响 Release。
第三方跳转不应携带长期凭据
一条第三方页面跳转参数中带有可用于认证的凭据。凭据进入跨应用 URL 后,可能被客户端日志、页面路由或服务端访问日志记录。应确认权限、有效期和接收范围;优先改用短时、单次使用的交换凭证。
日志脱敏应覆盖定位请求头
网络请求会携带位置上下文。日志库的敏感头名单没有显式覆盖全部位置类请求头,虽然另一条应用日志路径做了字段脱敏,仍建议明确配置并检查实际日志输出。
可以把敏感头名单集中起来维护,避免不同日志入口各自遗漏:
struct NetworkLogPolicy {
let sensitiveHeaders: Set<String> = [
"authorization",
"cookie",
"x-location-latitude",
"x-location-longitude"
]
func shouldRedact(header name: String) -> Bool {
sensitiveHeaders.contains(name.lowercased())
}
}
客户端金额不能作为最终计价依据
客户端会根据条目单价、数量和附加费用展示总额。服务端需要重新核价并计算最终金额,避免过期价格或被修改的请求参数影响实际交易。
客户端签名材料不是服务器秘密
编译进客户端的签名材料可被提取,旧式摘要算法只能满足兼容协议,不能证明客户端可信。授权、幂等、限流和业务规则必须由服务端执行。
环境选择应由 Build Configuration 等构建配置决定,不靠发版前手改同一份 plist;可在 CI 中检查 Release 配置,防止测试地址进入正式包。审查结论再按“发布阻断、近期修复、长期优化”排序,便于团队逐项落实。
保留并加强的设计
- 会话变化与请求身份一并校验,避免旧账号结果覆盖新账号。
- 支付记录只保留恢复对账所需的引用信息。
- 订单动作规则集中管理,并用纯逻辑测试覆盖状态差异。
- 定位、地图搜索和详情加载均处理过期响应。
- 网络传输可注入替身,便于检查编码和错误映射。
结论
这类工程适合渐进式演进:先处理发布环境和第三方凭据边界,再补充日志脱敏与金额校验保障,随后提升模块边界和测试诊断能力。本文只呈现匿名化后的工程经验,不包含可定位到原项目的名称、路径、域名或接口信息。
