You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI视图中如何优雅等待Task完成以正确保存数据?

解决方案:替代延迟等待,可靠触发任务完成后的保存操作

核心问题分析

你当前使用DispatchQueue.main.asyncAfter的方式完全依赖固定延迟,无法适配网络请求、地理编码的实际耗时,要么会保存旧值,要么会做不必要的等待。我们可以通过异步流程管控+状态监听的方式,确保所有任务完成后再执行保存逻辑,同时不阻塞UI。


步骤1:重构Networking类,用async/await管控异步流程

先把闭包式的异步API改为async/await,让任务执行顺序更清晰,同时添加状态标识来通知视图任务完成情况:

import CoreLocation

class Networking: NSObject, ObservableObject, CLLocationManagerDelegate {
    @Published public var lastLocation: CLLocation?
    @Published public var locationString: CLPlacemark?
    @Published private(set) var isProcessingLocation = false // 标记任务是否在执行
    private var locationManager = CLLocationManager()

    // 重构地理编码方法为async/await形式
    private func getLocationString() async throws -> CLPlacemark? {
        guard let location = lastLocation else { return nil }
        let geocoder = CLGeocoder()
        let placemarks = try await geocoder.reverseGeocodeLocation(location)
        return placemarks.first
    }

    // 重构天气、AQI请求为async/await(假设原方法是闭包形式,这里改为异步)
    private func getMainWeather(_ lat: Double, _ lon: Double) async {
        // 替换为你的异步请求实现,比如URLSession.data(from:)
    }

    private func getAQI(_ lat: Double, _ lon: Double) async {
        // 替换为你的异步请求实现
    }

    // 用自定义方法触发位置处理,替代直接设置lastLocation的didSet
    public func processLocation(_ location: CLLocation) {
        lastLocation = location
        isProcessingLocation = true
        
        Task {
            do {
                // 等待地理编码完成,再更新locationString
                if let placemark = try await getLocationString() {
                    await MainActor.run {
                        self.locationString = placemark
                    }
                }
                // 依次执行其他异步任务
                await getMainWeather(location.coordinate.latitude, location.coordinate.longitude)
                await getAQI(location.coordinate.latitude, location.coordinate.longitude)
            } catch {
                print("位置处理出错:\(error.localizedDescription)")
            }
            
            // 任务全部完成,更新状态并停止定位
            await MainActor.run {
                self.isProcessingLocation = false
                locationManager.stopUpdatingLocation()
            }
        }
    }
}

步骤2:重构视图与SavedLocations,监听状态触发保存

把SavedLocations改为ObservableObject统一管理本地存储,同时用SwiftUI的onChange监听Networking的任务状态,确保只有任务完成后才执行保存:

struct LocationSearch: View {
    @StateObject var locationService = LocationService()
    @ObservedObject var networking: Networking
    @StateObject private var savedLocations = SavedLocations() // 单实例管理存储
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        ForEach(locationService.searchResults, id: \.self) { location in
            Button(location.title) {
                getCoordinate(addressString: location.title) { coordinates, error in
                    guard error == nil, let coordinates = coordinates else { return }
                    let newLocation = CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)
                    // 调用自定义方法触发位置处理
                    networking.processLocation(newLocation)
                }
            }
        }
        // 监听任务完成状态,触发保存与页面关闭
        .onChange(of: networking.isProcessingLocation) { isProcessing in
            guard !isProcessing, let locality = networking.locationString?.locality else { return }
            // 可选:避免重复添加相同地点
            if !savedLocations.all.contains(locality) {
                savedLocations.all.append(locality)
                savedLocations.save()
                dismiss()
            }
        }
    }

    // 保留原有的坐标获取方法(若可能也建议改为async/await)
    private func getCoordinate(addressString: String, completion: @escaping (CLLocationCoordinate2D?, Error?) -> Void) {
        // 原实现逻辑
    }
}

// 统一管理本地存储的类
class SavedLocations: ObservableObject {
    @Published var all: [String] = []

    init() {
        // 初始化时读取UserDefaults中的旧数据
        if let saved = UserDefaults.standard.array(forKey: "savedLocations") as? [String] {
            all = saved
        }
    }

    // 封装保存逻辑
    func save() {
        UserDefaults.standard.set(all, forKey: "savedLocations")
    }
}

关键改进点说明

  1. async/await管控异步流程:确保地理编码、天气、AQI请求按顺序完成,避免任务执行混乱。
  2. 状态标识监听:通过isProcessingLocation让视图精准感知任务完成时机,替代不可靠的固定延迟。
  3. 统一存储管理:SavedLocations作为ObservableObject,避免每次点击按钮新建实例导致的旧数据丢失问题。
  4. 主线程更新UI:用MainActor.run确保@Published属性的更新在主线程执行,符合SwiftUI的线程安全要求。

内容的提问来源于stack exchange,提问作者John Harrington

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 18:35:16