API数据按今日/昨日分类的方案评估及优化咨询
问题描述
从API获取包含创建日期的数据,原实现是通过isMoreThan24HoursAgo函数判断创建时间是否超过24小时,在View的ForEach循环中逐个判断数据项,区分“今日”“昨日”分类展示。但存在两个问题:
- 重复判断:每个数据项在两个分类模块中都要被判断一次
- 空提示重复:空提示文本会随数据项数量重复显示
附现有代码:
struct TestView: View { @StateObject var viewModel: ViewModel = ViewModel() var body: some View { VStack { VStack(alignment: .leading) { HStack { Text("Today") .font(.ubuntu(name: .ubuntuBold, size: 14)) .foregroundColor(Color.gray.opacity(0.6)) .padding(.vertical, 15) Spacer() } ScrollView(showsIndicators: false) { if let history = viewModel.history?.data?.data { ForEach(history.prefix(2), id: \.self) { item in if !viewModel.isMoreThan24HoursAgo(item.createdAt ?? "N/A") { HistoryCard(historyModel: item) } else { Text("No recent history added.") } } } } } VStack(alignment: .leading) { HStack { Text("Yesterday") .font(.ubuntu(name: .ubuntuBold, size: 14)) .foregroundColor(Color.gray.opacity(0.6)) .padding(.vertical, 15) Spacer() } ScrollView(showsIndicators: false) { if let history = viewModel.history?.data?.data { ForEach(history.prefix(2), id: \.self) { item in if viewModel.isMoreThan24HoursAgo(item.createdAt ?? "N/A") { HistoryCard(historyModel: item) } else { Text("No recent history added.") } } } } } Spacer() } } }
日期判断函数:
func isMoreThan24HoursAgo(_ dateString: String) -> Bool { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" guard let date = dateFormatter.date(from: dateString) else { return false } let calendar = Calendar.current let now = Date() let components = calendar.dateComponents([.hour], from: date, to: now) if let hours = components.hour, hours > 24 { return true } else { return false } }
解答
一、分类方式是否合理?
用“是否超过24小时”区分“今日”“昨日”逻辑不够严谨:
- 若当前时间为晚上11点,24小时前是前一天晚上11点,前一天0点到11点的内容会被归为“昨日”,但实际距离当前不足24小时,不符合用户对“今日/昨日”的直觉认知
- 更合理的分类应该基于日历日期:“今日”指当前日历日的所有数据,“昨日”指前一个日历日的所有数据,完全贴合用户日常认知
如果业务明确要求用“24小时区间”划分,当前逻辑可行,但需明确该逻辑和常规“今日/昨日”概念的差异,对齐产品需求。
二、优化方案(解决重复判断+空提示重复问题)
1. 提前预处理数据,避免重复判断
在ViewModel中一次性完成数据分类,将数据拆分为“今日组”和“昨日组”,View层直接使用分组后的数据,无需重复判断:
- 在ViewModel中添加两个数组属性存储分组数据
- API数据返回后,执行一次分类逻辑,后续View直接取用分组结果
2. 重构视图逻辑,解决空提示重复问题
空提示应在分组数据为空时显示,而非在ForEach循环中逐个判断,确保空提示仅显示一次。
3. 优化日期判断函数
- 避免每次调用创建
DateFormatter(Formatter创建开销较大),将其设为静态属性复用 - 若按日历日期分类,直接使用
Calendar的isDateInToday和isDateInYesterday方法,更准确且符合用户直觉
优化后的完整代码
ViewModel部分
class ViewModel: ObservableObject { @Published var history: HistoryResponse? // 替换为你的API响应模型 // 预处理后的分组数据 @Published var todayItems: [HistoryItem] = [] @Published var yesterdayItems: [HistoryItem] = [] // 静态DateFormatter,避免重复创建 private static let dateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" return formatter }() func fetchHistory() { // 此处添加API请求逻辑,获取数据后调用分类方法 if let data = history?.data?.data { categorizeHistoryItems(data) } } private func categorizeHistoryItems(_ items: [HistoryItem]) { let calendar = Calendar.current todayItems = items.filter { item in guard let dateStr = item.createdAt, let date = Self.dateFormatter.date(from: dateStr) else { return false } // 按日历今日分类,符合用户直觉 return calendar.isDateInToday(date) // 若坚持用24小时区间,替换为: // return !isMoreThan24HoursAgo(dateStr) } yesterdayItems = items.filter { item in guard let dateStr = item.createdAt, let date = Self.dateFormatter.date(from: dateStr) else { return false } // 按日历昨日分类 return calendar.isDateInYesterday(date) // 若坚持用24小时区间,替换为: // return isMoreThan24HoursAgo(dateStr) } } // 若保留24小时判断逻辑,优化后的版本 private func isMoreThan24HoursAgo(_ dateString: String) -> Bool { guard let date = Self.dateFormatter.date(from: dateString) else { return false } let twentyFourHoursAgo = Date().addingTimeInterval(-24 * 60 * 60) return date < twentyFourHoursAgo } }
View部分
struct TestView: View { @StateObject var viewModel: ViewModel = ViewModel() var body: some View { VStack { // 今日分组视图 SectionView(title: "Today", items: viewModel.todayItems) // 昨日分组视图 SectionView(title: "Yesterday", items: viewModel.yesterdayItems) Spacer() } .onAppear { viewModel.fetchHistory() } } } // 提取复用的分组视图,减少代码冗余 struct SectionView: View { let title: String let items: [HistoryItem] var body: some View { VStack(alignment: .leading) { HStack { Text(title) .font(.ubuntu(name: .ubuntuBold, size: 14)) .foregroundColor(Color.gray.opacity(0.6)) .padding(.vertical, 15) Spacer() } ScrollView(showsIndicators: false) { if items.isEmpty { Text("No recent history added.") .foregroundColor(.gray) .padding(.vertical) } else { // 若需限制显示数量,使用prefix(2) ForEach(items.prefix(2), id: \.self) { item in HistoryCard(historyModel: item) } } } } .padding(.horizontal) } }
内容的提问来源于stack exchange,提问作者Luka Šalipur
相关产品推荐
相关产品推荐

