skip to Main Content

Disclaimer: English is not my native tongue, but I’ll try my best to explain the problem.

In this image I have 2 Lists, top displays a list of PostModels and bottom a list of UserModels.

Both of which are taken from an API, I am trying to create a list where PostModels userId == UserModels id.

Expected Output: Is something like what you would see in a facebook post. Where you will see the user and text in the ListItem.

How do I achieve this?

Models

struct PostModel {
    let id: Int
    let userId: Int
    let title: String
    let body: String
}
struct UserModel {
    let id: Int
    let firstName: String
    let lastName: String
    let email: String
    let image: String
}

//The 3rd model where I will store data taken from Post and User
struct PostWithUser {
    let postUserId: Int
    let userImg: String
    let postUserName: String
    let postTitle: String
    let postBody: String
}

ViewModel

class DashboardViewModel: WebViewModel, ObservableObject {
    var manager: UserManager = UserManager()
    var postManager: PostManager = PostManager()
    
    @Published var users: UsersModel = UsersModel(users: [UserModel(id: 0, firstName: "", lastName: "", email: "", image: "")])
    @Published var posts: PostsModel = PostsModel(posts: [PostModel(id: 0,userId: 0, title: "", body: "")])
    
    @Published var postsWithUser = [PostWithUser(postUserId: 0, userImg: "",postUserName: "", postTitle: "", postBody: "")]
    
    func getUsers() {
        manager.getUsers { [weak self] result in
            switch result {
            case .success(let usersModel):
                DispatchQueue.main.async {
                    self?.users = usersModel
                }
            case .failure(let error):
                print("Error: (error)")
            }
        }
    }
    
    func getPosts() {
        postManager.getPosts { [weak self] result in
            switch result {
            case .success(let postsModel):
                DispatchQueue.main.async {
                    self?.posts = postsModel
                }
            case .failure(let error):
                print(error)
            }
        }
    }
    
    func getPostsWithUser() {
        for post in self.posts.posts {
            for user in self.users.users {
                if user.id == post.userId {
                    let post = PostWithUser(postUserId: post.userId, userImg: user.image, postUserName: "(user.firstName) (user.lastName)", postTitle: post.title, postBody: post.body)
                    
                    self.postsWithUser.append(post)
                }
            }
        }
    }
}

View

struct DashboardView: View {
    @StateObject private var viewModel = DashboardViewModel()
    
    var body: some View {
        let users = viewModel.users.users
        let posts = viewModel.posts.posts
        let detailedPosts = viewModel.postsWithUser
        
        VStack {
            Text("Posts")
            List {
                ForEach(detailedPosts) { post in
                    Text(post.postTitle)
                }
            }.task {
                viewModel.getPosts()
                viewModel.getUsers()
                viewModel.getPostsWithUser()
            } 
        }
    }
}

2

Answers


  1. One solution is to look up the user using a function rather than creating a new type.

    class DashboardViewModel: ObservableObject {
    
        var users: [UserModel] = [UserModel(id: 0, firstName: "", lastName: "", email: "", image: "")]
        @Published var posts: PostsModel = PostsModel(posts: [PostModel(id: 0,userId: 0, title: "", body: "")])
        
    
        func userWith(id: Int) -> UserModel {
            users.first(where: { $0.id == id}) ?? .unknown
        }
    
        //...
    }
    

    Where I have created an unknown user in an extension to UserModel

    extension UserModel {
        static let unknown = UserModel(id: -1, firstName: "", lastName: "Unknown", email: "", image: "")
    }
    

    And then use it in the view like

    ForEach(detailedPosts) { post in
        Text(post.postTitle)
        Text(viewModel.userWith(id: post.userId).email)
    }
                
    

    Note that since I don’t know what UsersModel looks like I changed it to an array in my code.

    Login or Signup to reply.
  2. @State var posts: [Post] = []
    @Environment(.myController) var controller
    let userID: User.ID
    
    var body: some View {
    ...
        .task(id: userID) { userID in
            posts = await controller.getPosts(userID)
    ...
    

    FYI StateObject isn’t designed for legacy view model objects, in SwiftUI it’s the job of the View struct hierarchy to encapsulate view data.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search