Data coming from the network is not visible from the cell in ios uikit

44 views Asked by At

The data I pulled from the network appears as [Project Name.modelName (user ID: id: , title: , body: )]. It doesn't show up when I put it in the cell. How can I fix this? Thanks

fetch function

    func fetchPosts(completion: @escaping (Result<[Posts],Error>) -> Void){
        let url = URL(string:"https://jsonplaceholder.typicode.com/posts")!
        
        URLSession.shared.dataTask(with: url) { data, res, error in
            guard let data else{ return }
            
            do{
                let posts = try JSONDecoder().decode([Posts].self, from: data)
                completion(.success(posts))
                print(posts)
                
            }catch{
                completion(.failure(NSError()))
            }
        }.resume()
    }

Network json

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  }, ...
]

Protocols

protocol UserViewModelOutput: AnyObject{
    func updateView(title:[Posts])
}


protocol PostsService{
    func fetchPosts(completion: @escaping (Result<[Posts],Error>) -> Void)
}

TableViewController

class ViewController: UIViewController, UserViewModelOutput {
    
    private let viewModel: PostViewModel
    var postsArray = [Posts]()
    
    lazy var homeTableView: UITableView = {
        let tableView = UITableView()
        
        tableView.delegate = self
        tableView.dataSource = self
        
        tableView.register(HomeTableViewCell.self, forCellReuseIdentifier: HomeTableViewCell.reuseIdentifier)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        return tableView
    }()
    
    
    
    
    
    init(viewModel: PostViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
        self.viewModel.output = self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    

    
        
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        fetchUsers()
        view.backgroundColor = .yellow
    }
    
    private func setupViews() {
        NSLayoutConstraint.activate([
            homeTableView.topAnchor.constraint(equalTo: view.topAnchor),
            homeTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            homeTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            homeTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    
    func fetchUsers(){
        viewModel.fetchPost()
    }
    
    func updateView(title: [Posts]) {
        self.postsArray = title
    }
    
}

extension ViewController:UITableViewDataSource, UITableViewDelegate{
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print(postsArray.count)
        return postsArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = homeTableView.dequeueReusableCell(withIdentifier: HomeTableViewCell.reuseIdentifier, for: indexPath)
        cell.textLabel?.text = self.postsArray[indexPath.row].title
        
        return cell
    }
    
    
}

I added the data to the array, but it does not appear and the array count appears to be 0 when I print. When I print the incoming json data, it appears as [ProjectName.modelName (userId: id: , title: , body: )]. Where am I making a mistake? Thank you for your help

1

There are 1 answers

0
Maziar Saadatfar On

Your problem is Binding view to viewmodel (observe), there are a lot of way to set a binding that one of them I mention it:

You can define:

enum FetchViewModelChange {
    case success
    case error(Error)
}

and in Your ViewModel define a variable and a function like:

var fetchChangeHandler: ((FetchViewModelChange) -> Void)?

func emit(_ change: FetchViewModelChange) {
     fetchChangeHandler?(change)
}

after that in your fetchPosts function in viewModel:

func fetchPosts(completion: @escaping (Result<[Posts],Error>) -> Void){
        let url = URL(string:"https://jsonplaceholder.typicode.com/posts")!
        
        URLSession.shared.dataTask(with: url) { data, res, error in
            guard let data else{ return }
            
            do{
                let posts = try JSONDecoder().decode([Posts].self, from: data)
                completion(.success(posts))
                emit(.success)
                print(posts)
                
            } catch let error {
                completion(.failure(NSError()))
                emit(.error(error))
            }
        }.resume()
    }

and in your viewController you should define this function:

 func bindViewModel() {
        viewModel.fetchChangeHandler = { [weak self] change in
            guard let strongSelf = self else { return }
            switch change {
            case .didSuccess:
                strongSelf.postArray = viewModel.posts
                strongSelf.tableView.reload()
            case .didError(let error):
                debugPrint(error)
            }
        }
    }

and finally you should call it in viewDidLoad:

bindViewModel()