简体   繁体   中英

Why do I get nil in presenter when I use MVP?

I'm trying to get information from the GitHub API using MVP.
Search for GitHub users by the characters you enter in the UISearchBar.
I was able to pass the information from the Model to the Presenter as a result, but the following part of the Presenter shows an error.

self.view.reloadData(result)   // Unexpectedly found nil while implicitly unwrapping an Optional value

How do I solve this problem?

Model

import Foundation

class UserModel {
    
    //MARK: - Vars
    var userData = [SearchResult.UserData]()
    
    //MARK: - Fetch GitHubUser Data
    func fetchUserData(text: String, completion: @escaping ([SearchResult.UserData]) -> Void) {
        
        let urlString = "https://api.github.com/search/users?q=\(text.trimmingCharacters(in: .whitespaces))"
        let encode = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        guard let url = URL(string: encode) else {
            return
        }
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
            if let data = data {
                do {
                    let searchedUserData = try JSONDecoder().decode(SearchResult.self, from: data).items
                    self.userData = searchedUserData
                    dump(self.userData)
                    completion(self.userData)
                } catch {
                    print(error.localizedDescription)
                }
            }
        })
        task.resume()
    }
}

presenter

import Foundation

protocol Input {
    
    func didTappedSearchButton(searchText: String)
}

protocol UserView: AnyObject {
    
    func reloadData(_ users: [SearchResult.UserData])
}

final class SearchUserViewPresenter: Input {
    
    //MARK: - Vars
    var model = UserModel()
    private weak var view: UserView!
    
    //MARK: - Function
    func didTappedSearchButton(searchText: String) {

        print("Receive " + searchText)
        model.fetchUserData(text: searchText, completion: { result in
            print("Result",  result)
            self.view.reloadData(result) // Unexpectedly found nil while implicitly unwrapping an Optional value
        })
    }
}

view

import UIKit

class SearchUserViewController: UIViewController {
    
    //MARK: - Vars
    private var presenter = SearchUserViewPresenter()
    var userData = [SearchResult.UserData]()
    var selectedUrl: String!
    var userName: String!
    
    //MARK: - IBOutlet
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var searchBar: UISearchBar!
    
    //MARK: - View LifeCycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // tableViewの設定
        tableView.dataSource = self
        tableView.delegate = self
        tableView.tableFooterView = UIView(frame: .zero)
        
        // tableViewのcellにxibを設定
        let cellNib = UINib(nibName: "SearchUserTableViewCell", bundle: nil)
        tableView.register(cellNib, forCellReuseIdentifier: "Cell")
        
        // searchBarの設定
        searchBar.delegate = self
        searchBar.autocapitalizationType = .none
        searchBar.keyboardType = .alphabet
                
        navigationItem.title = "Search User"
   }
}



//MARK: - taleView DataSource
extension SearchUserViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return userData.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SearchUserTableViewCell
        let userViewData = userData[indexPath.row]
        cell.userNameLabel.text = userViewData.login
        cell.avatarImageView.image = UIImage(url: userViewData.avatarUrl)
        cell.userTypeLabel.text = userViewData.type
        
        return cell
    }
}


//MARK: - tableView Delegate
extension SearchUserViewController: UITableViewDelegate {
    
    func tableView(_ table: UITableView,didSelectRowAt indexPath: IndexPath) {
        
        tableView.deselectRow(at: indexPath, animated: true)
        let userViewData = userData[indexPath.row]
        selectedUrl = userViewData.url
        userName = userViewData.login
        performSegue(withIdentifier: "showUserDetail", sender: nil)
    }
        
    override func prepare(for segue: UIStoryboardSegue, sender: Any!) {
        
        if segue.identifier == "showUserDetail" {
            let userDetailVC: UserDetailViewController = segue.destination as! UserDetailViewController
            userDetailVC.userUrl = selectedUrl!
            userDetailVC.titleText = userName!
        }
    }
}


//MARK: - searchBar Delegate
extension SearchUserViewController: UISearchBarDelegate {
    
    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
        
        searchBar.setShowsCancelButton(true, animated: true)
    }
    
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        
        searchBar.endEditing(true)
        guard let text = searchBar.text else {return}
        presenter.didTappedSearchButton(searchText: text)
        searchBar.setShowsCancelButton(false, animated: true)
    }
    
    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        
        searchBar.endEditing(true)
        searchBar.text = ""
        searchBar.setShowsCancelButton(false, animated: true)
    }
}

//MARK: - Protocol UserView
extension SearchUserViewController: UserView {
    func reloadData(_ users: [SearchResult.UserData]) {

        userData = users
        print(users)
        if self.userData == [] {
            self.errorHUD()
        } else {
            self.tableView.reloadData()
            self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
            self.hideProgress()
        }
    }
}

The problem is in your SearchUserViewPresenter 's view property. In this line self.view.reloadData(result) the problem is because self.view is nil .

You didn't add a view to SearchUserViewPresenter . Just set the view of the presenter in SearchUserViewController .


Just declare this in SearchUserViewController 's viewDidLoad

presenter.view = self

And set view as a public in SearchUserViewPresenter to set this property through outside the presenter.

public weak var view: UserView!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM