SwiftUI如何等待异步请求完成后再执行onAppear逻辑更新视图
核心问题说明
- 现有代码没有主动调用
fetchTodos方法,网络请求根本不会执行 onAppear在视图渲染完成就立即触发,不会等待异步请求返回结果,此时读取的items永远是初始化的空数组
解决方案
我们可以通过增加加载状态标识+监听状态变化的方式,实现只有异步请求完成后才判断是否显示弹窗,修改步骤如下:
步骤1:修改数据请求类,增加加载状态
class AsyncGetTodosNetworkCall: ObservableObject { @AppStorage(DBUser.userID) var currentUserId: String? private var REF_USERS = DB_BASE.collection(DBCOLLECTION.appUsers) @Published var items = [TodoItem]() // 新增加载状态标记,避免初始空值误判 @Published var isDataLoaded = false func fetchTodos(toDetach: Bool) { guard let userID = currentUserId else { // 无用户ID的场景也标记加载完成 isDataLoaded = true return } let userDoc = REF_USERS.document(String(userID)) .collection(DBCOLLECTION.todos) .addSnapshotListener({ [weak self] (querySnapshot, error) in guard let self = self else { return } // 回调执行完成后统一标记加载完成 defer { self.isDataLoaded = true } guard let documents = querySnapshot?.documents else { print("No Documents Found") self.items = [] return } self.items = documents.map { document -> TodoItem in let todoID = document.documentID let todoName = document.get(ToDo.todoName) as? String ?? "" let todoCompleted = document.get(Todo.todoCompleted) as? Bool ?? false return TodoItem( id: todoID, todoName: todoName, todoCompleted: todoCompleted ) } }) if toDetach == true { userDoc.remove() } } }
步骤2:修改ContentView,主动触发网络请求
struct ContentView: View { @StateObject var arrayOfTodos = AsyncGetTodosNetworkCall() var body: some View { TodoListApp(asyncTodoList: arrayOfTodos) .onAppear { // 触发网络请求,根据业务需要传入toDetach参数 arrayOfTodos.fetchTodos(toDetach: false) } } }
步骤3:修改TodoListApp,监听加载完成状态后再判断弹窗显示逻辑
struct TodoListApp: View { @ObservedObject var asyncTodoList: AsyncGetTodosNetworkCall @State private var showPopUp: Bool = false var body: some View { NavigationView { ZStack { VStack { Text("Top Area") Text("List Area") } if showPopUp { VStack { Text("THIS IS MY POPUP!") Text("No Items Added Yet") }.frame(width: 300, height: 400) } } // 监听加载完成状态,只有加载完成后才判断是否显示弹窗 .onChange(of: asyncTodoList.isDataLoaded) { newValue in guard newValue else { return } showPopUp = asyncTodoList.items.isEmpty } // 可选:如果需要后续数据库实时更新后同步调整弹窗状态,增加对items的监听 .onChange(of: asyncTodoList.items) { newValue in guard asyncTodoList.isDataLoaded else { return } showPopUp = newValue.isEmpty } } } }
逻辑说明
- 通过
isDataLoaded标记过滤了请求未完成时的初始空数组场景,避免误判 - 如果不需要实时监听数据库变化,仅需要单次请求,可以把
addSnapshotListener替换为getDocuments接口,逻辑更简洁
内容的提问来源于stack exchange,提问作者Clay_F
相关产品推荐
相关产品推荐

