Swift中调用Firebase实现搜索栏查询数据库并展示结果的方法
问题说明与修改方案
你当前的代码逻辑是首次加载时拉取Firestore的items集合全量数据,在本地做过滤展示搜索结果,小数据量下可以直接运行,如果想要优化为从Firebase直接拉取匹配搜索关键词的数据(避免一次性拉全量数据浪费资源),可以按以下步骤修改:
1. 提前明确Firestore的搜索限制
- Firestore原生不支持中缀/后缀模糊查询,仅支持前缀匹配,也就是只能搜索「关键词开头」的内容,如果需要全字段模糊搜索,你需要额外存储搜索关键词数组字段,或者对接第三方搜索服务
- 默认查询是大小写敏感的,你可以在Firestore中额外存一个全小写的
searchName字段,避免大小写匹配失败的问题
2. 修改getData类,增加实时搜索方法
class getData : ObservableObject{ @Published var datas = [dataType]() private let db = Firestore.firestore() private var listener: ListenerRegistration? // 监听器,用于取消旧请求避免资源浪费 // 首次启动拉取全量数据 init() { fetchAllItems() } // 拉取全量items数据 func fetchAllItems() { listener?.remove() listener = db.collection("items").addSnapshotListener { [weak self] snap, err in guard let self = self, let snap = snap, err == nil else { print(err?.localizedDescription ?? "未知错误") return } self.datas = snap.documents.map { doc in dataType( id: doc.documentID, name: doc.get("name") as? String ?? "", msg: doc.get("msg") as? String ?? "" ) } } } // 按搜索关键词拉取匹配数据 func searchItems(keyword: String) { guard !keyword.isEmpty else { fetchAllItems() // 关键词为空时恢复全量数据展示 return } listener?.remove() let lowerKeyword = keyword.lowercased() // 构造前缀匹配的终止条件:关键词最后一位ASCII码+1 let endKeyword = lowerKeyword.prefix(lowerKeyword.count - 1) + String(Character(UnicodeScalar(lowerKeyword.last!.asciiValue! + 1)!)) listener = db.collection("items") .whereField("searchName", isGreaterThanOrEqualTo: lowerKeyword) .whereField("searchName", isLessThan: endKeyword) .addSnapshotListener { [weak self] snap, err in guard let self = self, let snap = snap, err == nil else { print(err?.localizedDescription ?? "未知错误") return } self.datas = snap.documents.map { doc in dataType( id: doc.documentID, name: doc.get("name") as? String ?? "", msg: doc.get("msg") as? String ?? "" ) } } } deinit { listener?.remove() } }
注意:上述代码用到的searchName字段需要你提前在Firestore的items集合中写入,值为对应name字段的全小写内容,用来实现大小写不敏感的匹配。
3. 修改CustomSearchBar,输入关键词时触发搜索
struct CustomSearchBar : View { @State var txt = "" @Binding var data : [dataType] // 传入ViewModel触发搜索请求 var viewModel: getData var body : some View{ VStack(spacing: 0){ HStack{ TextField("Search", text: self.$txt) .onChange(of: txt) { newValue in // 输入内容变化时触发搜索 viewModel.searchItems(keyword: newValue) } if self.txt != ""{ Button(action: { self.txt = "" }) { Text("Cancel") } .foregroundColor(.black) } }.padding() if self.txt != ""{ if data.isEmpty { Text("No Results Found").foregroundColor(Color.black.opacity(0.5)).padding() } else { List(data){ i in NavigationLink(destination: Detail(data: i)) { Text(i.name) } }.frame(height: UIScreen.main.bounds.height / 5) } } }.background(Color.white) .padding() } }
4. 修改ContentView中CustomSearchBar的传参
CustomSearchBar(data: self.$data.datas, viewModel: self.data).padding(.top)
如果你的数据量很小,不需要做服务端过滤,直接用你原来的本地过滤逻辑也可以正常运行,检查以下两点即可排查展示问题:
- 确认Firestore的items集合配置了读取权限,安全规则没有拦截请求
- 确认Firestore中的文档确实存在
name和msg字段,没有拼写错误
内容的提问来源于stack exchange,提问作者user14963385
相关产品推荐
相关产品推荐

