SwiftUI分组字典数组按日期升序排序异常的解决方法
解决SwiftUI聊天应用日期排序异常问题
问题分析
直接通过字符串比较dd-MM-yyyy格式的日期会导致跨月份排序逻辑错误——字符串按字符顺序逐位对比,比如05-07-2023的首字符0小于23-06-2023的首字符2,因此会被错误排在前面,忽略了实际的日期逻辑顺序。同月份内排序正常是因为日数字的字符顺序和逻辑顺序一致。
解决方案
1. 临时修复:排序时解析字符串为Date
若不想修改现有Model结构,可在排序过程中临时将日期字符串转为Date类型后再比较:
// 定义匹配日期格式的解析器 private let dayMonthYearFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy" formatter.locale = Locale(identifier: "en_US_POSIX") // 避免地区设置干扰解析 return formatter }() private let dayMonthYearTimeFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy HH:mm:ss" formatter.locale = Locale(identifier: "en_US_POSIX") return formatter }() // 转换分组并排序 let msgs = dictionaryArray.map { key, value in // 对单条消息的发送时间也用Date排序,避免同日期内时间排序异常 let sortedMessages = value.sorted { msg1, msg2 in guard let date1 = dayMonthYearTimeFormatter.date(from: msg1.sentDate), let date2 = dayMonthYearTimeFormatter.date(from: msg2.sentDate) else { return msg1.sentDate < msg2.sentDate // 解析失败时降级用字符串比较 } return date1 < date2 } return GroupedMessages(date: key, messages: sortedMessages) } // 对分组按日期升序排序 let groupMessages = msgs.sorted { group1, group2 in guard let dateStr1 = group1.date, let date1 = dayMonthYearFormatter.date(from: dateStr1), let dateStr2 = group2.date, let date2 = dayMonthYearFormatter.date(from: dateStr2) else { return group1.date ?? "" < group2.date ?? "" // 解析失败时降级处理 } return date1 < date2 } debugPrint(groupMessages)
2. 长期优化:修改Model存储Date类型
更高效的方式是直接在Model中存储Date类型,避免重复解析字符串,同时减少排序性能损耗:
修改后的Model
struct GroupedMessages: Identifiable, Hashable { var id = UUID() var date: Date? var messages: [Message] // 从dd-MM-yyyy字符串初始化日期 init(dateString: String, messages: [Message]) { let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy" formatter.locale = Locale(identifier: "en_US_POSIX") self.date = formatter.date(from: dateString) self.messages = messages } } struct Message: Identifiable, Hashable { var id : String var senderName: String var senderPersonalId: String var senderFirebaseId: String var messageId: String var sentDate: Date var message: String var seenMessage: String var isGroupChat: String var isBusinessChat: String var recieverFirebaseId: String var recieverFirebaseName: String var recieverPersonalID: String var type: String // 从dd-MM-yyyy HH:mm:ss字符串初始化发送时间 init(id: String, senderName: String, senderPersonalId: String, senderFirebaseId: String, messageId: String, sentDateString: String, message: String, seenMessage: String, isGroupChat: String, isBusinessChat: String, recieverFirebaseId: String, recieverFirebaseName: String, recieverPersonalID: String, type: String) { self.id = id self.senderName = senderName self.senderPersonalId = senderPersonalId self.senderFirebaseId = senderFirebaseId self.messageId = messageId let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy HH:mm:ss" formatter.locale = Locale(identifier: "en_US_POSIX") self.sentDate = formatter.date(from: sentDateString) ?? Date() // 解析失败用当前日期兜底 self.message = message self.seenMessage = seenMessage self.isGroupChat = isGroupChat self.isBusinessChat = isBusinessChat self.recieverFirebaseId = recieverFirebaseId self.recieverFirebaseName = recieverFirebaseName self.recieverPersonalID = recieverPersonalID self.type = type } }
简化后的排序代码
// 转换分组并排序单条消息 let msgs = dictionaryArray.map { key, value in let sortedMessages = value.sorted { $0.sentDate < $1.sentDate } return GroupedMessages(dateString: key, messages: sortedMessages) } // 对分组按日期升序排序 let groupMessages = msgs.sorted { $0.date ?? Date() < $1.date ?? Date() } debugPrint(groupMessages)
关键注意点
- 必须指定
locale为en_US_POSIX,避免设备地区设置影响日期解析结果 - 解析失败时添加兜底逻辑,防止排序崩溃
- 优先使用
Date类型存储日期,这是处理日期逻辑的标准做法,能避免大部分字符串排序异常
内容的提问来源于stack exchange,提问作者Taimoor Arif
相关产品推荐
相关产品推荐

