为什么我在使用 MVP 时在 Presenter 中得到的是 nil?

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

我正在尝试使用 MVP 从 GitHub API 获取信息。
按您在 UISearchBar 中输入的字符搜索 GitHub 用户。
结果,我能够将信息从 Model 传递给 Presenter,但是 Presenter 的以下部分显示错误。

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

如何解决这个问题?

型号

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()
    }
}

主持人

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
        })
    }
}

查看

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()
        }
    }
}

问题出在您的 SearchUserViewPresenterview 属性 中。 在这一行 self.view.reloadData(result) 中,问题是因为 self.viewnil.

您没有将 view 添加到 SearchUserViewPresenter。 只需在 SearchUserViewController.

中设置 presenterview

只需在 SearchUserViewControllerviewDidLoad

中声明即可
presenter.view = self

并将 view 设置为 SearchUserViewPresenter 中的 public 以通过演示者外部设置此 属性。

public weak var view: UserView!