SwiftUI列表拖动排序时高效更新SQLite数据库的优化方案咨询
SwiftUI列表拖动排序时高效更新SQLite数据库的优化方案咨询
你好呀!当前你用的这种全量更新所有列表项的方法确实有两个问题:一是效率极低——每次拖动都要执行和列表条目数一样多的更新语句,条目越多越卡;二是做了无用功——你更新的date、content、amount这些字段其实根本没变化,只是排序位置变了而已。咱们可以优化成只更新位置发生变化的条目,下面给你具体的思路和代码示例:
第一步:给数据模型和数据库表添加排序字段
首先,你的Item模型和对应的SQLite表需要加一个position字段(整数类型),用来记录每个条目在列表中的排序位置。这样我们只需要更新这个字段,而不是重复更新那些原本就没变化的内容。
比如你的MainTable可以这样定义(基于SQLite.swift):
let MainTable = Table("main_table") let dbId = Expression<Int>("id") let dbDate = Expression<String>("date") let dbContent = Expression<String>("content") let dbAmount = Expression<String>("amount") let dbPosition = Expression<Int>("position") // 新增排序字段
对应的Item结构体也要加上position属性:
struct Item: Identifiable { let id: Int let date: String let content: String let amount: String var position: Int // 记录排序位置 }
第二步:优化拖动后的更新逻辑
原来的moveItem方法里,你不需要遍历所有条目,只需要找出哪些条目的位置发生了变化,然后单独更新它们的position值就好。具体步骤:
- 先保存移动前的位置映射,方便后续对比
- 执行本地列表的移动操作
- 遍历新列表,只更新位置变化的条目
- 不需要重新全量加载数据(本地
listItem已经和数据库同步)
优化后的moveItem代码:
func moveItem(from source: IndexSet, to destination: Int) { // 保存移动前的「id-旧位置」映射 let oldPositionMap = Dictionary(uniqueKeysWithValues: listItem.enumerated().map { ($0.element.id, $0.offset) }) // 执行本地列表的移动操作 listItem.move(fromOffsets: source, toOffset: destination) // 遍历新列表,只更新位置变化的条目 for (newPosition, item) in listItem.enumerated() { guard let oldPosition = oldPositionMap[item.id], oldPosition != newPosition else { continue // 位置没变化,跳过更新 } dbManager.updateItemPosition(item.id, to: newPosition) } }
对应的数据库更新方法只处理position字段:
func updateItemPosition(_ id: Int, to newPosition: Int) { do { let update = MainTable.filter(dbId == id) try db.run(update.update(dbPosition <- newPosition)) } catch let err { print("updateItemPosition err = \(err)") } }
额外的实用小建议
- 加载数据库数据时,一定要按
position字段排序,保证列表顺序正确:func loadDbData() -> [Item] { do { let items = try db.prepare(MainTable.order(dbPosition)) return items.map { Item( id: $0[dbId], date: $0[dbDate], content: $0[dbContent], amount: $0[dbAmount], position: $0[dbPosition] ) } } catch let err { print("loadDbData err = \(err)") return [] } } - 如果列表条目较多,可以用SQLite事务包裹所有更新操作,减少IO开销:
func moveItem(from source: IndexSet, to destination: Int) { let oldPositionMap = Dictionary(uniqueKeysWithValues: listItem.enumerated().map { ($0.element.id, $0.offset) }) listItem.move(fromOffsets: source, toOffset: destination) do { // 开启事务 try db.run(.beginTransaction) for (newPosition, item) in listItem.enumerated() { guard let oldPosition = oldPositionMap[item.id], oldPosition != newPosition else { continue } let update = MainTable.filter(dbId == item.id) try db.run(update.update(dbPosition <- newPosition)) } // 提交事务 try db.run(.commit) } catch let err { print("Transaction err = \(err)") // 出错时回滚 try? db.run(.rollback) } }
这样修改之后,每次拖动排序只会更新真正位置变化的条目,相比全量更新效率提升非常明显,尤其是列表条目较多的时候~
备注:内容来源于stack exchange,提问作者androidisgood
相关产品推荐
相关产品推荐

