Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Desafio por Esdras Emanuel #9

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Xcode
#
## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
.build/

#Custom
/mockup-ios.png
Binary file added JavaPop/Custom Resources/userPic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added JavaPop/Custom Resources/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added JavaPop/Custom Resources/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
674 changes: 674 additions & 0 deletions JavaPop/JavaPop.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>JavaPop.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>7</integer>
</dict>
</dict>
</dict>
</plist>
10 changes: 10 additions & 0 deletions JavaPop/JavaPop.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
93 changes: 93 additions & 0 deletions JavaPop/JavaPop/Controller/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// AppDelegate.swift
// JavaPop
//
// Created by Esdras Emanuel on 20/10/17.
// Copyright © 2017 evtApps. All rights reserved.
//

import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
// self.saveContext() //TODO: USED FOR CORE DATA, CHECK
}

// // MARK: - Core Data stack
//
// lazy var persistentContainer: NSPersistentContainer = {
// /*
// The persistent container for the application. This implementation
// creates and returns a container, having loaded the store for the
// application to it. This property is optional since there are legitimate
// error conditions that could cause the creation of the store to fail.
// */
// let container = NSPersistentContainer(name: "JavaPop")
// container.loadPersistentStores(completionHandler: { (storeDescription, error) in
// if let error = error as NSError? {
// // Replace this implementation with code to handle the error appropriately.
// // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//
// /*
// Typical reasons for an error here include:
// * The parent directory does not exist, cannot be created, or disallows writing.
// * The persistent store is not accessible, due to permissions or data protection when the device is locked.
// * The device is out of space.
// * The store could not be migrated to the current model version.
// Check the error message to determine what the actual problem was.
// */
// fatalError("Unresolved error \(error), \(error.userInfo)")
// }
// })
// return container
// }()
//
// // MARK: - Core Data Saving support
//
// func saveContext () {
// let context = persistentContainer.viewContext
// if context.hasChanges {
// do {
// try context.save()
// } catch {
// // Replace this implementation with code to handle the error appropriately.
// // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
// let nserror = error as NSError
// fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
// }
// }
// }

}

230 changes: 230 additions & 0 deletions JavaPop/JavaPop/Controller/MainVC.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
//
// ViewController.swift
// JavaPop
//
// Created by Esdras Emanuel on 20/10/17.
// Copyright © 2017 evtApps. All rights reserved.
//

import UIKit
import Alamofire
import ObjectMapper
import AlamofireObjectMapper

class MainVC: UITableViewController{
private let cellId = "RepositoryCell"
private var repos = [Repository]() //object that will hold repositories data
private var nextUrl = "https://api.github.com/search/repositories?q=language:Java&sort=stars&page=1" //first url to be requested on API
private var lastUrl : String? // will hold what would be the last "page" provided by API
private var info = ["", ""] // used to pass information when performing a Segue to PullReqVC
private var lastPageReached = false // will be true whenever the last page provided by the API is reached

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

tableView.rowHeight = 150
tableView.isPagingEnabled = true

loadData(url: nextUrl)

let nib = UINib(nibName: "RepositoryCellNib", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: cellId)

}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? RepositoryCell else{
return UITableViewCell()
}

if repos.count > 0{

//add info from API to tableViewCells
let index = indexPath.row
let repo = repos[index]
cell.nameLbl.text = repo.name
cell.descTxt.text = repo.description
cell.userLbl.text = repo.owner?.username
if let forks = repo.forks{
cell.setForks(value: forks)
}else{
cell.setForks(value: 0)
}

if let stars = repo.stargazers_count{
cell.setStars(value: stars)
}else{
cell.setStars(value: 0)
}

if let link = repo.owner?.avatarUrl{
cell.userImg.downloadedFrom(link: link)
}

}

return cell
}

override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return repos.count
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let repo = repos[indexPath.row].name{
self.info[0] = repo
}
if let name = repos[indexPath.row].owner?.username{
self.info[1] = name
}


self.performSegue(withIdentifier: "MainVCPullReqVC", sender: info)
}

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row >= repos.count * 3/4{
loadData(url: nextUrl)
}
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PullReqVC{
if let info = sender as? [String]{
destination.nextUrl = "https://api.github.com/repos/\(info[1])/\(info[0])/pulls"
}

}
}

func loadData(url : String){
if !lastPageReached{
let URL = url
Alamofire.request(URL).responseObject { (response: DataResponse<SearchResponse>) in
switch response.result{
case .success( _) : do {
if let headers = response.response?.allHeaderFields{
if self.lastUrl == nil{ //add lastUrl if not added yet according to info provided by API
self.lastUrl = DataService.instance.lastPageLinkFrom(headers: headers)
}
self.nextUrl = DataService.instance.nextPageLinkFrom(headers: headers) //update nextUrl to be requested
let searchResponse = response.result.value
if let searchItems = searchResponse?.items{
for item in searchItems {
self.repos.append(item)
}
}
self.tableView.reloadData()
}
}
case .failure(let error) : do {
self.presentConnectionError(message: error.localizedDescription)
}
}

}
if let lastUrl = self.lastUrl{
if URL == lastUrl{ //check if lasPageReached
self.lastPageReached = true
}
}
}
}

func nextPageLinkFrom(headers: [AnyHashable : Any]) -> String{
var res = String()
if let link = headers["Link"] as? String{
let linkArr = link.components(separatedBy: " ")
res = linkArr[0]
res.removeFirst()
res.removeLast()
res.removeLast()
}
return res
}

func presentConnectionError(message: String){
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "Retry", style: .cancel, handler:{(action: UIAlertAction!) in
self.loadData(url: self.nextUrl)
})
alert.addAction(okAction)
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
}

//MARK: Methods for Unit Testing

func isInLastPage() -> Bool{
if lastPageReached{
return true
}else{
return false
}
}

func setLastUrl(url : String){
self.lastUrl = url
}

}

//MARK: JSON Mapping Classes

class SearchResponse : Mappable{

var items : [Repository]?

required init?(map: Map) {

}

func mapping(map: Map) {
items <- map["items"]
}

}


class Repository : Mappable{
var name : String?
var owner : User?
var description : String?
var forks : Int?
var stargazers_count : Int?

required init?(map: Map){
}

func mapping(map: Map) {
name <- map["name"]
owner <- map["owner"]
forks <- map["forks"]
description <- map["description"]
stargazers_count <- map["stargazers_count"]
}
}

class User : Mappable{

var username : String?
var avatarUrl : String?

required init?(map: Map) {

}

func mapping(map: Map) {
username <- map["login"]
avatarUrl <- map["avatar_url"]
}
}

Loading