diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8af3431 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Xcode +xcuserdata/ +project.xcworkspace/ +Pods/ \ No newline at end of file diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..a00d849 --- /dev/null +++ b/Podfile @@ -0,0 +1,21 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'mobile-coding-challenge' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for mobile-coding-challenge + pod 'Moya' + pod 'SwiftyJSON' + pod 'Kingfisher' + +end + +target 'mobile-coding-challenge-unit-tests' do + inherit! :search_paths + + pod 'Moya' + pod 'SwiftyJSON' + +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..6ed70c8 --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,32 @@ +PODS: + - Alamofire (5.2.2) + - Kingfisher (5.15.0): + - Kingfisher/Core (= 5.15.0) + - Kingfisher/Core (5.15.0) + - Moya (14.0.0): + - Moya/Core (= 14.0.0) + - Moya/Core (14.0.0): + - Alamofire (~> 5.0) + - SwiftyJSON (5.0.0) + +DEPENDENCIES: + - Kingfisher + - Moya + - SwiftyJSON + +SPEC REPOS: + trunk: + - Alamofire + - Kingfisher + - Moya + - SwiftyJSON + +SPEC CHECKSUMS: + Alamofire: 814429acc853c6c54ff123fc3d2ef66803823ce0 + Kingfisher: 1acb67597a520c2d0310677e66d6e0af455ef757 + Moya: 5b45dacb75adb009f97fde91c204c1e565d31916 + SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7 + +PODFILE CHECKSUM: 9a7dc13e8ed477ab04dc26d1d80a1fa72dece796 + +COCOAPODS: 1.9.3 diff --git a/README.md b/README.md index f83520b..b0d2b72 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,18 @@ -# Mobile Coding Challenge +# Mobile Coding Challenge by [Gemography](https://github.com/gemography) -## Idea of the App -The task is to implement a small app that will list the most starred Github repos that were created in the last 30 days. -You'll be fetching the sorted JSON data directly from the Github API (Github API explained down below). +## Idea of the App -## Features -* As a User I should be able to list the most starred Github repos that were created in the last 30 days. -* As a User I should see the results as a list. One repository per row. -* As a User I should be able to see for each repo/row the following details : - * Repository name - * Repository description - * Numbers of stars for the repo. - * Username and avatar of the owner. -* [BONUS] As a User I should be able to keep scrolling and new results should appear (pagination). +The task is just to implement a small app that will list the most starred Github repos that were created in the last 30 days. -## Things to keep in mind 🚨 -* Features are less important than code quality. Put more focus on code quality and less on speed and number of features implemented. -* Your code will be evaluated based on: code structure, programming best practices, legibility (and not number of features implemented or speed). -* The git commit history (and git commit messages) will be also evaluated. -* Do not forget to include few details about the project in the README (e.g explain choice of libraries, how to run it ...) +## How to run the challenge project? -## How to get the data from Github -To get the most starred Github repos created in the last 30 days (relative to 2017-11-22), you'll need to call the following endpoint : - -`https://api.github.com/search/repositories?q=created:>2017-10-22&sort=stars&order=desc` - -The JSON data from Github will be paginated (you'll receive around 100 repos per JSON page). - -To get the 2nd page, you add `&page=2` to the end of your API request : - -`https://api.github.com/search/repositories?q=created:>2017-10-22&sort=stars&order=desc&page=2` - -To get the 3rd page, you add `&page=3` ... etc - -You can read more about the Github API over [here](https://developer.github.com/v3/search/#search-repositories -). - -## Mockups -![alt text](https://raw.githubusercontent.com/hiddenfounders/mobile-coding-challenge/master/mockup.png) - -Here's what each element represents : - -![alt text](https://raw.githubusercontent.com/hiddenfounders/mobile-coding-challenge/master/row-explained.png) - - -## Technologies to use -Choose whatever mobile platform you're most familiar with. - -* For iOS, feel free to use Swift or Objective-C. -* For Android, feel free to use Kotlin or Java. +1. Clone this repo. +2. Open shell window and navigate to project folder. +3. Run +```bash +pod install +``` +4. Open mobile-coding-challenge.xcworkspace and run the project on selected device or simulator. +Good luck! diff --git a/mobile-coding-challenge-unit-tests/Info.plist b/mobile-coding-challenge-unit-tests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/mobile-coding-challenge-unit-tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/mobile-coding-challenge-unit-tests/MostStarredGithubReposJSONTests.swift b/mobile-coding-challenge-unit-tests/MostStarredGithubReposJSONTests.swift new file mode 100644 index 0000000..c6eef12 --- /dev/null +++ b/mobile-coding-challenge-unit-tests/MostStarredGithubReposJSONTests.swift @@ -0,0 +1,106 @@ +// +// MostStarredGithubReposJSONTests.swift +// mobile-coding-challenge-unit-tests +// +// Created by Mobile on 9/20/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import XCTest +import Moya +import SwiftyJSON + +class MostStarredGithubReposJSONTests: XCTestCase { + + override func setUp() { + super.setUp() + + self.continueAfterFailure = false + + } + + func testForIsTheMostStarredGithubReposJSONCorrect(){ + + let expectation = self.expectation(description: "theMostStarredGithubReposJSONExpectation") + + var previousMonthDate: Date? + + if let _previousMonthDate = Calendar.current.date(byAdding: .month, value: -1, to: Date()){ + + previousMonthDate = _previousMonthDate + + let requestProvider = MoyaProvider() + + requestProvider.request(.getMostGithubRepos(created_at: previousMonthDate!.formattedDate(using: "YYYY-MM-dd"), page: 1)) { result in + + switch result { + case let .success(moyaResponse): + + let responseJSON = JSON(moyaResponse.data) + + var mostStarredGithubRepos: [JSON]? + var totalCount : Int? + + if moyaResponse.statusCode == 200{ + + if let _mostStarredGithubRepos = responseJSON["items"].array{ + + if let _totalCount = responseJSON["total_count"].int{ + + mostStarredGithubRepos = _mostStarredGithubRepos + + totalCount = _totalCount + + var totalChecks: Int = 0 + + for _mostStarredGithubRepo in mostStarredGithubRepos!{ + + let mostStarredGithubRepo = GithubRepositoryEntity(from: _mostStarredGithubRepo) + + XCTAssertNotNil(mostStarredGithubRepo, "TEST: IS THE MOST STARRED GITHUB REPOS JSON CORRECT => Response JSON is not fit : Something missed from GithubRepositoryEntity") + + totalChecks += 1 + + } + + XCTAssertEqual(totalChecks, mostStarredGithubRepos!.count) + expectation.fulfill() + + return + + } + + XCTAssertNotNil(totalCount, "TEST: IS THE MOST STARRED GITHUB REPOS JSON CORRECT => Response JSON is not fit : (total_count => Int) is not exists or is not an integer.") + + return + + } + + XCTAssertNotNil(mostStarredGithubRepos, "TEST: IS THE MOST STARRED GITHUB REPOS JSON CORRECT => Response JSON is not fit : [items] is not exists or is not an array.") + + return + + } + + XCTAssertEqual(moyaResponse.statusCode, 200, "TEST: IS THE MOST STARRED GITHUB REPOS JSON CORRECT => Created_at is not fit") + expectation.fulfill() + + + default: + expectation.fulfill() + + } + + } + + self.wait(for: [expectation], timeout: 10.0) + + return + + } + + XCTAssertNotNil(previousMonthDate, "TEST: IS THE MOST STARRED GITHUB REPOS JSON CORRECT => Previous month date is incorrect") + + } + +} diff --git a/mobile-coding-challenge-unit-tests/ShortValueOfIntTests.swift b/mobile-coding-challenge-unit-tests/ShortValueOfIntTests.swift new file mode 100644 index 0000000..238cef3 --- /dev/null +++ b/mobile-coding-challenge-unit-tests/ShortValueOfIntTests.swift @@ -0,0 +1,23 @@ +// +// ShortValueOfIntTests.swift +// mobile-coding-challenge-unit-tests +// +// Created by Mobile on 9/20/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import XCTest + +class ShortValueOfIntTests: XCTestCase { + + func testForIsShortValueOfIntCorrect(){ + + XCTAssertEqual(100.shortValue, "100") + XCTAssertEqual((-800).shortValue, "-800") + XCTAssertEqual(8000.shortValue, "8.0K") + XCTAssertEqual(2000000.shortValue, "2.0M") + XCTAssertEqual(100000000000.shortValue, "+100...") + + } + +} diff --git a/mobile-coding-challenge.xcodeproj/project.pbxproj b/mobile-coding-challenge.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2c58ea2 --- /dev/null +++ b/mobile-coding-challenge.xcodeproj/project.pbxproj @@ -0,0 +1,673 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 75620E3B2516548300F87394 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E3A2516548300F87394 /* AppDelegate.swift */; }; + 75620E442516548700F87394 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 75620E432516548700F87394 /* Assets.xcassets */; }; + 75620E472516548700F87394 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 75620E452516548700F87394 /* LaunchScreen.storyboard */; }; + 75620E54251655E400F87394 /* MostStarredGithubReposProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E53251655E400F87394 /* MostStarredGithubReposProtocol.swift */; }; + 75620E5F251659B700F87394 /* BaseUINavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E5E251659B700F87394 /* BaseUINavigationController.swift */; }; + 75620E63251660A000F87394 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 75620E62251660A000F87394 /* Localizable.strings */; }; + 75620E6825166EB800F87394 /* UIViewController + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E6725166EB800F87394 /* UIViewController + Extension.swift */; }; + 75620E6925166F1300F87394 /* MostStarredGithubReposPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E572516561800F87394 /* MostStarredGithubReposPresenter.swift */; }; + 75620E6A25166F8B00F87394 /* MostStarredGithubReposView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E51251655D000F87394 /* MostStarredGithubReposView.swift */; }; + 75620E6B25166FE300F87394 /* MostStarredGithubReposUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E4F251655BC00F87394 /* MostStarredGithubReposUI.swift */; }; + 75620E6C251670E400F87394 /* MostStarredGithubReposRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E5B2516563100F87394 /* MostStarredGithubReposRouter.swift */; }; + 75620E6D2516719900F87394 /* MostStarredGithubReposTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E602516602900F87394 /* MostStarredGithubReposTableViewCell.swift */; }; + 75620E6E251671BF00F87394 /* MostStarredGithubReposInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E55251655FF00F87394 /* MostStarredGithubReposInteractor.swift */; }; + 75620E70251677C200F87394 /* MostStarredGithubReposEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E592516562600F87394 /* MostStarredGithubReposEntity.swift */; }; + 75620E722516895A00F87394 /* Int + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E712516895A00F87394 /* Int + Extension.swift */; }; + 75620E742516987F00F87394 /* String + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E732516987F00F87394 /* String + Extension.swift */; }; + 75620E772516A8B800F87394 /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E762516A8B800F87394 /* RequestHandler.swift */; }; + 75620E7A2516B5DF00F87394 /* Date + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E782516B58B00F87394 /* Date + Extension.swift */; }; + 7572ADA625175C2500C8679A /* ShortValueOfIntTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7572ADA525175C2500C8679A /* ShortValueOfIntTests.swift */; }; + 7572ADA725175CBD00C8679A /* Int + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E712516895A00F87394 /* Int + Extension.swift */; }; + 7572ADA925175DA000C8679A /* MostStarredGithubReposJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7572ADA825175DA000C8679A /* MostStarredGithubReposJSONTests.swift */; }; + 7572ADAA25175F2800C8679A /* MostStarredGithubReposEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E592516562600F87394 /* MostStarredGithubReposEntity.swift */; }; + 7572ADAB25175F3400C8679A /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E762516A8B800F87394 /* RequestHandler.swift */; }; + 7572ADAC251760B700C8679A /* Date + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75620E782516B58B00F87394 /* Date + Extension.swift */; }; + 7F2954C3013C6DFD1EB57D67 /* Pods_mobile_coding_challenge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F57D743DE22E5A3A3F84BF3F /* Pods_mobile_coding_challenge.framework */; }; + A01CAA4FF1F4C7C6B5E2D638 /* libPods-mobile-coding-challenge-unit-tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F66A9D032FA7F46C709E77 /* libPods-mobile-coding-challenge-unit-tests.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7572ADA025175BAF00C8679A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 75620E2F2516548300F87394 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 75620E362516548300F87394; + remoteInfo = "mobile-coding-challenge"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0F01CB3D02F0E5BDDFE0704F /* Pods-mobile-coding-challenge.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobile-coding-challenge.debug.xcconfig"; path = "Target Support Files/Pods-mobile-coding-challenge/Pods-mobile-coding-challenge.debug.xcconfig"; sourceTree = ""; }; + 4D18A88125FC096583B5DE6D /* Pods-mobile-coding-challenge.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobile-coding-challenge.release.xcconfig"; path = "Target Support Files/Pods-mobile-coding-challenge/Pods-mobile-coding-challenge.release.xcconfig"; sourceTree = ""; }; + 51F66A9D032FA7F46C709E77 /* libPods-mobile-coding-challenge-unit-tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-mobile-coding-challenge-unit-tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 75620E372516548300F87394 /* mobile-coding-challenge.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "mobile-coding-challenge.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 75620E3A2516548300F87394 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 75620E432516548700F87394 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 75620E462516548700F87394 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 75620E482516548700F87394 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 75620E4F251655BC00F87394 /* MostStarredGithubReposUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposUI.swift; sourceTree = ""; }; + 75620E51251655D000F87394 /* MostStarredGithubReposView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposView.swift; sourceTree = ""; }; + 75620E53251655E400F87394 /* MostStarredGithubReposProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposProtocol.swift; sourceTree = ""; }; + 75620E55251655FF00F87394 /* MostStarredGithubReposInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposInteractor.swift; sourceTree = ""; }; + 75620E572516561800F87394 /* MostStarredGithubReposPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposPresenter.swift; sourceTree = ""; }; + 75620E592516562600F87394 /* MostStarredGithubReposEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposEntity.swift; sourceTree = ""; }; + 75620E5B2516563100F87394 /* MostStarredGithubReposRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposRouter.swift; sourceTree = ""; }; + 75620E5E251659B700F87394 /* BaseUINavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseUINavigationController.swift; sourceTree = ""; }; + 75620E602516602900F87394 /* MostStarredGithubReposTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposTableViewCell.swift; sourceTree = ""; }; + 75620E62251660A000F87394 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + 75620E6725166EB800F87394 /* UIViewController + Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController + Extension.swift"; sourceTree = ""; }; + 75620E712516895A00F87394 /* Int + Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int + Extension.swift"; sourceTree = ""; }; + 75620E732516987F00F87394 /* String + Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String + Extension.swift"; sourceTree = ""; }; + 75620E762516A8B800F87394 /* RequestHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandler.swift; sourceTree = ""; }; + 75620E782516B58B00F87394 /* Date + Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date + Extension.swift"; sourceTree = ""; }; + 7572AD9B25175BAF00C8679A /* mobile-coding-challenge-unit-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "mobile-coding-challenge-unit-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7572AD9F25175BAF00C8679A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7572ADA525175C2500C8679A /* ShortValueOfIntTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortValueOfIntTests.swift; sourceTree = ""; }; + 7572ADA825175DA000C8679A /* MostStarredGithubReposJSONTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MostStarredGithubReposJSONTests.swift; sourceTree = ""; }; + A1A46AEB5C91E0ACBF4B1D5F /* Pods-mobile-coding-challenge-unit-tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobile-coding-challenge-unit-tests.debug.xcconfig"; path = "Target Support Files/Pods-mobile-coding-challenge-unit-tests/Pods-mobile-coding-challenge-unit-tests.debug.xcconfig"; sourceTree = ""; }; + C1CF037D9590E1CA8CBFC052 /* Pods-mobile-coding-challenge-unit-tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobile-coding-challenge-unit-tests.release.xcconfig"; path = "Target Support Files/Pods-mobile-coding-challenge-unit-tests/Pods-mobile-coding-challenge-unit-tests.release.xcconfig"; sourceTree = ""; }; + F57D743DE22E5A3A3F84BF3F /* Pods_mobile_coding_challenge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_mobile_coding_challenge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 75620E342516548300F87394 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7F2954C3013C6DFD1EB57D67 /* Pods_mobile_coding_challenge.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7572AD9825175BAF00C8679A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A01CAA4FF1F4C7C6B5E2D638 /* libPods-mobile-coding-challenge-unit-tests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 75620E2E2516548300F87394 = { + isa = PBXGroup; + children = ( + 75620E392516548300F87394 /* mobile-coding-challenge */, + 7572AD9C25175BAF00C8679A /* mobile-coding-challenge-unit-tests */, + 75620E382516548300F87394 /* Products */, + 958ADBB33EB7D44312ABB616 /* Pods */, + AC28514262F839DE19CD30B2 /* Frameworks */, + ); + sourceTree = ""; + }; + 75620E382516548300F87394 /* Products */ = { + isa = PBXGroup; + children = ( + 75620E372516548300F87394 /* mobile-coding-challenge.app */, + 7572AD9B25175BAF00C8679A /* mobile-coding-challenge-unit-tests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 75620E392516548300F87394 /* mobile-coding-challenge */ = { + isa = PBXGroup; + children = ( + 75620E64251660A800F87394 /* Localizable Files */, + 75620E6625166E8D00F87394 /* Controllers */, + 75620E6525166E7A00F87394 /* Extensions */, + 75620E752516A8A500F87394 /* Helpers */, + 75620E3A2516548300F87394 /* AppDelegate.swift */, + 75620E432516548700F87394 /* Assets.xcassets */, + 75620E452516548700F87394 /* LaunchScreen.storyboard */, + 75620E482516548700F87394 /* Info.plist */, + ); + path = "mobile-coding-challenge"; + sourceTree = ""; + }; + 75620E4E2516557900F87394 /* Most Starred Github Repos */ = { + isa = PBXGroup; + children = ( + 75620E4F251655BC00F87394 /* MostStarredGithubReposUI.swift */, + 75620E602516602900F87394 /* MostStarredGithubReposTableViewCell.swift */, + 75620E51251655D000F87394 /* MostStarredGithubReposView.swift */, + 75620E53251655E400F87394 /* MostStarredGithubReposProtocol.swift */, + 75620E55251655FF00F87394 /* MostStarredGithubReposInteractor.swift */, + 75620E572516561800F87394 /* MostStarredGithubReposPresenter.swift */, + 75620E592516562600F87394 /* MostStarredGithubReposEntity.swift */, + 75620E5B2516563100F87394 /* MostStarredGithubReposRouter.swift */, + ); + path = "Most Starred Github Repos"; + sourceTree = ""; + }; + 75620E5D2516597800F87394 /* Base Controllers */ = { + isa = PBXGroup; + children = ( + 75620E5E251659B700F87394 /* BaseUINavigationController.swift */, + ); + path = "Base Controllers"; + sourceTree = ""; + }; + 75620E64251660A800F87394 /* Localizable Files */ = { + isa = PBXGroup; + children = ( + 75620E62251660A000F87394 /* Localizable.strings */, + ); + path = "Localizable Files"; + sourceTree = ""; + }; + 75620E6525166E7A00F87394 /* Extensions */ = { + isa = PBXGroup; + children = ( + 75620E6725166EB800F87394 /* UIViewController + Extension.swift */, + 75620E712516895A00F87394 /* Int + Extension.swift */, + 75620E732516987F00F87394 /* String + Extension.swift */, + 75620E782516B58B00F87394 /* Date + Extension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 75620E6625166E8D00F87394 /* Controllers */ = { + isa = PBXGroup; + children = ( + 75620E5D2516597800F87394 /* Base Controllers */, + 75620E4E2516557900F87394 /* Most Starred Github Repos */, + ); + path = Controllers; + sourceTree = ""; + }; + 75620E752516A8A500F87394 /* Helpers */ = { + isa = PBXGroup; + children = ( + 75620E762516A8B800F87394 /* RequestHandler.swift */, + ); + path = Helpers; + sourceTree = ""; + }; + 7572AD9C25175BAF00C8679A /* mobile-coding-challenge-unit-tests */ = { + isa = PBXGroup; + children = ( + 7572ADA825175DA000C8679A /* MostStarredGithubReposJSONTests.swift */, + 7572ADA525175C2500C8679A /* ShortValueOfIntTests.swift */, + 7572AD9F25175BAF00C8679A /* Info.plist */, + ); + path = "mobile-coding-challenge-unit-tests"; + sourceTree = ""; + }; + 958ADBB33EB7D44312ABB616 /* Pods */ = { + isa = PBXGroup; + children = ( + 0F01CB3D02F0E5BDDFE0704F /* Pods-mobile-coding-challenge.debug.xcconfig */, + 4D18A88125FC096583B5DE6D /* Pods-mobile-coding-challenge.release.xcconfig */, + A1A46AEB5C91E0ACBF4B1D5F /* Pods-mobile-coding-challenge-unit-tests.debug.xcconfig */, + C1CF037D9590E1CA8CBFC052 /* Pods-mobile-coding-challenge-unit-tests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + AC28514262F839DE19CD30B2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F57D743DE22E5A3A3F84BF3F /* Pods_mobile_coding_challenge.framework */, + 51F66A9D032FA7F46C709E77 /* libPods-mobile-coding-challenge-unit-tests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 75620E362516548300F87394 /* mobile-coding-challenge */ = { + isa = PBXNativeTarget; + buildConfigurationList = 75620E4B2516548700F87394 /* Build configuration list for PBXNativeTarget "mobile-coding-challenge" */; + buildPhases = ( + B02EBC2749D9FC5BBDB2C878 /* [CP] Check Pods Manifest.lock */, + 75620E332516548300F87394 /* Sources */, + 75620E342516548300F87394 /* Frameworks */, + 75620E352516548300F87394 /* Resources */, + 47EB194D8AC28AD0E4900581 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "mobile-coding-challenge"; + productName = "mobile-coding-challenge"; + productReference = 75620E372516548300F87394 /* mobile-coding-challenge.app */; + productType = "com.apple.product-type.application"; + }; + 7572AD9A25175BAF00C8679A /* mobile-coding-challenge-unit-tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7572ADA425175BAF00C8679A /* Build configuration list for PBXNativeTarget "mobile-coding-challenge-unit-tests" */; + buildPhases = ( + DAD24B4F7197FA6D4240AF37 /* [CP] Check Pods Manifest.lock */, + 7572AD9725175BAF00C8679A /* Sources */, + 7572AD9825175BAF00C8679A /* Frameworks */, + 7572AD9925175BAF00C8679A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7572ADA125175BAF00C8679A /* PBXTargetDependency */, + ); + name = "mobile-coding-challenge-unit-tests"; + productName = "mobile-coding-challenge-unit-tests"; + productReference = 7572AD9B25175BAF00C8679A /* mobile-coding-challenge-unit-tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 75620E2F2516548300F87394 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1170; + LastUpgradeCheck = 1170; + ORGANIZATIONNAME = Zakariae; + TargetAttributes = { + 75620E362516548300F87394 = { + CreatedOnToolsVersion = 11.7; + }; + 7572AD9A25175BAF00C8679A = { + CreatedOnToolsVersion = 11.7; + TestTargetID = 75620E362516548300F87394; + }; + }; + }; + buildConfigurationList = 75620E322516548300F87394 /* Build configuration list for PBXProject "mobile-coding-challenge" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 75620E2E2516548300F87394; + productRefGroup = 75620E382516548300F87394 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 75620E362516548300F87394 /* mobile-coding-challenge */, + 7572AD9A25175BAF00C8679A /* mobile-coding-challenge-unit-tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 75620E352516548300F87394 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 75620E472516548700F87394 /* LaunchScreen.storyboard in Resources */, + 75620E63251660A000F87394 /* Localizable.strings in Resources */, + 75620E442516548700F87394 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7572AD9925175BAF00C8679A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 47EB194D8AC28AD0E4900581 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-mobile-coding-challenge/Pods-mobile-coding-challenge-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-mobile-coding-challenge/Pods-mobile-coding-challenge-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-mobile-coding-challenge/Pods-mobile-coding-challenge-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + B02EBC2749D9FC5BBDB2C878 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-mobile-coding-challenge-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + DAD24B4F7197FA6D4240AF37 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-mobile-coding-challenge-unit-tests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 75620E332516548300F87394 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 75620E772516A8B800F87394 /* RequestHandler.swift in Sources */, + 75620E6C251670E400F87394 /* MostStarredGithubReposRouter.swift in Sources */, + 75620E70251677C200F87394 /* MostStarredGithubReposEntity.swift in Sources */, + 75620E6825166EB800F87394 /* UIViewController + Extension.swift in Sources */, + 75620E54251655E400F87394 /* MostStarredGithubReposProtocol.swift in Sources */, + 75620E5F251659B700F87394 /* BaseUINavigationController.swift in Sources */, + 75620E6B25166FE300F87394 /* MostStarredGithubReposUI.swift in Sources */, + 75620E6E251671BF00F87394 /* MostStarredGithubReposInteractor.swift in Sources */, + 75620E7A2516B5DF00F87394 /* Date + Extension.swift in Sources */, + 75620E722516895A00F87394 /* Int + Extension.swift in Sources */, + 75620E6925166F1300F87394 /* MostStarredGithubReposPresenter.swift in Sources */, + 75620E3B2516548300F87394 /* AppDelegate.swift in Sources */, + 75620E742516987F00F87394 /* String + Extension.swift in Sources */, + 75620E6D2516719900F87394 /* MostStarredGithubReposTableViewCell.swift in Sources */, + 75620E6A25166F8B00F87394 /* MostStarredGithubReposView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7572AD9725175BAF00C8679A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7572ADA625175C2500C8679A /* ShortValueOfIntTests.swift in Sources */, + 7572ADAB25175F3400C8679A /* RequestHandler.swift in Sources */, + 7572ADA725175CBD00C8679A /* Int + Extension.swift in Sources */, + 7572ADAA25175F2800C8679A /* MostStarredGithubReposEntity.swift in Sources */, + 7572ADA925175DA000C8679A /* MostStarredGithubReposJSONTests.swift in Sources */, + 7572ADAC251760B700C8679A /* Date + Extension.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7572ADA125175BAF00C8679A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 75620E362516548300F87394 /* mobile-coding-challenge */; + targetProxy = 7572ADA025175BAF00C8679A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 75620E452516548700F87394 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 75620E462516548700F87394 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 75620E492516548700F87394 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 75620E4A2516548700F87394 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 75620E4C2516548700F87394 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0F01CB3D02F0E5BDDFE0704F /* Pods-mobile-coding-challenge.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "mobile-coding-challenge/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.zakariae.mobile-coding-challenge"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 75620E4D2516548700F87394 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4D18A88125FC096583B5DE6D /* Pods-mobile-coding-challenge.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "mobile-coding-challenge/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.zakariae.mobile-coding-challenge"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 7572ADA225175BAF00C8679A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A1A46AEB5C91E0ACBF4B1D5F /* Pods-mobile-coding-challenge-unit-tests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "mobile-coding-challenge-unit-tests/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 13.7; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.zakariae.mobile-coding-challenge-unit-tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mobile-coding-challenge.app/mobile-coding-challenge"; + }; + name = Debug; + }; + 7572ADA325175BAF00C8679A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C1CF037D9590E1CA8CBFC052 /* Pods-mobile-coding-challenge-unit-tests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "mobile-coding-challenge-unit-tests/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 13.7; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.zakariae.mobile-coding-challenge-unit-tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mobile-coding-challenge.app/mobile-coding-challenge"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 75620E322516548300F87394 /* Build configuration list for PBXProject "mobile-coding-challenge" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 75620E492516548700F87394 /* Debug */, + 75620E4A2516548700F87394 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 75620E4B2516548700F87394 /* Build configuration list for PBXNativeTarget "mobile-coding-challenge" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 75620E4C2516548700F87394 /* Debug */, + 75620E4D2516548700F87394 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7572ADA425175BAF00C8679A /* Build configuration list for PBXNativeTarget "mobile-coding-challenge-unit-tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7572ADA225175BAF00C8679A /* Debug */, + 7572ADA325175BAF00C8679A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 75620E2F2516548300F87394 /* Project object */; +} diff --git a/mobile-coding-challenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mobile-coding-challenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..dcb467b --- /dev/null +++ b/mobile-coding-challenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile-coding-challenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile-coding-challenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/mobile-coding-challenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile-coding-challenge.xcodeproj/xcuserdata/mobile.xcuserdatad/xcschemes/xcschememanagement.plist b/mobile-coding-challenge.xcodeproj/xcuserdata/mobile.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..4ce5fd5 --- /dev/null +++ b/mobile-coding-challenge.xcodeproj/xcuserdata/mobile.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + mobile-coding-challenge.xcscheme_^#shared#^_ + + orderHint + 9 + + + + diff --git a/mobile-coding-challenge.xcworkspace/contents.xcworkspacedata b/mobile-coding-challenge.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..e521183 --- /dev/null +++ b/mobile-coding-challenge.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/mobile-coding-challenge.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile-coding-challenge.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/mobile-coding-challenge.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile-coding-challenge.xcworkspace/xcuserdata/mobile.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/mobile-coding-challenge.xcworkspace/xcuserdata/mobile.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..c7cb89f --- /dev/null +++ b/mobile-coding-challenge.xcworkspace/xcuserdata/mobile.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/mobile-coding-challenge/AppDelegate.swift b/mobile-coding-challenge/AppDelegate.swift new file mode 100644 index 0000000..58ce5e3 --- /dev/null +++ b/mobile-coding-challenge/AppDelegate.swift @@ -0,0 +1,37 @@ +// +// AppDelegate.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import UIKit +import Kingfisher + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + self.window = UIWindow(frame: UIScreen.main.bounds) + self.window?.rootViewController = BaseUINavigationController(rootViewController: MostStarredGithubReposRouter.createModule()) + self.window?.makeKeyAndVisible() + + self.clearKFCache() + + return true + + } + + private func clearKFCache(){ + KingfisherManager.shared.cache.clearMemoryCache() + KingfisherManager.shared.cache.clearDiskCache() + KingfisherManager.shared.cache.cleanExpiredDiskCache() + } + + +} + diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/100.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/100.png new file mode 100644 index 0000000..b75a015 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/100.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/1024.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000..e623c74 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/114.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000..23f3a46 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/114.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/120.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000..a2ec2e8 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/120.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/144.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/144.png new file mode 100644 index 0000000..b211ff8 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/144.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/152.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000..a60a3c1 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/167.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000..0ec7c22 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/180.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000..d63898c Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/20.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000..667c064 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/29.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000..b9e8675 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/40.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000..8f055af Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/50.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/50.png new file mode 100644 index 0000000..cce72c2 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/50.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/57.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000..02ef1ab Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/57.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/58.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000..3e0fe58 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/60.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000..4f14b8f Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/72.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/72.png new file mode 100644 index 0000000..86ef20b Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/72.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/76.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000..8a34963 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/80.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000..dc72c45 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/87.png b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000..b47c4b2 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/Contents.json b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..65b74d7 --- /dev/null +++ b/mobile-coding-challenge/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1 @@ +{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]} \ No newline at end of file diff --git a/mobile-coding-challenge/Assets.xcassets/Contents.json b/mobile-coding-challenge/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/mobile-coding-challenge/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/Contents.json b/mobile-coding-challenge/Assets.xcassets/Icons/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/mobile-coding-challenge/Assets.xcassets/Icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/Contents.json b/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/Contents.json new file mode 100644 index 0000000..3e723af --- /dev/null +++ b/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "empty-avatar-icon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "empty-avatar-icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "empty-avatar-icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon.png b/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon.png new file mode 100644 index 0000000..72edda8 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon@2x.png b/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon@2x.png new file mode 100644 index 0000000..6627c25 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon@2x.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon@3x.png b/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon@3x.png new file mode 100644 index 0000000..d4a4d5d Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/Icons/empty-avatar-icon.imageset/empty-avatar-icon@3x.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/Contents.json b/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/Contents.json new file mode 100644 index 0000000..1c9348d --- /dev/null +++ b/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "star_icon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "star_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "star_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon.png b/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon.png new file mode 100644 index 0000000..89e8daf Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon@2x.png b/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon@2x.png new file mode 100644 index 0000000..c14cc8a Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon@2x.png differ diff --git a/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon@3x.png b/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon@3x.png new file mode 100644 index 0000000..f76a329 Binary files /dev/null and b/mobile-coding-challenge/Assets.xcassets/Icons/star_icon.imageset/star_icon@3x.png differ diff --git a/mobile-coding-challenge/Base.lproj/LaunchScreen.storyboard b/mobile-coding-challenge/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/mobile-coding-challenge/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile-coding-challenge/Controllers/Base Controllers/BaseUINavigationController.swift b/mobile-coding-challenge/Controllers/Base Controllers/BaseUINavigationController.swift new file mode 100644 index 0000000..1dfb9e1 --- /dev/null +++ b/mobile-coding-challenge/Controllers/Base Controllers/BaseUINavigationController.swift @@ -0,0 +1,20 @@ +// +// BaseUINavigationController.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import UIKit + +class BaseUINavigationController: UINavigationController { + + override func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) + + } + +} diff --git a/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposEntity.swift b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposEntity.swift new file mode 100644 index 0000000..71ec1aa --- /dev/null +++ b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposEntity.swift @@ -0,0 +1,83 @@ +// +// MostStarredGithubReposEntity.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import Foundation +import SwiftyJSON + +class GithubRepositoryEntity{ + + private var id : Int! + private var name : String! + private var description : String? + private var ownerImageLink : String? + private var ownerName : String! + private var numberOfStars : Int! + private var shortNumberOfStars: String? + + func getId() -> Int{ return self.id } + + func getName()->String{ return self.name } + + func getDescription()->String{ return self.description ?? "" } + + func getOwnerImageURL()->URL?{ + if let imageURL = URL(string: self.ownerImageLink ?? ""){ + return imageURL + } + return nil + } + + func getOwnerName()->String{ return self.ownerName } + + func getNumberOfStars()->Int{ return self.numberOfStars } + + func getShortNumberOfStars()->String{ return self.shortNumberOfStars ?? "" } + + private func setNumberOfStars(numberOfStars: Int){ + + self.numberOfStars = numberOfStars + self.shortNumberOfStars = numberOfStars.shortValue + + } + +} + +extension GithubRepositoryEntity{ + + convenience init?(from repoJSON: JSON){ + self.init() + + let ownerObject = repoJSON["owner"].dictionaryObject + + if let repoId = repoJSON["id"].int, + let repoName = repoJSON["name"].string, + let ownerName = ownerObject?["login"] as? String, + let numberOfStars = repoJSON["stargazers_count"].int{ + + self.id = repoId + self.name = repoName + self.ownerName = ownerName + self.setNumberOfStars(numberOfStars: numberOfStars) + + if let description = repoJSON["description"].string{ + self.description = description + } + + if let avatarStringURL = ownerObject?["avatar_url"] as? String{ + self.ownerImageLink = avatarStringURL + } + + return + + } + + return nil + + } + +} diff --git a/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposInteractor.swift b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposInteractor.swift new file mode 100644 index 0000000..14ac46b --- /dev/null +++ b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposInteractor.swift @@ -0,0 +1,69 @@ +// +// MostStarredGithubReposInteractor.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import Moya +import SwiftyJSON + +class MostStarredGithubReposInteractor: PresenterToMostStarredGithubReposInteractorProtocol{ + + var presenter: InteractorToMostStarredGithubReposPresenterProtocol! + + func getMostStarredGithubRepos(from page: Int, isToUsePullRefresh: Bool) { + + let requestHandler = MoyaProvider() + + if let previousMonthDate = Calendar.current.date(byAdding: .month, value: -1, to: Date()){ + + requestHandler.request(.getMostGithubRepos(created_at: previousMonthDate.formattedDate(using: "YYYY-MM-dd"), page: page)){ result in + + switch result { + case let .success(moyaResponse): + + let responseJSON = JSON(moyaResponse.data) + + var mostStarredGithubRepos: [GithubRepositoryEntity] = [] + var totalCount: Int = 0 + + if let _mostStarredGithubRepos = responseJSON["items"].array, + let _totalCount = responseJSON["total_count"].int{ + + totalCount = _totalCount + + for _mostStarredGithubRepo in _mostStarredGithubRepos{ + + if let mostStarredGithubRepo = GithubRepositoryEntity(from: _mostStarredGithubRepo){ + + mostStarredGithubRepos.append(mostStarredGithubRepo) + + } + + } + + } + + self.presenter.mostStarredGithubReposSuccessFetch(repos: mostStarredGithubRepos, isToUsePullRefresh: isToUsePullRefresh, totalCount: totalCount) + + + default: + + self.presenter.mostStarredGithubReposSuccessFailure() + + } + + } + + return + + } + + self.presenter.mostStarredGithubReposSuccessFailure() + + } + + +} diff --git a/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposPresenter.swift b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposPresenter.swift new file mode 100644 index 0000000..896f848 --- /dev/null +++ b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposPresenter.swift @@ -0,0 +1,63 @@ +// +// MostStarredGithubReposPresenter.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import Foundation +class MostStarredGithubReposPresenter: ViewToMostStarredGithubReposPresenterProtocol, InteractorToMostStarredGithubReposPresenterProtocol{ + + weak var view: PresenterToMostStarredGithubReposViewProtocol! + var interactor: PresenterToMostStarredGithubReposInteractorProtocol! + var router: PresenterToMostStarredGithubReposRouterProtocol! + var mostStarredGithubRepos: [GithubRepositoryEntity]! = [] + + var numberOfRows: Int!{ + return self.mostStarredGithubRepos.count + } + var totalNumberOfMostStarredGithubRepos: Int! = -1 + var activePage: Int! = 1 + + convenience init(view: PresenterToMostStarredGithubReposViewProtocol, interactor: PresenterToMostStarredGithubReposInteractorProtocol, router: PresenterToMostStarredGithubReposRouterProtocol) { + self.init() + + self.view = view + self.interactor = interactor + self.router = router + + } + + func viewDidLoad() { + + self.mostStarredGithubRepos = [] + self.view.showLoader() + self.interactor.getMostStarredGithubRepos(from: self.activePage, isToUsePullRefresh: false) + + } + + func mostStarredGithubReposSuccessFetch(repos: [GithubRepositoryEntity], isToUsePullRefresh: Bool, totalCount: Int) { + + self.totalNumberOfMostStarredGithubRepos = totalCount + + if isToUsePullRefresh{ + self.mostStarredGithubRepos = repos + }else{ + self.mostStarredGithubRepos.append(contentsOf: repos) + self.activePage += 1 + } + + self.view.hideLoader() + self.view.reloadData() + + } + + func mostStarredGithubReposSuccessFailure() { + + self.view.hideLoader() + self.view.showResponseError() + + } + +} diff --git a/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposProtocol.swift b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposProtocol.swift new file mode 100644 index 0000000..1e3386d --- /dev/null +++ b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposProtocol.swift @@ -0,0 +1,43 @@ +// +// MostStarredGithubReposProtocol.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import UIKit + +protocol PresenterToMostStarredGithubReposViewProtocol: class{ + var presenter: ViewToMostStarredGithubReposPresenterProtocol! { get set } + func showLoader() + func hideLoader() + func showResponseError() + func reloadData() + func refreshMostStarredGithubReposData() +} + +protocol ViewToMostStarredGithubReposPresenterProtocol{ + var view: PresenterToMostStarredGithubReposViewProtocol! { get set } + var interactor: PresenterToMostStarredGithubReposInteractorProtocol! { get set } + var router: PresenterToMostStarredGithubReposRouterProtocol! { get set } + var mostStarredGithubRepos: [GithubRepositoryEntity]! { get set } + var numberOfRows: Int! { get } + var totalNumberOfMostStarredGithubRepos: Int! { get set } + var activePage: Int! { get set } + func viewDidLoad() +} + +protocol PresenterToMostStarredGithubReposInteractorProtocol{ + var presenter: InteractorToMostStarredGithubReposPresenterProtocol! { get set } + func getMostStarredGithubRepos(from page: Int, isToUsePullRefresh: Bool) +} + +protocol InteractorToMostStarredGithubReposPresenterProtocol{ + func mostStarredGithubReposSuccessFetch(repos: [GithubRepositoryEntity], isToUsePullRefresh: Bool, totalCount: Int) + func mostStarredGithubReposSuccessFailure() +} + +protocol PresenterToMostStarredGithubReposRouterProtocol{ + static func createModule()->UIViewController +} diff --git a/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposRouter.swift b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposRouter.swift new file mode 100644 index 0000000..ceff9ec --- /dev/null +++ b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposRouter.swift @@ -0,0 +1,32 @@ +// +// MostStarredGithubReposRouter.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import UIKit + +class MostStarredGithubReposRouter: PresenterToMostStarredGithubReposRouterProtocol{ + + static func createModule() -> UIViewController { + + let mostStarredGithubReposView = MostStarredGithubReposView() + let mostStarredGithubReposInteractor = MostStarredGithubReposInteractor() + let mostStarredGithubReposRouter = MostStarredGithubReposRouter() + + let mostStarredGithubReposPresenter = MostStarredGithubReposPresenter( + view: mostStarredGithubReposView, + interactor: mostStarredGithubReposInteractor, + router: mostStarredGithubReposRouter + ) + + mostStarredGithubReposView.presenter = mostStarredGithubReposPresenter + mostStarredGithubReposInteractor.presenter = mostStarredGithubReposPresenter + + return mostStarredGithubReposView + + } + +} diff --git a/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposTableViewCell.swift b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposTableViewCell.swift new file mode 100644 index 0000000..9a52969 --- /dev/null +++ b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposTableViewCell.swift @@ -0,0 +1,139 @@ +// +// MostStarredGithubReposTableViewCell.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import UIKit +import Kingfisher + +class MostStarredGithubReposTableViewCell: UITableViewCell { + + private let repositoryNameLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = .boldSystemFont(ofSize: 17) + label.numberOfLines = 2 + return label + }() + + private let repositoryDescriptionLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = .systemFont(ofSize: 13) + label.numberOfLines = 3 + return label + }() + + private let repositoryOwnerImageView: UIImageView = { + let imageView = UIImageView() + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.image = #imageLiteral(resourceName: "empty-avatar-icon") + return imageView + }() + + private let repositoryOwnerNameLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = .systemFont(ofSize: 13) + return label + }() + + private let repositoryStarImageView: UIImageView = { + let imageView = UIImageView() + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.image = #imageLiteral(resourceName: "star_icon") + return imageView + }() + + private let repositoryNumberOfStarsLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = .systemFont(ofSize: 13) + return label + }() + + private let topBottomSpacing : CGFloat = 20 + private let leadingTrailingSpacing: CGFloat = 30 + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + self.createCellViewAutoLayout() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + self.createCellViewAutoLayout() + } + + func fillView(repositoryName: String, repositoryDescription: String, repositoryOwnerImageLink: URL?, repositoryOwnerName: String, repositoryNumberOfStars: String) { + + self.repositoryNameLabel.text = repositoryName + self.repositoryDescriptionLabel.text = repositoryDescription + self.repositoryOwnerNameLabel.text = repositoryOwnerName + self.repositoryNumberOfStarsLabel.text = repositoryNumberOfStars + + if let _ = repositoryOwnerImageLink{ + self.repositoryOwnerImageView.kf.setImage(with: repositoryOwnerImageLink!) + } + + } + + override func prepareForReuse() { + super.prepareForReuse() + + self.repositoryOwnerImageView.image = #imageLiteral(resourceName: "empty-avatar-icon") + + } + +} + +extension MostStarredGithubReposTableViewCell{ + + private func createCellViewAutoLayout(){ + + self.contentView.addSubview(self.repositoryNameLabel) + self.contentView.addSubview(self.repositoryDescriptionLabel) + self.contentView.addSubview(self.repositoryOwnerImageView) + self.contentView.addSubview(self.repositoryOwnerNameLabel) + self.contentView.addSubview(self.repositoryStarImageView) + self.contentView.addSubview(self.repositoryNumberOfStarsLabel) + + NSLayoutConstraint.activate([ + + self.repositoryNameLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: self.topBottomSpacing), + self.repositoryNameLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: self.leadingTrailingSpacing), + self.repositoryNameLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -self.leadingTrailingSpacing), + + self.repositoryDescriptionLabel.topAnchor.constraint(equalTo: self.repositoryNameLabel.bottomAnchor, constant: 10), + self.repositoryDescriptionLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: self.leadingTrailingSpacing), + self.repositoryDescriptionLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -self.leadingTrailingSpacing), + + self.repositoryOwnerImageView.topAnchor.constraint(equalTo: self.repositoryDescriptionLabel.bottomAnchor, constant: 10), + self.repositoryOwnerImageView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -self.topBottomSpacing), + self.repositoryOwnerImageView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: self.leadingTrailingSpacing), + self.repositoryOwnerImageView.widthAnchor.constraint(equalToConstant: 20.0), + self.repositoryOwnerImageView.heightAnchor.constraint(equalTo: self.repositoryOwnerImageView.widthAnchor), + + self.repositoryOwnerNameLabel.leadingAnchor.constraint(equalTo: self.repositoryOwnerImageView.trailingAnchor, constant: 6), + self.repositoryOwnerNameLabel.centerYAnchor.constraint(equalTo: self.repositoryOwnerImageView.centerYAnchor), + + self.repositoryStarImageView.leadingAnchor.constraint(equalTo: self.repositoryOwnerNameLabel.trailingAnchor, constant: 15), + self.repositoryStarImageView.centerYAnchor.constraint(equalTo: self.repositoryOwnerImageView.centerYAnchor), + self.repositoryStarImageView.widthAnchor.constraint(equalToConstant: 15.0), + self.repositoryStarImageView.heightAnchor.constraint(equalTo: self.repositoryStarImageView.widthAnchor), + + self.repositoryNumberOfStarsLabel.leadingAnchor.constraint(equalTo: self.repositoryStarImageView.trailingAnchor, constant: 6), + self.repositoryNumberOfStarsLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -self.leadingTrailingSpacing), + self.repositoryNumberOfStarsLabel.centerYAnchor.constraint(equalTo: self.repositoryOwnerImageView.centerYAnchor), + + ]) + + self.repositoryNameLabel.setContentHuggingPriority(.fittingSizeLevel, for: .vertical) + self.repositoryOwnerNameLabel.setContentHuggingPriority(.fittingSizeLevel, for: .horizontal) + + } + +} diff --git a/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposUI.swift b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposUI.swift new file mode 100644 index 0000000..8c78af0 --- /dev/null +++ b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposUI.swift @@ -0,0 +1,60 @@ +// +// MostStarredGithubReposUI.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import UIKit + +class MostStarredGithubReposUI: UIViewController{ + + let mostStarredReposTableView: UITableView = { + let tableView = UITableView() + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.backgroundColor = .white + tableView.separatorInset = .zero + tableView.tableFooterView = UIView() + return tableView + }() + + let loadingIndicatorView: UIActivityIndicatorView = { + let indicator = UIActivityIndicatorView() + indicator.translatesAutoresizingMaskIntoConstraints = false + indicator.color = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) + indicator.hidesWhenStopped = true + return indicator + }() + + let refreshControl: UIRefreshControl = { + let refreshControl = UIRefreshControl() + refreshControl.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) + refreshControl.attributedTitle = NSAttributedString(string: "pull_to_refresh".localized) + return refreshControl + }() + + override func viewDidLoad() { + super.viewDidLoad() + + self.view.addSubview(self.mostStarredReposTableView) + self.view.addSubview(self.loadingIndicatorView) + self.mostStarredReposTableView.refreshControl = self.refreshControl + + NSLayoutConstraint.activate([ + + self.mostStarredReposTableView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), + self.mostStarredReposTableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), + self.mostStarredReposTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), + self.mostStarredReposTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), + + self.loadingIndicatorView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + self.loadingIndicatorView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), + + ]) + + self.navigationController?.navigationBar.topItem?.title = "mostStarredGithubRepos.trending_repos".localized + + } + +} diff --git a/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposView.swift b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposView.swift new file mode 100644 index 0000000..278c181 --- /dev/null +++ b/mobile-coding-challenge/Controllers/Most Starred Github Repos/MostStarredGithubReposView.swift @@ -0,0 +1,126 @@ +// +// MostStarredGithubReposView.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import UIKit + +class MostStarredGithubReposView: MostStarredGithubReposUI, PresenterToMostStarredGithubReposViewProtocol{ + + var presenter: ViewToMostStarredGithubReposPresenterProtocol! + + override func viewDidLoad() { + super.viewDidLoad() + + self.mostStarredReposTableView.dataSource = self + self.mostStarredReposTableView.delegate = self + self.mostStarredReposTableView.register(MostStarredGithubReposTableViewCell.self, forCellReuseIdentifier: "\(MostStarredGithubReposTableViewCell.self)") + + self.refreshControl.addTarget(self, action: #selector(self.refreshMostStarredGithubReposData), for: .valueChanged) + + self.presenter.viewDidLoad() + + } + + func reloadData() { + self.mostStarredReposTableView.reloadData() + } + + func showLoader() { + self.mostStarredReposTableView.isUserInteractionEnabled = false + self.mostStarredReposTableView.separatorStyle = .none + self.loadingIndicatorView.startAnimating() + } + + func hideLoader() { + self.mostStarredReposTableView.isUserInteractionEnabled = true + self.mostStarredReposTableView.separatorStyle = .singleLine + self.loadingIndicatorView.stopAnimating() + self.refreshControl.endRefreshing() + self.mostStarredReposTableView.tableFooterView = nil + } + + func showResponseError(){ + self.loadingIndicatorView.stopAnimating() + self.refreshControl.endRefreshing() + self.mostStarredReposTableView.tableFooterView = nil + self.showSimpleAlert(title: "errors.something_went_wrong".localized, message: "mostStarredGithubRepos.errors.while_fetching_for_repos_description".localized) + } + + @objc + func refreshMostStarredGithubReposData(){ + self.presenter.interactor.getMostStarredGithubRepos(from: 1, isToUsePullRefresh: true) + } + +} + +extension MostStarredGithubReposView: UITableViewDataSource, UITableViewDelegate{ + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.presenter.mostStarredGithubRepos.count + } + + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + + if indexPath.row == self.presenter.mostStarredGithubRepos.count - 1 { + + let totalNumberOfMostStarredGithubRepos = self.presenter.totalNumberOfMostStarredGithubRepos + + if totalNumberOfMostStarredGithubRepos != -1 && totalNumberOfMostStarredGithubRepos ?? -1 > self.presenter.mostStarredGithubRepos.count{ + + let _view = UIView( + frame: .init( + x: 0, + y: 0, + width: tableView.contentSize.width, + height: 40 + ) + ) + + let indicatorView = UIActivityIndicatorView(frame: .init( + x: _view.frame.width / 2, + y: _view.frame.height / 2, + width: 20, + height: 20 + ) + ) + + indicatorView.startAnimating() + _view.addSubview(indicatorView) + + self.mostStarredReposTableView.tableFooterView = _view + + self.presenter.interactor.getMostStarredGithubRepos(from: self.presenter.activePage, isToUsePullRefresh: false) + + } + + } + + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + let mostStarredGithubReposCell = tableView.dequeueReusableCell(withIdentifier: "\(MostStarredGithubReposTableViewCell.self)", for: indexPath) as! MostStarredGithubReposTableViewCell + + let mostStarredGithubRepo = self.presenter.mostStarredGithubRepos[indexPath.row] + + mostStarredGithubReposCell.fillView( + repositoryName: mostStarredGithubRepo.getName(), + repositoryDescription: mostStarredGithubRepo.getDescription(), + repositoryOwnerImageLink: mostStarredGithubRepo.getOwnerImageURL(), + repositoryOwnerName: mostStarredGithubRepo.getOwnerName(), + repositoryNumberOfStars: mostStarredGithubRepo.getShortNumberOfStars() + ) + + return mostStarredGithubReposCell + + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + } + +} diff --git a/mobile-coding-challenge/Extensions/Date + Extension.swift b/mobile-coding-challenge/Extensions/Date + Extension.swift new file mode 100644 index 0000000..38cfae1 --- /dev/null +++ b/mobile-coding-challenge/Extensions/Date + Extension.swift @@ -0,0 +1,23 @@ +// +// File.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import Foundation + +extension Date{ + + func formattedDate(using dateFormat: String)->String{ + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = dateFormat + let formattedStringDate = dateFormatter.string(from: self) + + return formattedStringDate + + } + +} diff --git a/mobile-coding-challenge/Extensions/Int + Extension.swift b/mobile-coding-challenge/Extensions/Int + Extension.swift new file mode 100644 index 0000000..7073ccd --- /dev/null +++ b/mobile-coding-challenge/Extensions/Int + Extension.swift @@ -0,0 +1,34 @@ +// +// Int + Extension.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import Foundation + +extension Int{ + + var shortValue: String{ + + let num: Double = Double(abs(self)) + + if (num < 1000.0){ + return "\(self)" + } + + let sign : String = (self < 0) ? "-" : "" + let exp : Int = Int(log10(num) / 3.0 ) + let units : [String] = ["K","M"] + let roundedNum: Double = round(10 * num / pow(1000.0, Double(exp))) / 10 + + if exp <= units.count{ + return "\(sign)\(roundedNum)\(units[exp-1])" + } + + return "+\(Int(roundedNum))..." + + } + +} diff --git a/mobile-coding-challenge/Extensions/String + Extension.swift b/mobile-coding-challenge/Extensions/String + Extension.swift new file mode 100644 index 0000000..046b4f6 --- /dev/null +++ b/mobile-coding-challenge/Extensions/String + Extension.swift @@ -0,0 +1,17 @@ +// +// String + Extension.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import Foundation + +extension String{ + + var localized: String{ + return NSLocalizedString(self, comment: "") + } + +} diff --git a/mobile-coding-challenge/Extensions/UIViewController + Extension.swift b/mobile-coding-challenge/Extensions/UIViewController + Extension.swift new file mode 100644 index 0000000..c9c089c --- /dev/null +++ b/mobile-coding-challenge/Extensions/UIViewController + Extension.swift @@ -0,0 +1,25 @@ +// +// UIViewController + Extension.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import UIKit + +extension UIViewController{ + + func showSimpleAlert(title: String, message: String){ + + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + + let okAlertAction = UIAlertAction(title: "ok".localized, style: .default) + + alert.addAction(okAlertAction) + + self.present(alert, animated: true) + + } + +} diff --git a/mobile-coding-challenge/Helpers/RequestHandler.swift b/mobile-coding-challenge/Helpers/RequestHandler.swift new file mode 100644 index 0000000..8dc2a70 --- /dev/null +++ b/mobile-coding-challenge/Helpers/RequestHandler.swift @@ -0,0 +1,60 @@ +// +// RequestHandler.swift +// mobile-coding-challenge +// +// Created by Mobile on 9/19/20. +// Copyright © 2020 Zakariae. All rights reserved. +// + +import Foundation +import Moya + +enum RequestHandler{ + case getMostGithubRepos(created_at: String, page: Int) +} + +extension RequestHandler: TargetType{ + + var baseURL: URL { + return URL(string: "https://api.github.com/search/repositories")! + } + + var path: String { + switch self{ + case .getMostGithubRepos(_, _): + return "" + } + } + + var method: Moya.Method { + switch self{ + case .getMostGithubRepos: + return .get + } + } + + var sampleData: Data { return .init() } + + var task: Task { + switch self{ + case .getMostGithubRepos(let created_at, let page): + return .requestParameters( + parameters: [ + "q" : "created:>\(created_at)", + "page" : page, + "sort" : "stars", + "order": "desc" + ], + encoding: URLEncoding.queryString + ) + } + } + + var headers: [String : String]? { + switch self{ + case .getMostGithubRepos(_, _): + return ["Content-type": "application/json"] + } + } + +} diff --git a/mobile-coding-challenge/Info.plist b/mobile-coding-challenge/Info.plist new file mode 100644 index 0000000..fac5cdf --- /dev/null +++ b/mobile-coding-challenge/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarStyle + UIStatusBarStyleDarkContent + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/mobile-coding-challenge/Localizable Files/Localizable.strings b/mobile-coding-challenge/Localizable Files/Localizable.strings new file mode 100644 index 0000000..ac56c93 --- /dev/null +++ b/mobile-coding-challenge/Localizable Files/Localizable.strings @@ -0,0 +1,21 @@ +/* + Localizable.strings + mobile-coding-challenge + + Created by Mobile on 9/19/20. + Copyright © 2020 Zakariae. All rights reserved. +*/ + +//MARK: GENERAL + +"pull_to_refresh" = "Pull to refresh"; + +"errors.something_went_wrong" = "Something went wrong"; + +"ok" = "OK"; + +//MARK: MOST STARRED GITHUB REPOS VIEW + +"mostStarredGithubRepos.trending_repos" = "Trending Repos"; + +"mostStarredGithubRepos.errors.while_fetching_for_repos_description" = "Error while searching for the most starred github repos.\nPull to refresh or try again later"; diff --git a/mockup.png b/mockup.png deleted file mode 100644 index 9cfb608..0000000 Binary files a/mockup.png and /dev/null differ diff --git a/row-explained.png b/row-explained.png deleted file mode 100644 index ab4d2b8..0000000 Binary files a/row-explained.png and /dev/null differ