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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 13:54:04