为什么我在使用 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()
}
}
}
问题出在您的 SearchUserViewPresenter
的 view
属性 中。
在这一行 self.view.reloadData(result)
中,问题是因为 self.view
是 nil
.
您没有将 view
添加到 SearchUserViewPresenter
。
只需在 SearchUserViewController
.
中设置 presenter
的 view
只需在 SearchUserViewController
的 viewDidLoad
中声明即可
presenter.view = self
并将 view
设置为 SearchUserViewPresenter
中的 public 以通过演示者外部设置此 属性。
public weak var view: UserView!
我正在尝试使用 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()
}
}
}
问题出在您的 SearchUserViewPresenter
的 view
属性 中。
在这一行 self.view.reloadData(result)
中,问题是因为 self.view
是 nil
.
您没有将 view
添加到 SearchUserViewPresenter
。
只需在 SearchUserViewController
.
presenter
的 view
只需在 SearchUserViewController
的 viewDidLoad
presenter.view = self
并将 view
设置为 SearchUserViewPresenter
中的 public 以通过演示者外部设置此 属性。
public weak var view: UserView!