I would like to request iOS developer community to please review my POC i.e. [GithubUserFinder][1] which I have created as part of job screening test at T-mobile Bengaluru.
I have around 2 years of experience in programming and I got 24 hours to complete this POC.
Below is the feedback I got through mail by recruiter: As for Pooja, I will say probably no. Their code quality is a bit messy and sparse. They are missing a portion of the assignment and their resume doesn’t look the best, they are missing a year of work.
I request them to provide some pointer on 'messy and sparse code' or what is 'missing' in assignment but there was no reply.
Kindly request you guys to please go through my POC i.e [GithubUserFinder][1] and provide your valuable comments.
Here is the code for User search feature:
1. ViewController (search users)
import UIKit
import Alamofire
class ViewController: UIViewController {
@IBOutlet private weak var tableView: UITableView!
@IBOutlet private weak var searchBar: UISearchBar!
//datasource
private var users: [User] = [] {
didSet {
tableView.reloadData()
}
}
private enum constants {
static let userCellNibName = "UserCell"
static let cellReuseIdentifier = "Cell"
static let searchUsersPlaceholderText = "Search Users"
static let segueIdentifier = "detailSegue"
static let placeholderImageName = "placeholder"
}
//MARK:- View life cycle
override func viewDidLoad() {
super.viewDidLoad()
setupSearchBar()
setUpTableView()
}
override func viewWillAppear(_ animated: Bool) {
if let indexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: indexPath, animated: true)
}
}
//MARK:- Setup
private func setUpTableView() {
tableView.register(UINib(nibName: constants.userCellNibName, bundle: nil), forCellReuseIdentifier: constants.cellReuseIdentifier)
}
private func setupSearchBar() {
searchBar.placeholder = constants.searchUsersPlaceholderText
}
private func searchUsers(for name: String) {
ServiceManager.getUsers(for: name) {[weak self] (response) in
guard let self = self else { return }
guard let response = response as? SearchResponse else { return }
self.users = response.users
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == constants.segueIdentifier {
guard let destinationVC = segue.destination as? UserDetailViewController else {
return
}
destinationVC.user = sender as? User
}
}
}
//MARK:- TableView datasource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell: UserCell = tableView.dequeueReusableCell(withIdentifier: constants.cellReuseIdentifier, for: indexPath) as? UserCell else {
return UITableViewCell()
}
let user = users[indexPath.row]
cell.populate(user: user)
return cell
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: constants.segueIdentifier, sender: users[indexPath.row]);
}
}
//MARK:- SearchResultBar delegate
extension ViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if let text = searchBar.text, !text.isEmpty {
searchUsers(for: text)
} else {
users = []
}
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.endEditing(true)
}
}
2. TableView Cell (UserCell.swift)
import UIKit
import AlamofireImage
class UserCell: UITableViewCell {
@IBOutlet private weak var userImageView: UIImageView!
@IBOutlet private weak var nameLabel: UILabel!
@IBOutlet private weak var repoLable: UILabel!
private enum constants {
static let placeholderImageName = "placeholder"
}
func populate(user: User) {
nameLabel.text = user.login
let placeholderImage = UIImage(named: constants.placeholderImageName)
if let imageUrl = URL(string: user.avatarUrl) {
userImageView.af.setImage(withURL: imageUrl, cacheKey: "\(AppConstants.kCacheImagekey)\(user.id)", placeholderImage: placeholderImage);
}
}
}
3. Service Manager (ServiceManager.swift)
import Foundation
import Alamofire
enum ServiceType {
case users
case details
case repos
}
class ServiceManager {
typealias CompletionHandler = (_ response:Any?) -> Void
private static let baseUrl = "https://api.github.com/"
// Add your client Id and client secret here..
private static let clientId = ""
private static let clientSecret = ""
private static func getMethod(type: ServiceType) -> String {
switch type {
case .users:
return "search/users?q="
case .details, .repos:
return "users/"
}
}
private static func clientIdSecret() -> String {
//Add your client Id and client secret if you hit max hour limit which is only
// 50-60 request per hour. And enable this.
//return "&client_id=\(ServiceManager.clientId)&client_secret=\(ServiceManager.clinetSecret)"
return ""
}
public static func getUsers(for query: String, completion: @escaping CompletionHandler) {
let url = baseUrl + getMethod(type: .users) + query + clientIdSecret()
AF.request(url, parameters: nil).validate()
.responseDecodable(of: SearchResponse.self) { response in
guard let result = response.value else {
//TODO:- Currently the errors are not handled.
return
}
completion(result)
}
}
public static func getDetails(for query: String, completion: @escaping CompletionHandler) {
let url = baseUrl + getMethod(type: .details) + query
AF.request(url, parameters: nil).validate()
.responseDecodable(of: UserDetails.self) { response in
guard let result = response.value else { return }
completion(result)
}
}
public static func getRepos(for query: String, completion: @escaping CompletionHandler) {
let url = baseUrl + getMethod(type: .repos) + query + "/repos"
AF.request(url, parameters: nil).validate()
.responseDecodable(of: [Repo].self) { response in
guard let result = response.value else { return }
completion(result)
}
}
}
4. Model (Users.swift)
import Foundation
struct SearchResponse: Decodable {
let users: [User]
enum CodingKeys: String, CodingKey {
case users = "items"
}
}
struct User: Decodable {
let login: String
let id: Int
let avatarUrl: String
let url: String
let reposUrl: String
enum CodingKeys: String, CodingKey {
case login = "login"
case id = "id"
case avatarUrl = "avatar_url"
case url = "url"
case reposUrl = "repos_url"
}
}
[1]: https://github.com/pooja-iosTech13/GithubUserFinder