基于Firebase构建类Strava社交网络:iOS端用户查询与关注功能疑问
嘿,我来帮你搞定这个Firebase用户搜索+关注状态区分的问题!其实核心就是先搜用户,再匹配当前用户的关注列表,我给你拆解成具体步骤,结合iOS代码示例来说明:
先明确数据库结构(假设你用Firestore)
首先对齐下常见的Firebase用户/关注数据结构,方便后续代码对应:
users 集合 ├─ userId1 │ ├─ username: "johndoe" │ ├─ displayName: "John Doe" │ └─ ...其他用户字段 └─ userId2 ├─ username: "janedoe" ├─ displayName: "Jane Doe" └─ ...其他用户字段 following 集合 ├─ currentUserId(当前用户的ID) │ ├─ followedUserId1: true // 已关注用户的ID作为键,值为true │ ├─ followedUserId2: true │ └─ ... └─ ...其他用户的关注数据
如果你的结构是把following数组存在用户文档里也没关系,后面代码会标注对应写法。
第一步:实现用户名搜索功能
先做基础的前缀匹配搜索(比全包含搜索高效,适合Strava这类社交场景):
// 假设你有一个Codable的User模型,包含id、username等字段 func searchUsers(withUsername keyword: String, completion: @escaping ([User]) -> Void) { guard let currentUid = Auth.auth().currentUser?.uid else { completion([]) return } let db = Firestore.firestore() // 前缀匹配:搜索用户名以keyword开头的用户 db.collection("users") .whereField("username", isGreaterThanOrEqualTo: keyword) .whereField("username", isLessThanOrEqualTo: keyword + "\u{f8ff}") .getDocuments { snapshot, error in guard let docs = snapshot?.documents, error == nil else { completion([]) return } // 解析用户数据并排除当前用户自己 let users = docs.compactMap { try? $0.data(as: User.self) } .filter { $0.id != currentUid } completion(users) } }
第二步:区分已关注/未关注用户
这里分两种方案,根据你的用户量级选择:
方案一:一次性拉取当前用户的关注列表(适合关注人数不多的情况)
先拿到当前用户所有已关注的ID,再和搜索结果匹配:
func getCurrentUserFollowingIds(completion: @escaping ([String]) -> Void) { guard let currentUid = Auth.auth().currentUser?.uid else { completion([]) return } let db = Firestore.firestore() // 对应上面的following集合结构 db.collection("following").document(currentUid).getDocument { doc, error in guard let data = doc?.data(), error == nil else { completion([]) return } // 提取所有已关注用户的ID let followingIds = data.keys.compactMap { $0 } completion(followingIds) } // 如果你的结构是用户文档里有following数组,替换成下面这段: // db.collection("users").document(currentUid).getDocument { doc, error in // let currentUser = try? doc?.data(as: User.self) // completion(currentUser?.following ?? []) // } } // 结合搜索和状态匹配 searchUsers(withUsername: "jane") { searchResults in getCurrentUserFollowingIds { followingIds in // 给每个搜索结果标记关注状态 let usersWithStatus = searchResults.map { user in var mutableUser = user mutableUser.isFollowed = followingIds.contains(user.id) return mutableUser } // 回到主线程更新UI DispatchQueue.main.async { self.userList = usersWithStatus self.tableView.reloadData() } } }
方案二:逐个查询关注状态(适合关注人数极多的情况)
如果当前用户关注了上千人,一次性拉取所有ID太占带宽,可以对每个搜索结果单独查询:
func checkIfUserIsFollowed(userId: String, completion: @escaping (Bool) -> Void) { guard let currentUid = Auth.auth().currentUser?.uid else { completion(false) return } let db = Firestore.firestore() // 检查当前用户的关注文档里是否存在该用户ID db.collection("following").document(currentUid).getDocument { doc, error in guard let data = doc?.data(), error == nil else { completion(false) return } completion(data[userId] != nil) } } // 使用方式 searchUsers(withUsername: "jane") { searchResults in let dispatchGroup = DispatchGroup() var usersWithStatus: [User] = [] for user in searchResults { dispatchGroup.enter() checkIfUserIsFollowed(userId: user.id) { isFollowed in var mutableUser = user mutableUser.isFollowed = isFollowed usersWithStatus.append(mutableUser) dispatchGroup.leave() } } // 所有查询完成后更新UI dispatchGroup.notify(queue: .main) { self.userList = usersWithStatus self.tableView.reloadData() } }
第三步:列表视图处理关注/取消关注按钮
在cell里根据isFollowed状态设置按钮,并实现点击切换逻辑:
// 自定义UserCell示例 class UserCell: UITableViewCell { @IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var followButton: UIButton! } // 列表数据源方法 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as! UserCell let user = userList[indexPath.row] cell.usernameLabel.text = user.username cell.followButton.setTitle(user.isFollowed ? "已关注" : "关注", for: .normal) cell.followButton.tag = indexPath.row cell.followButton.addTarget(self, action: #selector(toggleFollowAction(_:)), for: .touchUpInside) return cell } // 切换关注状态的方法 @objc func toggleFollowAction(_ sender: UIButton) { let index = sender.tag let targetUser = userList[index] guard let currentUid = Auth.auth().currentUser?.uid else { return } let db = Firestore.firestore() let followingDocRef = db.collection("following").document(currentUid) if targetUser.isFollowed { // 取消关注:删除对应字段 followingDocRef.updateData([targetUser.id: FieldValue.delete()]) { [weak self] error in guard error == nil else { return } DispatchQueue.main.async { self?.userList[index].isFollowed = false self?.tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic) } } } else { // 关注:添加字段 followingDocRef.setData([targetUser.id: true], merge: true) { [weak self] error in guard error == nil else { return } DispatchQueue.main.async { self?.userList[index].isFollowed = true self?.tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic) } } } }
额外优化建议
- 缓存关注列表:把当前用户的关注ID存在本地(比如UserDefaults或者ViewModel),避免每次搜索都重新拉取,定期刷新即可。
- 搜索性能优化:如果用户量很大,建议用Firebase Cloud Functions+Algolia做全文搜索,Firestore的前缀查询在数据量过万后性能会下降。
- 加载状态处理:搜索时显示ActivityIndicator,给用户明确的反馈。
内容的提问来源于stack exchange,提问作者swifferina
相关产品推荐
相关产品推荐

