SwiftUI中View与含API调用的类间传递十六进制参数的最佳方案
实现方案
首先先修正现有代码的两处核心问题:
ObservableObject类中不能使用@State属性包装器,@State仅适用于SwiftUI View结构体内部的状态管理- 原代码仅在类初始化时调用一次接口,没有提供外部触发重新请求的入口
第一步:改造API调用类(ViewModel)
我们做以下调整:
- 修正属性包装器错误,将请求参数
postHashHex改为@Published可监听属性 - 新增可主动调用的请求方法,支持外部传入十六进制参数
- 优化参数生成逻辑,改用系统JSON序列化避免字符串拼接的转义错误
- 可选添加防抖逻辑,避免输入过程中频繁触发接口请求
import Combine // 符合Swift命名规范,类名首字母大写 class FetchResultsViewModel: ObservableObject { @Published var clout = Cloutington() @Published var dataHasLoaded = false @Published var postHashHex: String = "" // 用于监听属性变化的订阅者 private var cancellable = Set<AnyCancellable>() init() { // 可选:监听postHashHex变化,防抖500ms后自动发起请求 $postHashHex .debounce(for: .milliseconds(500), scheduler: DispatchQueue.main) .removeDuplicates() .filter { !$0.isEmpty } // 空值不请求 .sink { [weak self] hex in self?.getData(postHashHex: hex) } .store(in: &cancellable) } // 公开请求方法,支持外部传入参数调用 func getData(postHashHex: String) { // 改用JSON序列化生成参数,避免转义错误 let parameters = ["PostHashHex": postHashHex] guard let postData = try? JSONSerialization.data(withJSONObject: parameters) else { return } var request = URLRequest(url: URL(string: "https://bitclout.com/api/v0/get-single-post")!, timeoutInterval: 10) // 不要用无穷超时 request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.httpBody = postData request.httpMethod = "POST" let task = URLSession.shared.dataTask(with: request) { [weak self] responseData, response, error in guard let self = self, let resData = responseData, error == nil else { print(error ?? "请求错误") return } do { let finalData = try JSONDecoder().decode(Cloutington.self, from: resData) DispatchQueue.main.async { self.clout = finalData self.dataHasLoaded = true } } catch { print("解析错误: \(error)") } } task.resume() } }
第二步:改造View层代码
将TextField的输入内容和ViewModel的参数绑定即可,两种可选交互方式:
- 输入过程中自动触发请求(配合ViewModel的防抖逻辑)
- 新增查询按钮,用户点击后才发起请求(避免无效请求,更推荐)
这里提供更常用的带查询按钮的实现:
import SwiftUI struct TestingText: View { @StateObject var viewModel = FetchResultsViewModel() @Binding var text: String var body: some View { VStack(spacing: 20) { TextField("Paste Clout Hexcode Here", text: $text) .font(.title2) .padding() .border(.gray) // 可选:按回车键也触发请求 .onSubmit { viewModel.postHashHex = text } Button { // 点击按钮将输入内容传给ViewModel发起请求 viewModel.postHashHex = text } label: { Text("查询内容") .font(.title3) .padding(.horizontal, 30) .padding(.vertical, 10) .background(.blue) .foregroundColor(.white) .cornerRadius(8) } Text(viewModel.clout.postFound?.body ?? "暂无数据") } .padding() } } struct TestingText_Previews: PreviewProvider { static var previews: some View { TestingText(text: .constant("8004bb672ad3f46118775cd4b2cb5306c63f6d68787457990bf0d2fda3f7993a")) } }
核心逻辑说明
- 用
@Published修饰ViewModel的postHashHex属性,既可以接收View层传入的输入内容,也可以自身监听变化自动触发请求 - 避免了数据跨层传递的冗余代码,符合SwiftUI的MVVM设计规范
- 输入和请求逻辑分离,后续要加输入校验、加载状态等扩展都很方便
内容的提问来源于stack exchange,提问作者DeveloperDanK
相关产品推荐
相关产品推荐

