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

是否可以强制SwiftUI代码同步执行?异步网络请求提前返回如何解决

问题核心原因

你当前的代码逻辑存在本质问题:你使用静态存储属性的初始化闭包发起异步网络请求,闭包执行时网络请求还未完成,就已经返回了空的viewModel.modelData,且该闭包只会执行一次,后续请求拿到结果也不会更新master属性的值。
绝对不要强制把异步网络请求转为同步执行,尤其在主线程操作会直接卡住UI,严重时会触发系统 watchdog 直接杀死应用。

可行解决方案

方案1:回调闭包方案(兼容所有iOS版本)

第一步:修正ViewModel的已知bug

你当前的getData方法中用JSONEncoder编码JSON字符串属于逻辑错误,会把原有JSON串外层多包一层引号,导致服务端解析失败,修正后代码如下:

class ViewModel<T: Codable> : ObservableObject {
    @Published var modelData : T?
 
    func getData(url: URL, encoded: String, completion: (@escaping ()->()) ) {
        var request = URLRequest(url: url)
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"
        // 直接将JSON字符串转为UTF8数据即可,不需要用JSONEncoder二次编码
        request.httpBody = encoded.data(using: .utf8)
        
        URLSession.shared.dataTask(with: request) { data1, response, error in
            if let error = error {
                print("Request error: ", error)
                return
            }
            guard let data1 = data1 else { return }
            DispatchQueue.main.async {
                let decoder = JSONDecoder()
                if let data = try? decoder.decode(T.self, from: data1) {
                    self.modelData = data
                    completion()
                    print("Success!")
                } else {
                    print("Does not decode correctly")
                }
            }
        }.resume()
    }
}

第二步:改造MasterControllerStruct,废弃同步属性改用回调方法

struct MasterControllerStruct {
    static var isMasterPopulated = false
    static var objMaster : Master.MasterDetails?
    
    // 替换原同步master属性,用带回调的方法获取数据
    static func getMaster(completion: @escaping (Master.MasterDetails?) -> Void) {
        // 已有缓存直接返回
        if isMasterPopulated {
            completion(objMaster)
            return
        }
        // 无缓存发起网络请求
        let viewModel = ViewModel<Master>()
        let url = URL(string: "URL Goes Here")!
        let encoded = """
        {
            <JSON Parameters go here>
        }
        """
        viewModel.getData(url: url, encoded: encoded) {
            objMaster = viewModel.modelData?.Data
            isMasterPopulated = true
            completion(objMaster)
        }
    }
}

调用示例

MasterControllerStruct.getMaster { master in
    // 拿到数据后执行后续逻辑,UI操作需要切回主线程
    DispatchQueue.main.async {
        // 刷新UI等操作
    }
}

方案2:async/await方案(iOS 15+/macOS 12+ 支持)

如果你的项目不需要兼容旧系统,用Swift Concurrency语法可以让代码更简洁易读:

改造ViewModel为async版本

class ViewModel<T: Codable> : ObservableObject {
    @Published var modelData : T?
 
    func getData(url: URL, encoded: String) async throws -> T? {
        var request = URLRequest(url: url)
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"
        request.httpBody = encoded.data(using: .utf8)
        
        let (data, _) = try await URLSession.shared.data(for: request)
        let result = try JSONDecoder().decode(T.self, from: data)
        DispatchQueue.main.async {
            self.modelData = result
        }
        return result
    }
}

改造MasterControllerStruct

struct MasterControllerStruct {
    static var isMasterPopulated = false
    static var objMaster : Master.MasterDetails?
    
    static func getMaster() async -> Master.MasterDetails? {
        if isMasterPopulated {
            return objMaster
        }
        do {
            let viewModel = ViewModel<Master>()
            let url = URL(string: "URL Goes Here")!
            let encoded = """
            {
                <JSON Parameters go here>
            }
            """
            let result = try await viewModel.getData(url: url, encoded: encoded)
            objMaster = result?.Data
            isMasterPopulated = true
            return objMaster
        } catch {
            print("请求失败: \(error.localizedDescription)")
            return nil
        }
    }
}

调用示例

Task {
    let master = await MasterControllerStruct.getMaster()
    // UI操作切回主线程
    await MainActor.run {
        // 刷新UI等操作
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 14:36:04