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

在Swift中基于属性合并两个模型创建新模型的实现方法

解决方案

1. 修复PostWithUser模型,添加Identifiable协议

ForEach需要遍历遵循Identifiable的元素,给PostWithUser添加唯一标识字段并遵循协议:

struct PostWithUser: Identifiable {
    let id: Int // 复用帖子的id作为唯一标识
    let userImg: String
    let postUserName: String
    let postTitle: String
    let postBody: String
}

2. 优化ViewModel,确保异步请求完成后再关联数据

原代码中getPostsWithUser调用时机错误,异步请求未返回数据就执行关联,导致结果为空。这里提供两种优化方案:

方案一:用async/await重构(iOS 15+支持)

让请求逻辑更简洁,保证数据加载完成后再关联:

class DashboardViewModel: WebViewModel, ObservableObject {
    var manager: UserManager = UserManager()
    var postManager: PostManager = PostManager()
    
    @Published var users: [UserModel] = [] // 直接存储用户数组,简化结构
    @Published var posts: [PostModel] = [] // 直接存储帖子数组
    @Published var postsWithUser: [PostWithUser] = [] // 初始化为空数组
    
    // 并行加载用户和帖子数据,完成后生成关联列表
    func loadAllData() async {
        do {
            async let usersTask = manager.getUsersAsync()
            async let postsTask = postManager.getPostsAsync()
            
            let fetchedUsers = try await usersTask
            let fetchedPosts = try await postsTask
            
            DispatchQueue.main.async {
                self.users = fetchedUsers.users
                self.posts = fetchedPosts.posts
                self.generatePostsWithUser()
            }
        } catch {
            print("加载数据失败: \(error)")
        }
    }
    
    // 生成带用户信息的帖子列表
    private func generatePostsWithUser() {
        postsWithUser = posts.compactMap { post in
            guard let user = users.first(where: { $0.id == post.userId }) else {
                return nil // 找不到对应用户则跳过该帖子
            }
            return PostWithUser(
                id: post.id,
                userImg: user.image,
                postUserName: "\(user.firstName) \(user.lastName)",
                postTitle: post.title,
                postBody: post.body
            )
        }
    }
}

配套修改UserManager和PostManager,添加async/await版本的请求方法:

// UserManager示例
func getUsersAsync() async throws -> UsersModel {
    return try await withCheckedThrowingContinuation { continuation in
        getUsers { result in
            switch result {
            case .success(let users): continuation.resume(returning: users)
            case .failure(let error): continuation.resume(throwing: error)
            }
        }
    }
}

方案二:基于回调的状态检查

如果不支持async/await,通过标记请求状态,确保两个请求都完成后再关联:

class DashboardViewModel: WebViewModel, ObservableObject {
    var manager: UserManager = UserManager()
    var postManager: PostManager = PostManager()
    
    @Published var users: [UserModel] = []
    @Published var posts: [PostModel] = []
    @Published var postsWithUser: [PostWithUser] = []
    
    private var usersLoaded = false
    private var postsLoaded = false

    func getUsers() {
        manager.getUsers { [weak self] result in
            switch result {
            case .success(let usersModel):
                DispatchQueue.main.async {
                    self?.users = usersModel.users
                    self?.usersLoaded = true
                    self?.checkAndGeneratePosts()
                }
            case .failure(let error):
                print("获取用户失败: \(error)")
            }
        }
    }

    func getPosts() {
        postManager.getPosts { [weak self] result in
            switch result {
            case .success(let postsModel):
                DispatchQueue.main.async {
                    self?.posts = postsModel.posts
                    self?.postsLoaded = true
                    self?.checkAndGeneratePosts()
                }
            case .failure(let error):
                print("获取帖子失败: \(error)")
            }
        }
    }

    private func checkAndGeneratePosts() {
        guard usersLoaded && postsLoaded else { return }
        generatePostsWithUser()
    }

    private func generatePostsWithUser() {
        // 同方案一的generatePostsWithUser实现
    }
}

3. 优化View代码,构建Facebook风格帖子列表

修改View,正确渲染关联后的帖子数据:

struct DashboardView: View {
    @StateObject private var viewModel = DashboardViewModel()
    
    var body: some View {
        VStack {
            Text("Posts")
                .font(.title)
                .padding()
            
            List(viewModel.postsWithUser) { post in
                VStack(alignment: .leading, spacing: 8) {
                    // 用户信息行
                    HStack(spacing: 12) {
                        AsyncImage(url: URL(string: post.userImg)) { image in
                            image.resizable()
                                 .scaledToFill()
                                 .frame(width: 40, height: 40)
                                 .clipShape(Circle())
                        } placeholder: {
                            Circle().fill(.gray.opacity(0.3)).frame(width: 40, height: 40)
                        }
                        
                        Text(post.postUserName)
                            .font(.headline)
                    }
                    
                    // 帖子内容
                    VStack(alignment: .leading, spacing: 4) {
                        Text(post.postTitle)
                            .font(.subheadline)
                            .fontWeight(.medium)
                        Text(post.postBody)
                            .font(.body)
                            .foregroundColor(.secondary)
                    }
                    .padding(.top, 4)
                }
                .padding(.vertical, 8)
            }
            .listStyle(.plain)
        }
        .task {
            // 对应方案一:
            await viewModel.loadAllData()
            // 对应方案二:
            // viewModel.getUsers()
            // viewModel.getPosts()
        }
    }
}

关键问题说明

原代码核心问题:getPostsWithUser在异步请求未完成时执行,此时users和posts还是初始空数据,导致关联结果为空;同时PostWithUser未遵循Identifiable,ForEach无法正常遍历。优化后确保数据加载完成后再关联,用compactMap替代嵌套循环提升效率,同时构建符合需求的帖子UI样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 18:34:57