如何同步调用Firebase数据库数据?解决异步加载业务冲突
解决方案说明
首先明确:iOS中绝对不能将Firebase的异步请求改为同步实现,因为这会阻塞主线程,导致APP失去响应甚至被系统看门狗进程杀死。正确的做法是用规范的异步流程处理,并控制界面渲染时机。
现有代码的问题
- 使用
DispatchGroup.wait()会阻塞当前线程,若在主线程调用会直接导致APP卡顿崩溃 - 错误使用
withUnsafeThrowingContinuation,Firestore已经提供原生的async/await API,无需手动封装 - 代码中
i.document未定义,缺少对查询结果文档的遍历逻辑 - 返回值
Float无实际意义,函数核心是获取经纬度数据
正确实现方案
1. 重构异步数据获取函数
利用Firestore原生的async/await API,简化代码并避免线程阻塞:
import FirebaseFirestore import SwiftUI struct MapView: View { @State private var realLat: [String] = [] @State private var realLong: [String] = [] @State private var mapAnnotations: [MapAnnotationItem] = [] // 自定义标注类型 @State private var isLoading = true // 控制加载状态 // 自定义地图标注模型(示例) struct MapAnnotationItem: Identifiable { let id = UUID() let coordinate: CLLocationCoordinate2D } var body: some View { Group { if isLoading { ProgressView("加载数据中...") } else { Map(coordinateRegion: .constant(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 39.9, longitude: 116.4), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))), annotationItems: mapAnnotations) { item in MapPin(coordinate: item.coordinate) } } } .onAppear { loadData() } } // 重构后的异步数据获取函数 func downloadFirebaseData() async throws { let db = Firestore.firestore() let querySnapshot = try await db.collection("annotations").getDocuments() // 遍历所有文档,提取经纬度 for document in querySnapshot.documents { if let lat = document.get("lat") as? String, let long = document.get("long") as? String { await MainActor.run { realLat.append(lat) realLong.append(long) } } } // 数据加载完成后生成地图标注 await MainActor.run { mapAnnotations = zip(realLat, realLong).compactMap { latStr, longStr in guard let lat = Double(latStr), let long = Double(longStr) else { // 跳过无效经纬度数据 return nil } return MapAnnotationItem(coordinate: CLLocationCoordinate2D(latitude: lat, longitude: long)) } isLoading = false } } // 触发数据加载的入口 func loadData() { Task { do { try await downloadFirebaseData() } catch { print("加载数据失败: \(error.localizedDescription)") await MainActor.run { isLoading = false // 可在此添加错误提示UI,比如Alert } } } } }
2. 核心思路说明
- 使用
async/await替代回调,代码更简洁易读 - 通过
isLoading状态控制界面显示:加载时显示进度条,完成后渲染地图 - 所有更新
@State变量的操作都通过MainActor.run切换到主线程,符合SwiftUI的线程安全要求 - 数据加载完成后再生成
mapAnnotations,确保标注基于完整的经纬度数据
关键注意事项
- 永远不要在主线程调用阻塞式API(比如
DispatchGroup.wait()),这是iOS开发的基本准则 - 处理经纬度字符串转Double的异常情况,避免无效数据导致地图显示异常
- 添加错误处理逻辑,在数据加载失败时给用户反馈
内容的提问来源于stack exchange,提问作者SwiftX
相关产品推荐
相关产品推荐

