Swift:等待阻塞用户数组加载完成后再调用FireStore获取用户函数
解决方案:串行加载并过滤用户数据
我来帮你搞定这个问题!核心问题在于当前两个异步请求是并行执行的,导致获取全量用户的请求先完成时,阻塞用户数组还没加载好,没法实时过滤。而didSet的问题是它会在数组每次变更时触发,导致重复发起请求,自然就出现了重复用户。
具体实现方案(分两种常见场景)
1. 用Swift 5.5+的async/await(推荐)
这种方式代码更简洁易读,能天然保证请求的串行顺序:
// 定义一个专门负责加载并过滤用户的方法 func loadAndFilterUsers() async { do { // 第一步:先获取阻塞用户数组,等待请求完成 let blockedUsers = try await fetchArrayOfUsersBlockingTheCurrentUser() // 第二步:拿到阻塞数组后,再请求全量用户 let allUsers = try await fireStoreFetchUsers() // 第三步:剔除阻塞用户,得到最终要展示的列表 let filteredUsers = allUsers.filter { user in !blockedUsers.contains(user.id) // 这里假设用户有唯一标识id,根据你的实际字段调整 } // 回到主队列更新UI(必须保证UI操作在主线程) DispatchQueue.main.async { self.displayedUsers = filteredUsers } } catch { // 处理请求失败的情况,比如打印错误、显示提示弹窗 print("加载用户数据失败:\(error.localizedDescription)") } }
然后在页面的生命周期里只调用一次这个方法:
- 如果是SwiftUI,用
.task修饰符(只会在视图出现时执行一次,消失时自动取消):var body: some View { UserListView(users: displayedUsers) .task { await loadAndFilterUsers() } } - 如果是UIKit,在
viewWillAppear里判断列表为空时才加载,避免每次进入页面都重复请求:override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if displayedUsers.isEmpty { Task { await loadAndFilterUsers() } } }
2. 用传统的Completion Handler(兼容老版本Swift)
如果你还没升级到支持async/await的版本,可以用嵌套的闭包保证串行顺序:
func loadAndFilterUsers() { // 第一步:先请求阻塞用户数组 fetchArrayOfUsersBlockingTheCurrentUser { [weak self] blockedUsers in guard let self = self else { return } // 第二步:拿到阻塞数组后再请求全量用户 self.fireStoreFetchUsers { allUsers in // 过滤阻塞用户 let filteredUsers = allUsers.filter { !blockedUsers.contains($0.id) } // 主线程更新UI DispatchQueue.main.async { self.displayedUsers = filteredUsers } } } }
调用时机和上面一样,在页面初始化/出现时只执行一次即可。
为什么didSet会导致重复用户?
didSet会在displayedUsers每次被赋值时触发,如果在didSet里调用fireStoreFetchUsers(),会导致:
- 第一次加载时设置数组,触发
didSet再次发起请求 - 新请求返回后又赋值数组,再次触发
didSet,形成循环 - 多次请求返回的用户列表叠加,就出现了重复用户
所以一定要避免在didSet里触发数据请求,把加载逻辑放在专门的方法里,只在合适的时机执行一次。
内容的提问来源于stack exchange,提问作者user9043410
相关产品推荐
相关产品推荐

