SwiftUI二次跳转页面时Alert无法显示的问题排查
问题原因分析
- NWPathMonitor重复启动异常:每次进入
DashboardView调用getDashboardData时,都会重复执行monitor.start(queue: queue)。NWPathMonitor不允许多次启动,重复调用会导致路径更新处理器(pathUpdateHandler)无法正常触发,进而无法设置appError,Alert自然不会弹出。 - ViewModel持有方式错误:使用
@ObservedObject持有DashboardViewModel,当返回上一页再进入时,若View未被完全销毁可能复用旧ViewModel;若View被销毁,旧ViewModel的NWPathMonitor未正确停止,会干扰新实例的网络状态检测。 - 未主动触发当前网络状态检测:默认情况下
NWPathMonitor仅在网络状态变化时触发pathUpdateHandler,若网络状态无变化,第二次进入时不会主动检测当前状态,导致无法及时设置错误信息。
解决方案
1. 修正ViewModel持有方式
将@ObservedObject改为@StateObject,确保ViewModel在View生命周期内唯一且稳定:
struct DashboardView: View { // 替换@ObservedObject为@StateObject @StateObject var viewModel = DashboardViewModel() // ... 其余代码不变 }
2. 正确管理NWPathMonitor生命周期
在DashboardViewModel中仅初始化时启动监控,销毁时停止,避免重复启动:
class DashboardViewModel: ObservableObject, FetchDashboardDataFromServer { // ... 已有代码 init() { self.currentState = .LOADING // 在init中初始化监控逻辑 monitor.pathUpdateHandler = { [weak self] path in guard let self = self else { return } DispatchQueue.main.async { if path.status == .satisfied { self.fetchDashboardDataFromAPI() } else { self.currentState = .FAILURE(error: StringConstants.NoInterNet) self.appError = AppError(errorString: StringConstants.NoInterNet) } } } monitor.start(queue: queue) } // 分离API请求逻辑 private func fetchDashboardDataFromAPI() { self.currentState = .LOADING APIClient.dispatch( APIRouter.GetDashboardData(queryParams: APIParameters.GetDasbhboardParams(apikey: "API_KEY"))) .sink { [weak self] completion in guard let self = self else { return } switch completion { case .finished: print("Execution Finished dashboard.") case .failure(let error): DispatchQueue.main.async { print("dashboard error", error) self.appError = AppError(errorString: error.localizedDescription) self.currentState = .FAILURE(error: error.localizedDescription) } } } receiveValue: { [weak self] dashboardData in guard let self = self else { return } print("received dashboard data", dashboardData) self.currentState = .SUCCESS(dashboardModel: dashboardData) }.store(in: &self.cancelable) } // 重写getDashboardData,主动触发状态检查 func getDashboardData() { print("fetch dashboard data") // 主动获取当前网络状态并处理 if let currentPath = monitor.currentPath { DispatchQueue.main.async { if currentPath.status == .satisfied { self.fetchDashboardDataFromAPI() } else { self.currentState = .FAILURE(error: StringConstants.NoInterNet) self.appError = AppError(errorString: StringConstants.NoInterNet) } } } } // 销毁时停止监控并取消请求 deinit { monitor.cancel() cancelable.forEach { $0.cancel() } } }
3. 调整onAppear位置
将onAppear移到父容器上,确保每次View出现时都调用getDashboardData:
var body: some View { ZStack { // ... 原有视图内容 } .navigationBarTitle("Dashboard", displayMode: .inline) .alert(item: $viewModel.appError) { appAlert in Alert(title: Text("Error"), message: Text(""" \(appAlert.errorString) Please try again later! """ ) ) } .ignoresSafeArea(.all) // 将onAppear移到此处,确保每次View出现都触发 .onAppear(perform: viewModel.getDashboardData) }
关键说明
@StateObject确保ViewModel在View生命周期内仅初始化一次,避免重复创建导致的资源泄漏和状态混乱。NWPathMonitor仅启动一次,销毁时停止,避免重复启动引发的异常。- 主动获取当前网络状态,确保每次进入View都能触发对应的错误处理逻辑。
内容的提问来源于stack exchange,提问作者iosdev1111
相关产品推荐
相关产品推荐

