Swift异步获取JSON API数据后传入视图展示的实现问题
实现方案
核心逻辑是通过ObservableObject类型的 ViewModel 作为异步数据和视图之间的中介:你在异步回调中拿到解析好的User结构体后,更新 ViewModel 中被@Published修饰的属性,视图会自动监听到属性变化,完成UI刷新。
步骤1:实现 ViewModel 层
import SwiftUI class UserViewModel: ObservableObject { // 存储请求到的User数据,@Published修饰后值变化会自动通知视图 @Published var user: User? // 可选:存储错误信息用于异常状态展示 @Published var errorMsg: String? // 可选:控制加载状态展示 @Published var isLoading = false func loadUser() { isLoading = true errorMsg = nil // 直接复用你已实现的fetchUser方法 fetchUser { [weak self] result in // 网络请求回调在后台队列,必须切回主线程更新UI DispatchQueue.main.async { self?.isLoading = false switch result { case .success(let user): self?.user = user case .failure(let err): self?.errorMsg = err.localizedDescription } } } } }
步骤2:调整视图层实现
你原本的DisplayUser子视图不需要任何修改,只需新增父容器视图处理状态判断、触发请求即可:
struct UserContainerView: View { // 持有ViewModel,iOS14及以上用@StateObject,如果是父视图传入改用@ObservedObject @StateObject private var vm = UserViewModel() var body: some View { VStack(spacing: 16) { // 加载中状态 if vm.isLoading { ProgressView("正在加载用户信息") } // 错误状态 else if let error = vm.errorMsg { Text("加载失败:\(error)") .foregroundColor(.red) } // 请求成功,传入User结构体展示 else if let user = vm.user { DisplayUser(user: user) } } // 页面出现时触发网络请求 .onAppear { vm.loadUser() } } } // 你原有代码无需修改 struct DisplayUser: View { var user: User var body: some View { VStack(spacing: 8) { Text(user.userID) Text(user.userName) Text(user.lastName) Text(user.firstName) Text(user.fullName) } } }
可选优化(iOS15+)
如果项目最低支持iOS15及以上版本,可以用async/await简化异步逻辑,避免闭包循环引用问题:
// 改写原有fetchUser为async版本 func fetchUser() async throws -> User { guard let url = URL(string: "http://localhost:4001/user") else { throw NSError(domain: "InvalidURL", code: -1) } let (data, _) = try await URLSession.shared.data(from: url) return try JSONDecoder().decode(User.self, from: data) } // ViewModel对应修改加载方法 func loadUser() async { isLoading = true errorMsg = nil do { let user = try await fetchUser() await MainActor.run { self.user = user isLoading = false } } catch { await MainActor.run { errorMsg = error.localizedDescription isLoading = false } } } // 视图中触发请求的代码替换为task修饰符 .task { await vm.loadUser() }
注意事项
- 本地测试使用http请求时,需要在项目Info.plist中添加
NSAppTransportSecurity配置,打开NSAllowsArbitraryLoads开关,否则请求会被系统拦截。
内容的提问来源于stack exchange,提问作者John Sorensen
相关产品推荐
相关产品推荐

