是否可以强制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
相关产品推荐
相关产品推荐

