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

URLSession HTTP错误无法实时更新视图,需重复触发操作

问题分析与解决方案:API调用后视图不更新及异步逻辑错误

核心问题拆解

  1. 异步逻辑错误:checkDriverIn使用URLSession.dataTask回调处理请求,但函数本身标记为async却在网络请求未完成时,就基于旧的httpError值返回布尔结果,导致第二次调用才用上一次的请求结果。
  2. UI更新失效:网络回调在后台线程执行,直接修改@Published属性可能无法触发视图更新;另外HTTPError无需作为ObservableObject,反而会干扰@Published的状态发布逻辑。

解决方案步骤

1. 简化HTTPError类型

HTTPError只需作为普通Codable值类型即可,不需要继承ObservableObject,ViewModel中的@Published会负责发布整个对象的变化:

struct HTTPError: Codable {
    var statusCode: Int?
    var message: [String]?
    var error: String?

    init(statusCode: Int? = nil, message: [String]? = [], error: String? = nil) {
        self.statusCode = statusCode
        self.message = message ?? []
        self.error = error
    }

    enum CodingKeys: String, CodingKey {
        case statusCode, message, error
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.statusCode = try container.decodeIfPresent(Int.self, forKey: .statusCode)
        
        // 兼容message字段为字符串或数组的情况
        if let strMessage = try container.decodeIfPresent(String.self, forKey: .message) {
            self.message = [strMessage]
        } else {
            self.message = try container.decodeIfPresent([String].self, forKey: .message) ?? []
        }
        
        self.error = try container.decodeIfPresent(String.self, forKey: .error)
    }
}

2. 修正ViewModel的异步逻辑

改用URLSession的async/await版本,让函数等待网络请求完成后再返回结果,同时确保状态更新在主线程执行:

class VM: ObservableObject {
    @Published var isLoading = false
    @Published var httpError = HTTPError()

    func checkDriverIn(_ record: DriverQRParam) async -> Bool {
        isLoading = true
        defer { isLoading = false } // 请求结束后自动关闭加载状态
        
        guard let url = URL(string: "你的API地址") else {
            httpError = HTTPError(error: "无效的请求地址")
            return false
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST" // 根据实际请求方式调整
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        do {
            // 编码请求体(根据你的DriverQRParam实现)
            let jsonData = try JSONEncoder().encode(record)
            request.httpBody = jsonData
            
            // 使用async/await版本的网络请求API
            let (data, response) = try await URLSession.shared.data(for: request)
            
            // 处理响应状态码
            if let httpResponse = response as? HTTPURLResponse {
                switch httpResponse.statusCode {
                case 400:
                    httpError = try JSONDecoder().decode(HTTPError.self, from: data)
                    return false
                case 200:
                    httpError = HTTPError() // 成功时重置错误状态
                    return true
                default:
                    httpError = HTTPError(error: "请求失败,状态码: \(httpResponse.statusCode)")
                    return false
                }
            }
        } catch {
            httpError = HTTPError(error: error.localizedDescription)
            return false
        }
        
        httpError = HTTPError(error: "请求处理异常")
        return false
    }
}

3. 修正视图中的ViewModel声明

使用@StateObject而非@State管理ObservableObject的生命周期,确保视图能正确响应ViewModel的状态变化:

struct xyz: View {
    @StateObject private var vm = VM()
    @State private var driverParam = DriverQRParam() // 假设已完成初始化

    var body: some View {
        VStack {
            // 展示错误信息
            if let errorMsg = vm.httpError.error {
                Text(errorMsg)
                    .foregroundColor(.red)
                    .padding()
            }
            
            Button(vm.isLoading ? "加载中..." : "点击") {
                Task {
                    let result = await vm.checkDriverIn(driverParam)
                    // 根据请求结果执行后续逻辑,比如弹窗、导航等
                    print("请求结果: \(result)")
                }
            }
            .disabled(vm.isLoading) // 加载中禁用按钮,避免重复触发请求
        }
    }
}

关键说明

  • async/await替代回调:彻底解决函数提前返回旧值的问题,确保返回结果为当前请求的最新状态。
  • 自动主线程更新:URLSession.shared.data(for:)在async上下文会自动切换回主线程,修改@Published属性时能直接触发视图更新。
  • 值类型优化:HTTPError改为struct符合Swift值语义,避免引用类型带来的意外状态共享问题。

内容的提问来源于stack exchange,提问作者Mohamed Said

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 19:40:37