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.