在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
相关产品推荐
相关产品推荐

