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

SwiftUI中如何等待城市值赋值后再执行.onAppear加载数据?

解决SwiftUI中逆地理编码完成前调用API导致城市为空的问题

这个问题我之前也遇到过——本质是异步操作的顺序问题:你的位置获取和逆地理编码都是异步完成的,但onAppear会在视图加载时立刻触发,这时候city还没被赋值,自然会导致API请求用了空值。咱们可以通过监听城市值的变化来触发API请求,同时优化位置管理器的逻辑,避免不必要的重复操作。

核心解决方案:监听城市状态变化,而非视图加载时直接请求

1. 修改HomeScreenView,替换onAppear为onChange监听

把原来在List上的.onAppear(perform: loadData)移除,改成监听city的变化,当城市有效值出现时再调用loadData。另外可以加个状态变量避免重复请求:

struct HomeScreenView: View {
    @State var results = [Cinema]()
    @ObservedObject var lm = LocationManager()
    @State private var hasLoadedCinemas = false // 避免重复请求
    @State private var isLoading = false // 可选:添加加载状态

    var latitude: String {
        return("\(lm.location?.latitude ?? 0)")
    }
    var longitude: String {
        return("\(lm.location?.longitude ?? 0)")
    }
    var placemark: String {
        return("\(lm.placemark?.description ?? \"XXX\")")
    }
    var status: String {
        return("\(lm.status ?? .notDetermined)")
    }
    var city: String {
        return("\(lm.placemark?.locality ?? \"empty\")")
    }

    var body: some View {
        VStack {
            // 可选:添加加载提示
            if isLoading {
                ProgressView("加载影院中...")
            }
            
            List(results, id: \._id) { item in
                VStack(alignment: .leading) {
                    Text(item.cinemaName)
                        .font(.headline)
                    Text(item.cinemaCategory)
                }
            }
            // 监听city的变化,当城市有效且未加载过数据时触发请求
            .onChange(of: city) { newCity in
                guard newCity != "empty", !hasLoadedCinemas else { return }
                loadData()
            }
        }
    }

    func loadData() {
        guard let url = URL(string: "https://mycinemasapi.com/cinemasbycity/\(self.city)") else {
            print("Invalid URL")
            return
        }
        isLoading = true
        let request = URLRequest(url: url)
        URLSession.shared.dataTask(with: request) { data, response, error in
            defer {
                DispatchQueue.main.async {
                    isLoading = false
                }
            }
            if let data = data {
                if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
                    DispatchQueue.main.async {
                        self.results = decodedResponse.cinemas
                        self.hasLoadedCinemas = true // 标记已加载,避免重复请求
                    }
                    return
                }
            }
            print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
        }.resume()
    }
}

2. 优化LocationManager,避免重复获取位置和逆地理编码

当前你的LocationManager会持续更新位置,这会导致多次触发逆地理编码,既浪费资源又可能引发不必要的API请求。可以在获取到第一次有效位置后停止更新:

extension LocationManager: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        self.status = status
        // 可选:如果用户拒绝权限,可以在这里处理提示
        if status == .denied {
            print("用户拒绝了位置权限")
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        self.location = location
        self.geocode()
        // 获取到位置后停止更新,避免重复触发
        manager.stopUpdatingLocation()
    }
}

另外,建议给geocode方法的错误处理补全日志,方便调试:

private func geocode() {
    guard let location = self.location else { return }
    geocoder.reverseGeocodeLocation(location, completionHandler: { (places, error) in
        if let error = error {
            print("逆地理编码失败:\(error.localizedDescription)")
            self.placemark = nil
            return
        }
        self.placemark = places?[0]
    })
}

为什么这样修改有效?

  • onChange(of: city):只有当city从"empty"变成实际城市名称时,才会触发loadData,确保API请求时城市值已经准备好。
  • hasLoadedCinemas:避免城市值因某些原因变化时重复请求API(比如位置微调导致逆地理编码重复触发)。
  • 停止位置更新:获取到一次有效位置后就停止更新,减少不必要的异步操作,提升性能。

这样修改后,你的应用就能在城市信息准备完成后再去请求影院列表,不会再出现city为空的问题了。

内容的提问来源于stack exchange,提问作者Jose Q

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 12:42:49