├── Go-Jek ├── GO-JEK-Assignment │ ├── .gitignore │ ├── .swiftlint.yml │ ├── GJAssignment.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── GJAssignment.xcscheme │ ├── GJAssignment.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── GJAssignment │ │ ├── AppDelegate │ │ │ ├── AppDelegate.swift │ │ │ └── AppDelegateExtension.swift │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ ├── Controllers │ │ │ ├── AddContactViewController │ │ │ │ ├── AddContactViewController.swift │ │ │ │ ├── AddContactViewController.xib │ │ │ │ └── AddContactViewControllerExtension.swift │ │ │ ├── ContactDetailsViewController │ │ │ │ ├── ContactDetailsViewController.swift │ │ │ │ ├── ContactDetailsViewController.xib │ │ │ │ └── ContactDetailsViewControllerExtension.swift │ │ │ └── ContactListViewController │ │ │ │ ├── ContactListViewController.swift │ │ │ │ ├── ContactListViewController.xib │ │ │ │ └── ContactListViewControllerExtension.swift │ │ ├── GJAssignment.xcdatamodeld │ │ │ ├── .xccurrentversion │ │ │ └── GJAssignment.xcdatamodel │ │ │ │ └── contents │ │ ├── Info.plist │ │ ├── Models │ │ │ ├── Contact+CoreDataClass.swift │ │ │ ├── Contact+CoreDataProperties.swift │ │ │ ├── ContactMetaData.swift │ │ │ └── ServerError.swift │ │ ├── Networking │ │ │ ├── AppServices │ │ │ │ └── ContactService.swift │ │ │ ├── Constants │ │ │ │ └── NetworkConstants.swift │ │ │ ├── Extensions │ │ │ │ ├── URLResponseExtension.swift │ │ │ │ ├── URLSessionDataTaskExtension.swift │ │ │ │ └── URLSessionExtension.swift │ │ │ ├── HTTPClient │ │ │ │ ├── GJError.swift │ │ │ │ ├── HTTPClient.swift │ │ │ │ └── HTTPMethod.swift │ │ │ └── Protocols │ │ │ │ ├── RequestProtocol.swift │ │ │ │ ├── URLSessionDataTaskProtocol.swift │ │ │ │ └── URLSessionProtocol.swift │ │ ├── Resources │ │ │ ├── Assets.xcassets │ │ │ │ ├── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ │ ├── CallButton.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── call_button@2x.png │ │ │ │ │ └── call_button@3x.png │ │ │ │ ├── CameraButton.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── camera_button@2x.png │ │ │ │ │ └── camera_button@3x.png │ │ │ │ ├── Contents.json │ │ │ │ ├── EmailButton.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── email_button@2x.png │ │ │ │ │ └── email_button@3x.png │ │ │ │ ├── FavouriteButton.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── favourite_button@2x.png │ │ │ │ │ └── favourite_button@3x.png │ │ │ │ ├── FavouriteButtonSelected.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── favourite_button_selected@2x.png │ │ │ │ │ └── favourite_button_selected@3x.png │ │ │ │ ├── HomeFavourite.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── home_favourite@2x.png │ │ │ │ │ └── home_favourite@3x.png │ │ │ │ ├── MessageButton.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── message_button@2x.png │ │ │ │ │ └── message_button@3x.png │ │ │ │ └── PlaceholderPhoto.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── placeholder_photo@2x.png │ │ │ │ │ └── placeholder_photo@3x.png │ │ │ ├── Colors.xcassets │ │ │ │ ├── Common │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── DescText.colorset │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Primary.colorset │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── PrimaryText.colorset │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── TableSectionBackground.colorset │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── TableSeparator.colorset │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── ViewBackground.colorset │ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ └── Localizable.strings │ │ ├── Utilities │ │ │ ├── Bindable │ │ │ │ ├── Bindable.swift │ │ │ │ └── BindableTextField.swift │ │ │ ├── Constants.swift │ │ │ ├── CoreDataManager │ │ │ │ └── CoreDataManager.swift │ │ │ ├── CustomControls │ │ │ │ ├── CircularImageView.swift │ │ │ │ ├── GradientView.swift │ │ │ │ └── ImagePicker.swift │ │ │ ├── Extensions │ │ │ │ ├── NSObjectExtension.swift │ │ │ │ ├── UIAlertControllerExtension.swift │ │ │ │ ├── UIColorExtension.swift │ │ │ │ ├── UIImageExtension.swift │ │ │ │ ├── UIImageViewExtension.swift │ │ │ │ └── UIViewExtension.swift │ │ │ └── Logger │ │ │ │ └── Log.swift │ │ ├── ViewModels │ │ │ ├── AddContactViewModel │ │ │ │ └── AddContactViewModel.swift │ │ │ ├── ContactDetailsViewModel │ │ │ │ └── ContactDetailsViewModel.swift │ │ │ └── ContactListViewModel │ │ │ │ ├── ContactListViewModel.swift │ │ │ │ └── ContactViewModel.swift │ │ └── Views │ │ │ ├── ContactDetailsTableViewCell │ │ │ ├── ContactDetailsTableViewCell.swift │ │ │ └── ContactDetailsTableViewCell.xib │ │ │ ├── ContactEditTableViewCell │ │ │ ├── ContactEditTableViewCell.swift │ │ │ └── ContactEditTableViewCell.xib │ │ │ ├── ContactEditTableViewHeader │ │ │ ├── ContactEditTableViewHeader.swift │ │ │ └── ContactEditTableViewHeader.xib │ │ │ └── ContactTableViewCell │ │ │ ├── ContactTableViewCell.swift │ │ │ └── ContactTableViewCell.xib │ ├── GJAssignmentTests │ │ ├── AddContactTests │ │ │ └── AddContactTests.swift │ │ ├── ContactDetailsTests │ │ │ ├── ContactDetailsErrorTests.swift │ │ │ ├── ContactDetailsTests.swift │ │ │ └── ContactDetailsViewModelTests.swift │ │ ├── ContactListTests │ │ │ ├── ContactListErrorTests.swift │ │ │ ├── ContactListTests.swift │ │ │ └── ContactViewModelTests.swift │ │ ├── Info.plist │ │ ├── MockData │ │ │ ├── MockContact.swift │ │ │ ├── MockRequest.swift │ │ │ └── StubJSON │ │ │ │ ├── contact.json │ │ │ │ ├── contacts.json │ │ │ │ ├── fav_contact.json │ │ │ │ └── not_found.json │ │ ├── MockNetworking │ │ │ ├── BadMockSession.swift │ │ │ ├── MockDataTask.swift │ │ │ └── MockSession.swift │ │ └── UpdateContactTests │ │ │ └── UpdateContactTests.swift │ ├── GJAssignmentUITests │ │ ├── CustomControlTests │ │ │ └── ImagePickerTests.swift │ │ ├── Info.plist │ │ ├── NavigationTests │ │ │ ├── AddContactNavigationTests.swift │ │ │ ├── ContactDetailsNavigationTests.swift │ │ │ └── ContactListNavigationTests.swift │ │ └── ViewControllerTests │ │ │ ├── AddContactControllerTests.swift │ │ │ └── ContactDetailsControllerTests.swift │ ├── Gemfile │ ├── Gemfile.lock │ ├── Podfile │ ├── Podfile.lock │ ├── Pods │ │ ├── MBProgressHUD │ │ │ ├── LICENSE │ │ │ ├── MBProgressHUD.h │ │ │ ├── MBProgressHUD.m │ │ │ └── README.mdown │ │ ├── Manifest.lock │ │ ├── Pods.xcodeproj │ │ │ └── project.pbxproj │ │ └── Target Support Files │ │ │ ├── MBProgressHUD │ │ │ ├── MBProgressHUD-Info.plist │ │ │ ├── MBProgressHUD-dummy.m │ │ │ ├── MBProgressHUD-prefix.pch │ │ │ ├── MBProgressHUD-umbrella.h │ │ │ ├── MBProgressHUD.modulemap │ │ │ └── MBProgressHUD.xcconfig │ │ │ ├── Pods-GJAssignment │ │ │ ├── Pods-GJAssignment-Info.plist │ │ │ ├── Pods-GJAssignment-acknowledgements.markdown │ │ │ ├── Pods-GJAssignment-acknowledgements.plist │ │ │ ├── Pods-GJAssignment-dummy.m │ │ │ ├── Pods-GJAssignment-frameworks-Debug-input-files.xcfilelist │ │ │ ├── Pods-GJAssignment-frameworks-Debug-output-files.xcfilelist │ │ │ ├── Pods-GJAssignment-frameworks-Release-input-files.xcfilelist │ │ │ ├── Pods-GJAssignment-frameworks-Release-output-files.xcfilelist │ │ │ ├── Pods-GJAssignment-frameworks.sh │ │ │ ├── Pods-GJAssignment-umbrella.h │ │ │ ├── Pods-GJAssignment.debug.xcconfig │ │ │ ├── Pods-GJAssignment.modulemap │ │ │ └── Pods-GJAssignment.release.xcconfig │ │ │ ├── Pods-GJAssignmentTests │ │ │ ├── Pods-GJAssignmentTests-Info.plist │ │ │ ├── Pods-GJAssignmentTests-acknowledgements.markdown │ │ │ ├── Pods-GJAssignmentTests-acknowledgements.plist │ │ │ ├── Pods-GJAssignmentTests-dummy.m │ │ │ ├── Pods-GJAssignmentTests-umbrella.h │ │ │ ├── Pods-GJAssignmentTests.debug.xcconfig │ │ │ ├── Pods-GJAssignmentTests.modulemap │ │ │ └── Pods-GJAssignmentTests.release.xcconfig │ │ │ └── Pods-GJAssignmentUITests │ │ │ ├── Pods-GJAssignmentUITests-Info.plist │ │ │ ├── Pods-GJAssignmentUITests-acknowledgements.markdown │ │ │ ├── Pods-GJAssignmentUITests-acknowledgements.plist │ │ │ ├── Pods-GJAssignmentUITests-dummy.m │ │ │ ├── Pods-GJAssignmentUITests-umbrella.h │ │ │ ├── Pods-GJAssignmentUITests.debug.xcconfig │ │ │ ├── Pods-GJAssignmentUITests.modulemap │ │ │ └── Pods-GJAssignmentUITests.release.xcconfig │ ├── README.md │ ├── docs │ │ ├── FastlaneTestResult.png │ │ ├── MemoryLeaks.png │ │ ├── ProblemStatement.pdf │ │ ├── Screenshot.png │ │ └── XcodeTestResult.png │ └── fastlane │ │ ├── Appfile │ │ ├── Fastfile │ │ └── README.md └── Go-Jek-Parking-Lot-Assignment-Python │ ├── .gitignore │ ├── Constants.py │ ├── Dockerfile │ ├── ParkingLot.py │ ├── README.md │ ├── Tests.py │ ├── Utilities.py │ ├── docs │ └── CodeCoverage.png │ └── input.txt ├── LICENSE ├── README-SEO.md └── README.md /Go-Jek/GO-JEK-Assignment/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | 4 | opt_in_rules: 5 | - empty_count 6 | - empty_string 7 | 8 | excluded: 9 | - Carthage 10 | - Pods 11 | - SwiftLint/Common/3rdPartyLib 12 | 13 | line_length: 14 | warning: 150 15 | error: 200 16 | ignores_function_declarations: true 17 | ignores_comments: true 18 | ignores_urls: true 19 | 20 | function_body_length: 21 | warning: 300 22 | error: 500 23 | 24 | function_parameter_count: 25 | warning: 6 26 | error: 8 27 | 28 | type_body_length: 29 | warning: 300 30 | error: 500 31 | 32 | file_length: 33 | warning: 1000 34 | error: 1500 35 | ignore_comment_only_lines: true 36 | 37 | cyclomatic_complexity: 38 | warning: 15 39 | error: 25 40 | 41 | identifier_name: 42 | excluded: 43 | - id 44 | 45 | reporter: "xcode" 46 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/xcshareddata/xcschemes/GJAssignment.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 39 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 54 | 60 | 61 | 62 | 63 | 64 | 70 | 71 | 72 | 73 | 74 | 75 | 85 | 87 | 93 | 94 | 95 | 96 | 97 | 98 | 104 | 106 | 112 | 113 | 114 | 115 | 117 | 118 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 14/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | 19 | setupNavigationBarAppearance() 20 | 21 | window = UIWindow(frame: UIScreen.main.bounds) 22 | window?.rootViewController = getRootViewController() 23 | window?.makeKeyAndVisible() 24 | 25 | return true 26 | } 27 | 28 | func applicationWillTerminate(_ application: UIApplication) { 29 | CoreDataManager.shared.saveContext() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/AppDelegate/AppDelegateExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegateExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension AppDelegate { 13 | func setupNavigationBarAppearance() { 14 | UINavigationBar.appearance().tintColor = UIColor.Common.tint 15 | } 16 | 17 | func getRootViewController() -> UIViewController { 18 | let contactViewController = ContactListViewController.get() 19 | let rootNavigationController = UINavigationController(rootViewController: contactViewController) 20 | return rootNavigationController 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddContactViewController.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol AddContactViewControllerDelegate: class { 12 | func contactSyncSuccessfully(contact: Contact) 13 | } 14 | 15 | class AddContactViewController: UIViewController { 16 | 17 | // MARK: - Outlet 18 | @IBOutlet weak var contactImageView: CircularImageView! 19 | @IBOutlet weak var selectImageButton: UIButton! 20 | @IBOutlet weak var contactTableView: UITableView! 21 | 22 | // MARK: - Weak Properties and Delgate 23 | weak var tableViewHeader: ContactEditTableViewHeader! 24 | weak var delegate: AddContactViewControllerDelegate? 25 | 26 | // MARK: - Internal Properties 27 | var imagePicker: ImagePicker! 28 | 29 | // MARK: - Properties 30 | var viewModel: AddContactViewModel! 31 | 32 | // MARK: - Class Functions 33 | class func present(contact: Contact?, delegate: AddContactViewControllerDelegate? = nil) { 34 | let addContactViewController = AddContactViewController(nibName: AddContactViewController.name, bundle: nil) 35 | addContactViewController.delegate = delegate 36 | addContactViewController.viewModel = AddContactViewModel(contact: contact) 37 | let navigationController = UINavigationController(rootViewController: addContactViewController) 38 | UIApplication.shared.keyWindow?.rootViewController?.present( 39 | navigationController, animated: true, completion: nil) 40 | } 41 | 42 | // MARK: - View Lifecycle 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | 46 | setupTableView() 47 | setupImagePicker() 48 | setupBindingAndGetContact() 49 | setupNavigationBarButtonItems() 50 | } 51 | 52 | override func viewDidLayoutSubviews() { 53 | super.viewDidLayoutSubviews() 54 | let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 150) 55 | contactTableView.tableHeaderView?.frame = frame 56 | } 57 | 58 | // MARK: - Helper Functions 59 | private func setupNavigationBarButtonItems() { 60 | navigationController?.navigationBar.shadowImage = UIImage() 61 | 62 | //set cancel and done bar button item 63 | let cancelBarButtonItem = UIBarButtonItem(title: viewModel?.cancelBarButtonTitle, 64 | style: .plain, 65 | target: self, 66 | action: #selector(cancelBarButtonItemAction)) 67 | navigationItem.leftBarButtonItem = cancelBarButtonItem 68 | 69 | let doneBarButtonItem = UIBarButtonItem(title: viewModel?.doneBarButtonTitle, 70 | style: .done, 71 | target: self, 72 | action: #selector(doneBarButtonItemAction)) 73 | navigationItem.rightBarButtonItem = doneBarButtonItem 74 | } 75 | 76 | private func setupTableView() { 77 | contactTableView.delegate = self 78 | contactTableView.dataSource = self 79 | contactTableView.tableFooterView = UIView(frame: CGRect.zero) 80 | contactTableView.register(ContactEditTableViewCell.nib, forCellReuseIdentifier: ContactEditTableViewCell.identifier) 81 | contactTableView.accessibilityIdentifier = "addContactTableView" 82 | 83 | tableViewHeader = ContactEditTableViewHeader.get() 84 | tableViewHeader.delegate = self 85 | contactTableView.tableHeaderView = tableViewHeader 86 | } 87 | 88 | private func setupImagePicker() { 89 | imagePicker = ImagePicker(from: self) 90 | imagePicker.delegate = self 91 | } 92 | 93 | private func setupBindingAndGetContact() { 94 | //Binding 95 | viewModel.isBusy.bind { [unowned self] isBusy in 96 | self.navigationController?.view.showLoader(show: isBusy) 97 | } 98 | 99 | viewModel.isContactSync.bind { [unowned self] (isSync) in 100 | if isSync { 101 | self.delegate?.contactSyncSuccessfully(contact: self.viewModel.contact.value) 102 | self.dismiss(animated: true, completion: nil) 103 | } 104 | } 105 | 106 | viewModel.contact.bind(listener: {[unowned self] (_) in 107 | self.contactTableView.reloadData() 108 | }) 109 | 110 | viewModel.error.bind { [unowned self] (error) in 111 | if let error = error { 112 | UIAlertController.show(error.localizedDescription, from: self) 113 | } 114 | } 115 | } 116 | 117 | @objc private func cancelBarButtonItemAction() { 118 | view.endEditing(true) 119 | dismiss(animated: true, completion: nil) 120 | } 121 | 122 | @objc private func doneBarButtonItemAction() { 123 | view.endEditing(true) 124 | viewModel.syncContact() 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddContactViewControllerExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import UIKit 12 | 13 | extension AddContactViewController: UITableViewDataSource { 14 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 15 | return viewModel.contactMetadata?.count ?? 0 16 | } 17 | 18 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 19 | guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactEditTableViewCell.identifier, 20 | for: indexPath) as? ContactEditTableViewCell else { 21 | fatalError("Unable to dequeue ContactDetailsTableViewCell cell.") 22 | } 23 | cell.delegate = self 24 | cell.accessibilityIdentifier = String(format: "editTableViewCell_%ld", indexPath.row) 25 | cell.config(metaData: viewModel!.contactMetadata[indexPath.row]) 26 | return cell 27 | } 28 | } 29 | 30 | extension AddContactViewController: UITableViewDelegate { 31 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 32 | return 56 33 | } 34 | } 35 | 36 | extension AddContactViewController: ContactEditTableViewHeaderDelegate { 37 | func takeImageButtonAction(_ sender: UIButton) { 38 | imagePicker.showImagePickerSources(sender: sender) 39 | } 40 | } 41 | 42 | extension AddContactViewController: ImagePickerDelegate { 43 | func didFinishPickingImage(_ image: UIImage?) { 44 | tableViewHeader.imageView.image = image 45 | } 46 | } 47 | 48 | extension AddContactViewController: ContactEditTableViewCellDelegate { 49 | func textChanged(contactMetaData: ContactMetadata, text: String) { 50 | contactMetaData.info = text 51 | switch contactMetaData.type { 52 | case .firstName: 53 | viewModel.contact.value.firstName = text 54 | case .lastName: 55 | viewModel.contact.value.lastName = text 56 | case .email: 57 | viewModel.contact.value.email = text 58 | case .mobile: 59 | viewModel.contact.value.phoneNumber = text 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsViewController.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ContactDetailsViewController: UIViewController { 12 | 13 | // MARK: - Outlet 14 | @IBOutlet weak var userImageView: UIImageView! 15 | @IBOutlet weak var userName: UILabel! 16 | @IBOutlet weak var topContainerView: UIView! 17 | @IBOutlet weak var detailsTableView: UITableView! 18 | @IBOutlet weak var favouriteImageView: UIImageView! 19 | 20 | // MARK: - Private Properties 21 | private var navigationBarShadowImage: UIImage? 22 | 23 | // MARK: - Internal Properties 24 | var viewModel: ContactDetailsViewModel! 25 | 26 | // MARK: - Class Functions 27 | class func get(contact: Contact) -> ContactDetailsViewController { 28 | let detailsViewController = ContactDetailsViewController(nibName: ContactDetailsViewController.name, bundle: nil) 29 | detailsViewController.viewModel = ContactDetailsViewModel(contact: contact) 30 | return detailsViewController 31 | } 32 | 33 | // MARK: - View Lifecycle 34 | override func viewDidLoad() { 35 | super.viewDidLoad() 36 | 37 | setupTableView() 38 | setupBindingAndGetContact() 39 | setupNavigationBarButtonItems() 40 | } 41 | 42 | override func viewWillAppear(_ animated: Bool) { 43 | super.viewWillAppear(animated) 44 | navigationController?.navigationBar.shadowImage = UIImage() 45 | } 46 | 47 | override func viewWillDisappear(_ animated: Bool) { 48 | super.viewWillDisappear(animated) 49 | navigationController?.navigationBar.shadowImage = navigationBarShadowImage 50 | } 51 | 52 | // MARK: - Helper Functions 53 | private func setupNavigationBarButtonItems() { 54 | //Store original shadow image 55 | navigationBarShadowImage = navigationController?.navigationBar.shadowImage 56 | 57 | //set edit contact bar button item 58 | let editBarButtonItem = UIBarButtonItem(title: viewModel?.editBarButtonTitle, 59 | style: .plain, 60 | target: self, 61 | action: #selector(editBarButtonItemAction)) 62 | navigationItem.rightBarButtonItem = editBarButtonItem 63 | } 64 | 65 | private func setupTableView() { 66 | detailsTableView.delegate = self 67 | detailsTableView.dataSource = self 68 | detailsTableView.tableFooterView = UIView(frame: CGRect.zero) 69 | detailsTableView.register(ContactDetailsTableViewCell.nib, forCellReuseIdentifier: ContactDetailsTableViewCell.identifier) 70 | detailsTableView.accessibilityIdentifier = "detailsTableView" 71 | } 72 | 73 | private func setupBindingAndGetContact() { 74 | //Binding 75 | viewModel.isBusy.bind { [unowned self] isBusy in 76 | self.navigationController?.view.showLoader(show: isBusy) 77 | } 78 | 79 | viewModel.contact.bind(listener: {[unowned self] (_) in 80 | let isFavorite = self.viewModel?.isFavorite ?? false 81 | let image = isFavorite ? UIImage.Action.favoriteSelected : UIImage.Action.favorite 82 | self.favouriteImageView.image = image 83 | 84 | self.userImageView.image = UIImage.Contact.placeHolder 85 | self.userName.text = self.viewModel?.name 86 | self.detailsTableView.reloadData() 87 | }) 88 | 89 | viewModel.error.bind { [unowned self] (error) in 90 | if let error = error { 91 | UIAlertController.show(error.localizedDescription, from: self) 92 | } 93 | } 94 | 95 | viewModel.getContactDetails() 96 | } 97 | 98 | // MARK: - Actions 99 | @objc private func editBarButtonItemAction() { 100 | AddContactViewController.present(contact: viewModel.contact.value, delegate: self) 101 | } 102 | 103 | @IBAction func messageTapGestureAction(_ sender: UITapGestureRecognizer) { 104 | guard let messageURL = viewModel.messageURL else { 105 | UIAlertController.show("Contact number not valid.", from: self) 106 | return 107 | } 108 | UIApplication.shared.open(messageURL, options: [:], completionHandler: nil) 109 | } 110 | 111 | @IBAction func callTapGestureAction(_ sender: UITapGestureRecognizer) { 112 | guard let telURL = viewModel.telURL else { 113 | UIAlertController.show("Contact number not valid.", from: self) 114 | return 115 | } 116 | UIApplication.shared.open(telURL, options: [:], completionHandler: nil) 117 | } 118 | 119 | @IBAction func emailTapGestureAction(_ sender: UITapGestureRecognizer) { 120 | guard let mailURL = viewModel.mailURL else { 121 | UIAlertController.show("Contact email not valid.", from: self) 122 | return 123 | } 124 | UIApplication.shared.open(mailURL, options: [:], completionHandler: nil) 125 | } 126 | 127 | @IBAction func favouriteTapGestureAction(_ sender: UITapGestureRecognizer) { 128 | if sender.state == .recognized { 129 | viewModel.updateFavourite() 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsViewControllerExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension ContactDetailsViewController: UITableViewDataSource { 13 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 14 | return viewModel.contactMetadata.count 15 | } 16 | 17 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 18 | guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactDetailsTableViewCell.identifier, 19 | for: indexPath) as? ContactDetailsTableViewCell else { 20 | fatalError("Unable to dequeue ContactDetailsTableViewCell cell.") 21 | } 22 | cell.config(metaData: viewModel.contactMetadata[indexPath.row]) 23 | cell.accessibilityIdentifier = String(format: "detailsTableViewCell_%ld", indexPath.row) 24 | return cell 25 | } 26 | } 27 | 28 | extension ContactDetailsViewController: UITableViewDelegate { 29 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 30 | return 56 31 | } 32 | } 33 | 34 | extension ContactDetailsViewController: AddContactViewControllerDelegate { 35 | func contactSyncSuccessfully(contact: Contact) { 36 | viewModel.contact.value = contact 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactViewController.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 16/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | class ContactListViewController: UIViewController { 13 | 14 | // MARK: - Outlet 15 | @IBOutlet weak var contactTableView: UITableView! 16 | 17 | // MARK: - Private Properties 18 | private let viewModel = ContactListViewModel() 19 | var fetchedResultController: NSFetchedResultsController? 20 | 21 | // MARK: - Class Functions 22 | class func get() -> ContactListViewController { 23 | let contactViewController = ContactListViewController(nibName: ContactListViewController.name, bundle: nil) 24 | return contactViewController 25 | } 26 | 27 | // MARK: - View Lifecycle 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | 31 | title = viewModel.title 32 | 33 | contactTableView.delegate = self 34 | contactTableView.dataSource = self 35 | contactTableView.tableFooterView = UIView(frame: CGRect.zero) 36 | contactTableView.register(ContactTableViewCell.nib, forCellReuseIdentifier: ContactTableViewCell.identifier) 37 | contactTableView.accessibilityIdentifier = "contactListTableView" 38 | 39 | setupNavigationBarButtonItems() 40 | setupFetchedResultController() 41 | setupBindingAndGetContacts() 42 | } 43 | 44 | // MARK: - Helper Functions 45 | private func setupNavigationBarButtonItems() { 46 | let groupsBarButtonItem = UIBarButtonItem(title: viewModel.groupBarButtonTitle, 47 | style: .plain, 48 | target: self, 49 | action: nil) 50 | navigationItem.leftBarButtonItem = groupsBarButtonItem 51 | 52 | let addContactBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, 53 | target: self, 54 | action: #selector(addContactBarButtonItemAction)) 55 | navigationItem.rightBarButtonItem = addContactBarButtonItem 56 | } 57 | 58 | private func setupFetchedResultController() { 59 | let contactsFetchRequest: NSFetchRequest = Contact.fetchRequest() 60 | let sortDescriptor = NSSortDescriptor(key: #keyPath(Contact.firstName), 61 | ascending: true, 62 | selector: #selector(NSString.caseInsensitiveCompare(_:))) 63 | contactsFetchRequest.sortDescriptors = [sortDescriptor] 64 | let managedObjectContext = CoreDataManager.shared.managedObjectContext 65 | fetchedResultController = .init(fetchRequest: contactsFetchRequest, 66 | managedObjectContext: managedObjectContext, 67 | sectionNameKeyPath: #keyPath(Contact.sectionTitle), 68 | cacheName: nil) 69 | fetchedResultController?.delegate = self 70 | } 71 | 72 | private func performFetchRequest() { 73 | do { 74 | try fetchedResultController?.performFetch() 75 | contactTableView.reloadData() 76 | } catch { 77 | Log.error("Unable to perform fetch operation from DB.", error: error) 78 | } 79 | } 80 | 81 | private func setupBindingAndGetContacts() { 82 | //Binding 83 | viewModel.isBusy.bind { [unowned self] isBusy in 84 | self.view.showLoader(show: isBusy) 85 | } 86 | 87 | viewModel.contacts.bind { [unowned self] (contacts) in 88 | if contacts != nil { 89 | self.performFetchRequest() 90 | } 91 | } 92 | 93 | viewModel.error.bind { [unowned self] (error) in 94 | if let error = error { 95 | self.performFetchRequest() 96 | UIAlertController.show(error.localizedDescription, from: self) 97 | } 98 | } 99 | 100 | //Get Contacts 101 | viewModel.getContacts() 102 | } 103 | 104 | @objc private func addContactBarButtonItemAction() { 105 | AddContactViewController.present(contact: nil) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactListViewControllerExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreData 12 | 13 | // MARK: - NSFetchResultController Delegate 14 | extension ContactListViewController: NSFetchedResultsControllerDelegate { 15 | func controllerWillChangeContent(_ controller: NSFetchedResultsController) { 16 | contactTableView.beginUpdates() 17 | } 18 | 19 | func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { 20 | switch type { 21 | case .insert: 22 | contactTableView.insertRows(at: [newIndexPath!], with: .none) 23 | case .delete: 24 | contactTableView.deleteRows(at: [indexPath!], with: .none) 25 | case .update: 26 | contactTableView.reloadRows(at: [indexPath!], with: .none) 27 | case .move: 28 | contactTableView.moveRow(at: indexPath!, to: newIndexPath!) 29 | default: 30 | break 31 | } 32 | } 33 | 34 | func controller(_ controller: NSFetchedResultsController, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) { 35 | switch type { 36 | case .insert: 37 | contactTableView.insertSections(IndexSet(integer: sectionIndex), with: .none) 38 | case .delete: 39 | contactTableView.deleteSections(IndexSet(integer: sectionIndex), with: .none) 40 | case .update: 41 | contactTableView.reloadSections(IndexSet(integer: sectionIndex), with: .none) 42 | default: 43 | break 44 | } 45 | } 46 | 47 | func controllerDidChangeContent(_ controller: NSFetchedResultsController) { 48 | contactTableView.endUpdates() 49 | } 50 | 51 | } 52 | 53 | // MARK: - UITableView Data Source 54 | extension ContactListViewController: UITableViewDataSource { 55 | func numberOfSections(in tableView: UITableView) -> Int { 56 | return fetchedResultController?.sections?.count ?? 0 57 | } 58 | 59 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 60 | return fetchedResultController?.sections?[section].objects?.count ?? 0 61 | } 62 | 63 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 64 | guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactTableViewCell.identifier, 65 | for: indexPath) as? ContactTableViewCell else { 66 | fatalError("Unable to dequeue ContactTableViewCell.") 67 | } 68 | cell.config(contact: fetchedResultController!.object(at: indexPath)) 69 | cell.accessibilityIdentifier = String(format: "contactTableViewCell_%ld_%ld", indexPath.section, indexPath.row) 70 | return cell 71 | } 72 | 73 | func sectionIndexTitles(for tableView: UITableView) -> [String]? { 74 | return fetchedResultController?.sectionIndexTitles 75 | } 76 | 77 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 78 | return fetchedResultController?.sections?[section].indexTitle 79 | } 80 | } 81 | 82 | extension ContactListViewController: UITableViewDelegate { 83 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 84 | let contact = fetchedResultController!.object(at: indexPath) 85 | let contactDetailsViewController = ContactDetailsViewController.get(contact: contact) 86 | navigationController?.pushViewController(contactDetailsViewController, animated: true) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/GJAssignment.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | GJAssignment.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/GJAssignment.xcdatamodeld/GJAssignment.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPhotoLibraryUsageDescription 6 | App requires access to your phone’s photo lib. 7 | NSCameraUsageDescription 8 | App requires access to your phone’s camera. 9 | CFBundleDevelopmentRegion 10 | $(DEVELOPMENT_LANGUAGE) 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Models/Contact+CoreDataClass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Contact+CoreDataClass.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | // 9 | 10 | import Foundation 11 | import CoreData 12 | 13 | extension Contact { 14 | 15 | public var fullName: String { 16 | let seprator = (firstName != nil) ? " " : "" 17 | return (firstName ?? "") + seprator + (lastName ?? "") 18 | } 19 | 20 | @objc public var sectionTitle: String { 21 | let firstCharString = firstName?.first?.uppercased() ?? "" 22 | if firstCharString >= "A" && firstCharString <= "Z" { 23 | return firstCharString 24 | } 25 | return "#" 26 | } 27 | 28 | @nonobjc public class func fetchRequest() -> NSFetchRequest { 29 | return NSFetchRequest(entityName: Contact.name) 30 | } 31 | 32 | public class func getContact(id: Int) -> Contact? { 33 | let fetchRequest: NSFetchRequest = Contact.fetchRequest() 34 | fetchRequest.predicate = NSPredicate(format: "id=%ld", id) 35 | fetchRequest.fetchLimit = 1 36 | do { 37 | let contacts: [Contact] = try CoreDataManager.shared.managedObjectContext.fetch(fetchRequest) 38 | return contacts.first 39 | } catch { 40 | Log.error("Unable to fetch contact with id \(id).", error: error) 41 | } 42 | return nil 43 | } 44 | 45 | public class func deleteAllContacts() { 46 | let fetchRequest: NSFetchRequest = Contact.fetchRequest() 47 | let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) 48 | deleteRequest.resultType = .resultTypeObjectIDs 49 | 50 | // perform the delete 51 | do { 52 | let managedObjectContext = CoreDataManager.shared.managedObjectContext 53 | let result = try managedObjectContext.execute(deleteRequest) as? NSBatchDeleteResult 54 | let managedObjectIds = result?.result as? [NSManagedObjectID] ?? [] 55 | let changes: [AnyHashable: Any] = [NSDeletedObjectsKey: managedObjectIds] 56 | NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [managedObjectContext]) 57 | } catch let error as NSError { 58 | Log.error("Unable to delete existing contacts.", error: error) 59 | } 60 | } 61 | 62 | func getDetailsMetadata() -> [ContactMetadata] { 63 | let phoneMetadata = ContactMetadata(desc: NSLocalizedString("mobile", comment: ""), 64 | info: phoneNumber, type: .mobile, keyboardType: .phonePad) 65 | let emailMetadata = ContactMetadata(desc: NSLocalizedString("email", comment: ""), 66 | info: email, type: .email, keyboardType: .emailAddress) 67 | return [phoneMetadata, emailMetadata] 68 | } 69 | 70 | func getEditMetaData() -> [ContactMetadata] { 71 | let firstNameMetaData = ContactMetadata(desc: NSLocalizedString("First Name", comment: ""), 72 | info: firstName, type: .firstName) 73 | 74 | let lastNameMetaData = ContactMetadata(desc: NSLocalizedString("Last Name", comment: ""), 75 | info: lastName, type: .lastName) 76 | 77 | return [firstNameMetaData, lastNameMetaData] + getDetailsMetadata() 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Models/Contact+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Contact+CoreDataProperties.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | // 9 | 10 | import Foundation 11 | import CoreData 12 | 13 | @objc(Contact) 14 | public class Contact: NSManagedObject, Codable { 15 | @NSManaged public var id: Int64 16 | @NSManaged public var lastName: String? 17 | @NSManaged public var firstName: String? 18 | @NSManaged public var profilePic: String? 19 | @NSManaged public var favorite: Bool 20 | @NSManaged public var email: String? 21 | @NSManaged public var phoneNumber: String? 22 | 23 | enum CodingKeys: String, CodingKey { 24 | case id, favorite, email 25 | case firstName = "first_name" 26 | case lastName = "last_name" 27 | case profilePic = "profile_pic" 28 | case phoneNumber = "phone_number" 29 | } 30 | 31 | public required convenience init(from decoder: Decoder) throws { 32 | let container = try decoder.container(keyedBy: CodingKeys.self) 33 | let contactId = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0 34 | let firstName = try container.decodeIfPresent(String.self, forKey: .firstName) 35 | let lastName = try container.decodeIfPresent(String.self, forKey: .lastName) 36 | let profilePic = try container.decodeIfPresent(String.self, forKey: .profilePic) 37 | let favorite = try container.decodeIfPresent(Bool.self, forKey: .favorite) ?? false 38 | let phoneNumber = try container.decodeIfPresent(String.self, forKey: .phoneNumber) 39 | let email = try container.decodeIfPresent(String.self, forKey: .email) 40 | 41 | let managedObjectContext = CoreDataManager.shared.managedObjectContext 42 | guard let entity = NSEntityDescription.entity(forEntityName: Contact.name, in: managedObjectContext) else { 43 | fatalError("Failed to decode Contact") 44 | } 45 | self.init(entity: entity, insertInto: nil) 46 | self.id = Int64(contactId) 47 | self.firstName = firstName 48 | self.lastName = lastName 49 | self.profilePic = profilePic 50 | self.favorite = favorite 51 | self.phoneNumber = phoneNumber 52 | self.email = email 53 | 54 | if let entity = Contact.getContact(id: contactId) { 55 | entity.firstName = firstName 56 | entity.lastName = lastName 57 | entity.profilePic = profilePic 58 | entity.favorite = favorite 59 | entity.phoneNumber = phoneNumber 60 | entity.email = email 61 | } else { 62 | managedObjectContext.insert(self) 63 | } 64 | } 65 | 66 | public func encode(to encoder: Encoder) throws { 67 | var container = encoder.container(keyedBy: CodingKeys.self) 68 | try container.encode(firstName, forKey: .firstName) 69 | try container.encode(lastName, forKey: .lastName) 70 | //try container.encode(profilePic, forKey: .profilePic) 71 | try container.encode(favorite, forKey: .favorite) 72 | try container.encode(phoneNumber, forKey: .phoneNumber) 73 | try container.encode(email, forKey: .email) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Models/ContactMetaData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactMetaData.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | enum ContactMetadataType { 13 | case firstName, lastName, email, mobile 14 | } 15 | 16 | class ContactMetadata { 17 | var desc: String! 18 | var info: String! 19 | var type: ContactMetadataType 20 | var keyboardType: UIKeyboardType 21 | 22 | init(desc: String, info: String?, type: ContactMetadataType, keyboardType: UIKeyboardType = .default) { 23 | self.desc = desc 24 | self.info = info 25 | self.type = type 26 | self.keyboardType = keyboardType 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Models/ServerError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerError.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ServerError: Decodable { 12 | let status: String? 13 | let error: String? 14 | } 15 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/AppServices/ContactService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactService.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //Contact APIs 12 | enum ContactAPI { 13 | case getContacts 14 | case getContact(id: Int) 15 | case deleteContact(id: Int) 16 | case addContact(contact: Contact) 17 | case updateContact(id: Int, contact: Contact) 18 | } 19 | 20 | extension ContactAPI: RequestProtocol { 21 | //Set Base URL 22 | var baseURL: URL { 23 | guard let url = URL(string: Constants.Service.baseURL) else { 24 | fatalError("BaseURL could not be configured.") 25 | } 26 | return url 27 | } 28 | 29 | //Returns EndPoint for Contact APIs 30 | var path: String { 31 | switch self { 32 | case .getContacts, .addContact: 33 | return "contacts.json" 34 | case .getContact(let id), .deleteContact(let id), .updateContact(let id, _): 35 | return "contacts/\(String(describing: id)).json" 36 | } 37 | } 38 | 39 | //Returns HTTP Method for contact APIs 40 | var httpMethod: HTTPMethod { 41 | switch self { 42 | case .getContacts, .getContact: 43 | return .get 44 | case .addContact: 45 | return .post 46 | case .updateContact: 47 | return .put 48 | case .deleteContact: 49 | return .delete 50 | } 51 | } 52 | 53 | //Encode and Returns Encoded Data 54 | var httpBody: Data? { 55 | switch self { 56 | case .addContact(let contact), .updateContact(_, let contact): 57 | do { 58 | return try JSONEncoder().encode(contact) 59 | } catch { 60 | Log.error("Unable to encode Contact.", error: error) 61 | } 62 | default: 63 | return nil 64 | } 65 | return nil 66 | } 67 | 68 | //Return Contact APIs Specific Headers 69 | var headers: HTTPHeaders? { 70 | return nil 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Constants/NetworkConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Constants { 12 | struct Service { 13 | static let baseURL = "https://gojek-contacts-app.herokuapp.com/" 14 | static let timeout: TimeInterval = 60.0 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLResponseExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLResponseExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension URLResponse { 12 | var isSuccess: Bool { 13 | return httpStatusCode >= 200 && httpStatusCode < 300 14 | } 15 | 16 | var httpStatusCode: Int { 17 | guard let statusCode = (self as? HTTPURLResponse)?.statusCode else { 18 | return 0 19 | } 20 | return statusCode 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLSessionDataTaskExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionDataTaskExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension URLSessionDataTask: URLSessionDataTaskProtocol { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLSessionExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension URLSession: URLSessionProtocol { 12 | func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol { 13 | let task = dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTask 14 | return task as URLSessionDataTaskProtocol 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/GJError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GJError.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | typealias GJErrorHandler = (GJError?) -> Void 12 | 13 | struct GJError: Error { 14 | var localizedDescription: String 15 | init(_ localizedDescription: String) { 16 | self.localizedDescription = localizedDescription 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/HTTPClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkManager.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class HTTPClient { 12 | // MARK: Typealias 13 | typealias CompletionResult = (Result) -> Void 14 | 15 | // MARK: - Shared Instance 16 | static let shared = HTTPClient(session: URLSession.shared) 17 | 18 | // MARK: - Private Properties 19 | private let session: URLSessionProtocol 20 | private var task: URLSessionDataTaskProtocol? 21 | private var completionResult: CompletionResult? 22 | 23 | // MARK: - Initialiser 24 | init(session: URLSessionProtocol) { 25 | self.session = session 26 | } 27 | 28 | // MARK: - Data Task Helper 29 | func dataTask(_ request: RequestProtocol, completion: @escaping CompletionResult) { 30 | completionResult = completion 31 | var urlRequest = URLRequest(url: request.baseURL.appendingPathComponent(request.path), 32 | cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, 33 | timeoutInterval: Constants.Service.timeout) 34 | urlRequest.httpMethod = request.httpMethod.rawValue 35 | urlRequest.httpBody = request.httpBody 36 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 37 | 38 | task = session.dataTask(with: urlRequest) { (data, response, error) in 39 | //return error if there is any error in making request 40 | if let error = error { 41 | self.completionResult(.failure(GJError(error.localizedDescription))) 42 | return 43 | } 44 | 45 | //check response 46 | if let response = response, response.isSuccess { 47 | if let data = data { 48 | self.completionResult(.success(data)) 49 | } 50 | 51 | if response.httpStatusCode == 204 { 52 | self.completionResult(.success(nil)) 53 | } 54 | } else { 55 | let commonErrorMessage = NSLocalizedString("Somthing went wrong!", comment: "") 56 | guard let data = data else { 57 | Log.error(commonErrorMessage) 58 | self.completionResult(.failure(GJError(commonErrorMessage))) 59 | return 60 | } 61 | do { 62 | let serverError = try JSONDecoder().decode(ServerError.self, from: data) 63 | Log.error(serverError.error ?? commonErrorMessage) 64 | self.completionResult(.failure(GJError(serverError.error ?? commonErrorMessage))) 65 | } catch { 66 | Log.error(commonErrorMessage, error: error) 67 | self.completionResult(.failure(GJError(commonErrorMessage))) 68 | } 69 | } 70 | } 71 | 72 | //Resume task 73 | self.task?.resume() 74 | } 75 | 76 | func cancel() { 77 | self.task?.cancel() 78 | } 79 | 80 | // MARK: - Private Helper Function 81 | private func completionResult(_ result: Result) { 82 | DispatchQueue.main.async { 83 | self.completionResult?(result) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum HTTPMethod: String { 12 | case get = "GET" 13 | case post = "POST" 14 | case put = "PUT" 15 | case patch = "PATCH" 16 | case delete = "DELETE" 17 | } 18 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/RequestProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestProtocol.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias HTTPHeaders = [String: String] 12 | 13 | protocol RequestProtocol { 14 | var baseURL: URL { get } 15 | var path: String { get } 16 | var httpMethod: HTTPMethod { get } 17 | var httpBody: Data? { get } 18 | var headers: HTTPHeaders? { get } 19 | } 20 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/URLSessionDataTaskProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionTaskProtocol.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol URLSessionDataTaskProtocol { 12 | func resume() 13 | func cancel() 14 | } 15 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/URLSessionProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionProtocol.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol URLSessionProtocol { 12 | func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol 13 | } 14 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "call_button@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "call_button@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@2x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@3x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "camera_button@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "camera_button@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@2x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@3x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "email_button@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "email_button@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@2x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@3x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "favourite_button@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "favourite_button@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@2x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@3x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "favourite_button_selected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "favourite_button_selected@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@2x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@3x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_favourite@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_favourite@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@2x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@3x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "message_button@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "message_button@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@2x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@3x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "placeholder_photo@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "placeholder_photo@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@2x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@3x.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/DescText.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0x4A", 13 | "alpha" : "0.500", 14 | "blue" : "0x4A", 15 | "green" : "0x4A" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/Primary.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.314", 13 | "alpha" : "1.000", 14 | "blue" : "0.761", 15 | "green" : "0.890" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/PrimaryText.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0x4A", 13 | "alpha" : "1.000", 14 | "blue" : "0x4A", 15 | "green" : "0x4A" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/TableSectionBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xE8", 13 | "alpha" : "1.000", 14 | "blue" : "0xE8", 15 | "green" : "0xE8" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/TableSeparator.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xF0", 13 | "alpha" : "1.000", 14 | "blue" : "0xF0", 15 | "green" : "0xF0" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/ViewBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xF9", 13 | "alpha" : "1.000", 14 | "blue" : "0xF9", 15 | "green" : "0xF9" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | GJAssignment 4 | 5 | Created by Anonymous on 17/08/19. 6 | Copyright © 2019 Anonymous. All rights reserved. 7 | */ 8 | 9 | "Contact" = "Contact"; 10 | "Groups" = "Groups"; 11 | "Edit" = "Edit"; 12 | "Cancel" = "Cancel"; 13 | "Done" = "Done"; 14 | "message" = "message"; 15 | "call" = "call"; 16 | "email" = "email"; 17 | "favourite" = "favourite"; 18 | "First Name" = "First Name"; 19 | "Last Name" = "Last Name"; 20 | "mobile" = "mobile"; 21 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Bindable/Bindable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bindable.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Bindable { 12 | typealias Listener = (T) -> Void 13 | var listener: Listener? 14 | 15 | var value: T { 16 | didSet { 17 | DispatchQueue.main.async { [weak self] in 18 | guard let self = self else { 19 | return 20 | } 21 | self.listener?(self.value) 22 | } 23 | } 24 | } 25 | 26 | init(_ value: T) { 27 | self.value = value 28 | } 29 | 30 | func bind(listener: Listener?) { 31 | self.listener = listener 32 | listener?(value) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Bindable/BindableTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BindableTextField.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class BindableTextField: UITextField { 13 | typealias Listener = (String) -> Void 14 | var textChanged: Listener = { _ in } 15 | 16 | func bind(listener: @escaping Listener) { 17 | self.textChanged = listener 18 | self.addTarget(self, action: #selector(textFieldDidChanged(_:)), for: .editingChanged) 19 | } 20 | 21 | @objc func textFieldDidChanged(_ textField: UITextField) { 22 | self.textChanged(textField.text!) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Constants { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CoreDataManager/CoreDataManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataManager.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class CoreDataManager { 13 | static let shared = CoreDataManager() 14 | 15 | private init() { 16 | } 17 | 18 | // MARK: - Core Data stack 19 | lazy var persistentContainer: NSPersistentContainer = { 20 | /* 21 | The persistent container for the application. This implementation 22 | creates and returns a container, having loaded the store for the 23 | application to it. This property is optional since there are legitimate 24 | error conditions that could cause the creation of the store to fail. 25 | */ 26 | let container = NSPersistentContainer(name: "GJAssignment") 27 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in 28 | if let error = error as NSError? { 29 | Log.error("Unresolved error \(error), \(error.userInfo)") 30 | } else { 31 | Log.debug("Store Description \(storeDescription)") 32 | } 33 | }) 34 | return container 35 | }() 36 | 37 | var managedObjectContext: NSManagedObjectContext { 38 | return persistentContainer.viewContext 39 | } 40 | 41 | // MARK: - Core Data Saving support 42 | func saveContext () { 43 | let context = persistentContainer.viewContext 44 | if context.hasChanges { 45 | do { 46 | try context.save() 47 | } catch { 48 | // Replace this implementation with code to handle the error appropriately. 49 | // 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. 50 | let nserror = error as NSError 51 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)") 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/CircularImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircularImageView.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CircularImageView: UIImageView { 12 | override func layoutSubviews() { 13 | super.layoutSubviews() 14 | layer.cornerRadius = frame.height / 2 15 | layer.masksToBounds = true 16 | clipsToBounds = true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/GradientView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradientView.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class GradientView: UIView { 12 | 13 | private var gradientLayer: CAGradientLayer! 14 | 15 | private var topColor: UIColor = UIColor.Common.viewBackground 16 | private var bottomColor: UIColor = UIColor.Common.tint.withAlphaComponent(0.5) 17 | 18 | override class var layerClass: AnyClass { 19 | return CAGradientLayer.self 20 | } 21 | 22 | override func layoutSubviews() { 23 | self.gradientLayer = self.layer as? CAGradientLayer 24 | self.gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor] 25 | self.gradientLayer.startPoint = CGPoint(x: 0, y: 0) 26 | self.gradientLayer.endPoint = CGPoint(x: 0, y: 1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/ImagePicker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagePicker.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | protocol ImagePickerDelegate: class { 13 | func didFinishPickingImage(_ image: UIImage?) 14 | } 15 | 16 | class ImagePicker: NSObject { 17 | weak var viewController: UIViewController! 18 | let imagePickerController = UIImagePickerController() 19 | weak var delegate: ImagePickerDelegate? 20 | 21 | init(from viewController: UIViewController) { 22 | self.viewController = viewController 23 | } 24 | 25 | func showImagePickerSources(sender: UIView) { 26 | let alert = UIAlertController(title: "Choose Image", message: nil, preferredStyle: .actionSheet) 27 | if UIImagePickerController.isSourceTypeAvailable(.camera) { 28 | alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in 29 | self.openImagePickerController(source: .camera) 30 | })) 31 | } 32 | 33 | alert.addAction(UIAlertAction(title: "Gallery", style: .default, handler: { _ in 34 | self.openImagePickerController(source: .photoLibrary) 35 | })) 36 | 37 | alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil)) 38 | 39 | switch UIDevice.current.userInterfaceIdiom { 40 | case .pad: 41 | alert.popoverPresentationController?.sourceView = sender 42 | alert.popoverPresentationController?.sourceRect = sender.bounds 43 | alert.popoverPresentationController?.permittedArrowDirections = .up 44 | default: 45 | break 46 | } 47 | 48 | viewController.present(alert, animated: true, completion: nil) 49 | } 50 | 51 | func openImagePickerController(source: UIImagePickerController.SourceType) { 52 | imagePickerController.delegate = self 53 | imagePickerController.sourceType = source 54 | imagePickerController.allowsEditing = true 55 | viewController.present(imagePickerController, animated: true, completion: nil) 56 | } 57 | } 58 | 59 | extension ImagePicker: UIImagePickerControllerDelegate, UINavigationControllerDelegate { 60 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { 61 | if let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage { 62 | delegate?.didFinishPickingImage(image) 63 | } else { 64 | delegate?.didFinishPickingImage(nil) 65 | } 66 | imagePickerController.dismiss(animated: true, completion: nil) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/NSObjectExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObjectExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 16/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSObject { 12 | class var name: String { 13 | return String(describing: self) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIAlertControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertViewController.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIAlertController { 13 | static func show(_ message: String, from viewController: UIViewController) { 14 | let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) 15 | alert.addAction(.init(title: "OK", style: .cancel, handler: nil)) 16 | viewController.present(alert, animated: true, completion: nil) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIColorExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColorExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 16/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | struct Common { 13 | static var tint = #colorLiteral(red: 0.3140000105, green: 0.8899999857, blue: 0.7609999776, alpha: 1) 14 | static var text = #colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1) 15 | static var tableSeparator = #colorLiteral(red: 0.9411764706, green: 0.9411764706, blue: 0.9411764706, alpha: 1) 16 | static var tableSectionBackground = #colorLiteral(red: 0.9098039216, green: 0.9098039216, blue: 0.9098039216, alpha: 1) 17 | static var viewBackground = #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIImageExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIImage { 13 | struct Contact { 14 | static let placeHolder = #imageLiteral(resourceName: "PlaceholderPhoto") 15 | static let showFavorite = #imageLiteral(resourceName: "HomeFavourite") 16 | } 17 | 18 | struct Action { 19 | static let call = #imageLiteral(resourceName: "CallButton") 20 | static let mail = #imageLiteral(resourceName: "EmailButton") 21 | static let message = #imageLiteral(resourceName: "MessageButton") 22 | static let favorite = #imageLiteral(resourceName: "FavouriteButton") 23 | static let favoriteSelected = #imageLiteral(resourceName: "FavouriteButtonSelected") 24 | static let takePhoto = #imageLiteral(resourceName: "CameraButton") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIImageViewExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageViewExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIImageView { 13 | func makeCircle() { 14 | layer.cornerRadius = bounds.height / 2 15 | layer.masksToBounds = true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIViewExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewExtension.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import MBProgressHUD 12 | 13 | extension UIView { 14 | func showLoader(show: Bool) { 15 | if show { 16 | MBProgressHUD.showAdded(to: self, animated: true) 17 | } else { 18 | MBProgressHUD.hide(for: self, animated: true) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Logger/Log.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Log.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import os 11 | 12 | struct Log { 13 | private static let subsystem = Bundle.main.bundleIdentifier! 14 | private static let appLog = OSLog(subsystem: Log.subsystem, category: "Default") 15 | 16 | static func error(_ msg: String, error: Error? = nil, log: OSLog = appLog) { 17 | os_log("🔥 - %@ %@", log: log, type: .error, msg, error?.localizedDescription ?? "") 18 | } 19 | 20 | static func info(_ msg: String, log: OSLog = appLog) { 21 | os_log("🚀 - %@", log: log, type: .info, msg) 22 | } 23 | 24 | static func debug(_ msg: String, log: OSLog = appLog) { 25 | os_log("👀 - %@", log: log, type: .debug, msg) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/AddContactViewModel/AddContactViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddContactViewModel.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class AddContactViewModel { 13 | private var httpClient: HTTPClient! 14 | private var editMode: Bool! 15 | 16 | let doneBarButtonTitle = NSLocalizedString("Done", comment: "") 17 | let cancelBarButtonTitle = NSLocalizedString("Cancel", comment: "") 18 | let managedObjectContext = CoreDataManager.shared.managedObjectContext 19 | 20 | var contact: Bindable! 21 | var contactMetadata: [ContactMetadata]! 22 | var isBusy: Bindable = Bindable(false) 23 | var isContactSync: Bindable = Bindable(false) 24 | var error: Bindable = Bindable(nil) 25 | 26 | init(contact: Contact?, client: HTTPClient? = nil) { 27 | self.httpClient = client ?? HTTPClient.shared 28 | 29 | if let contact = contact { 30 | editMode = true 31 | self.contact = Bindable(contact) 32 | self.contactMetadata = self.contact.value.getEditMetaData() 33 | } else { 34 | editMode = false 35 | guard let entity = NSEntityDescription.entity(forEntityName: Contact.name, in: managedObjectContext) else { 36 | fatalError("Failed to decode Contact") 37 | } 38 | 39 | self.contact = Bindable(Contact(entity: entity, insertInto: nil)) 40 | self.contactMetadata = self.contact.value.getEditMetaData() 41 | } 42 | } 43 | 44 | func syncContact() { 45 | //Validate Contact 46 | if !validateContact() { return } 47 | 48 | var request: RequestProtocol = ContactAPI.addContact(contact: contact.value) 49 | if editMode { 50 | request = ContactAPI.updateContact(id: Int(contact.value.id), contact: contact.value) 51 | } 52 | 53 | isBusy.value = true 54 | httpClient.dataTask(request) { [weak self] (result) in 55 | guard let self = self else { 56 | return 57 | } 58 | 59 | self.isBusy.value = false 60 | switch result { 61 | case .success(let data): 62 | guard let data = data else { 63 | return 64 | } 65 | 66 | do { 67 | let contact = try JSONDecoder().decode(Contact.self, from: data) 68 | CoreDataManager.shared.saveContext() 69 | self.isContactSync.value = true 70 | self.contact.value = contact 71 | } catch { 72 | self.error.value = GJError(error.localizedDescription) 73 | Log.error("Unable to decode contact.", error: error) 74 | } 75 | case .failure(let error): 76 | self.error.value = GJError(error.localizedDescription) 77 | Log.error("Error in contact sync.", error: error) 78 | } 79 | } 80 | } 81 | 82 | private func validateContact() -> Bool { 83 | guard let firstName = contact.value.firstName else { 84 | error.value = GJError(NSLocalizedString("Please enter first name.", comment: "")) 85 | return false 86 | } 87 | 88 | if firstName.count < 2 { 89 | let message = NSLocalizedString("First name is too short (minimum is 2 characters)", comment: "") 90 | error.value = GJError(message) 91 | return false 92 | } 93 | 94 | guard let lastName = contact.value.lastName else { 95 | error.value = GJError(NSLocalizedString("Please enter last name.", comment: "")) 96 | return false 97 | } 98 | 99 | if lastName.count < 2 { 100 | let message = NSLocalizedString("Last name is too short (minimum is 2 characters)", comment: "") 101 | error.value = GJError(message) 102 | return false 103 | } 104 | 105 | if let mobile = contact.value.phoneNumber, mobile.count < 10 { 106 | let message = NSLocalizedString("Please enter a vailid mobile number. Mobile number should be of 10 digits or more.", comment: "") 107 | error.value = GJError(message) 108 | return false 109 | } 110 | 111 | if let email = contact.value.email { 112 | let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" 113 | let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegEx) 114 | if !emailPredicate.evaluate(with: email) { 115 | error.value = GJError(NSLocalizedString("Please enter a valid email.", comment: "")) 116 | return false 117 | } 118 | } 119 | return true 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactDetailsViewModel/ContactDetailsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsViewModel.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ContactDetailsViewModel { 12 | private var httpClient: HTTPClient! 13 | 14 | let editBarButtonTitle = NSLocalizedString("Edit", comment: "") 15 | 16 | var contact: Bindable! 17 | var isBusy: Bindable = Bindable(false) 18 | var error: Bindable = Bindable(nil) 19 | 20 | // MARK: - Computed Properties 21 | var contactMetadata: [ContactMetadata] { 22 | return contact.value.getDetailsMetadata() 23 | } 24 | 25 | var name: String { 26 | return contact.value.fullName 27 | } 28 | 29 | var imageURL: URL? { 30 | return URL(string: contact.value.profilePic ?? "") 31 | } 32 | 33 | var isFavorite: Bool { 34 | return contact.value.favorite 35 | } 36 | 37 | var telURL: URL? { 38 | if let phone = contact.value.phoneNumber { 39 | return URL(string: String(format: "tel://%@", phone)) 40 | } 41 | return nil 42 | } 43 | 44 | var messageURL: URL? { 45 | if let phone = contact.value.phoneNumber { 46 | return URL(string: String(format: "sms://%@", phone)) 47 | } 48 | return nil 49 | } 50 | 51 | var mailURL: URL? { 52 | if let email = contact.value.email { 53 | return URL(string: String(format: "mailto://%@", email)) 54 | } 55 | return nil 56 | } 57 | 58 | init(contact: Contact, client: HTTPClient? = nil) { 59 | self.contact = Bindable(contact) 60 | self.httpClient = client ?? HTTPClient.shared 61 | } 62 | 63 | func getContactDetails() { 64 | isBusy.value = true 65 | httpClient.dataTask(ContactAPI.getContact(id: Int(contact.value.id))) { [weak self] (result) in 66 | guard let self = self else { 67 | return 68 | } 69 | 70 | self.isBusy.value = false 71 | switch result { 72 | case .success(let data): 73 | guard let data = data else { 74 | return 75 | } 76 | 77 | do { 78 | let contact = try JSONDecoder().decode(Contact.self, from: data) 79 | CoreDataManager.shared.saveContext() 80 | self.contact.value = contact 81 | } catch { 82 | Log.error("Unable to decode Contact.", error: error) 83 | } 84 | case .failure(let error): 85 | self.error.value = GJError(error.localizedDescription) 86 | Log.error("Error in fetching Contact.", error: error) 87 | } 88 | } 89 | } 90 | 91 | func updateFavourite() { 92 | contact.value.favorite = !contact.value.favorite 93 | let contactRequest = ContactAPI.updateContact(id: Int(contact.value.id), contact: contact.value) 94 | isBusy.value = true 95 | httpClient.dataTask(contactRequest) { [weak self] (result) in 96 | guard let self = self else { 97 | return 98 | } 99 | 100 | self.isBusy.value = false 101 | switch result { 102 | case .success(let data): 103 | guard let data = data else { 104 | return 105 | } 106 | 107 | do { 108 | let contact = try JSONDecoder().decode(Contact.self, from: data) 109 | CoreDataManager.shared.saveContext() 110 | self.contact.value = contact 111 | } catch { 112 | Log.error("Unable to decode Contact.", error: error) 113 | } 114 | case .failure(let error): 115 | self.error.value = GJError(error.localizedDescription) 116 | self.contact.value.favorite = !self.contact.value.favorite 117 | Log.error("Error in updating Contact.", error: error) 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactListViewModel/ContactListViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactListViewModel.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ContactListViewModel { 12 | 13 | private var httpClient: HTTPClient! 14 | 15 | let title = NSLocalizedString("Contact", comment: "") 16 | let groupBarButtonTitle = NSLocalizedString("Groups", comment: "") 17 | 18 | var isBusy: Bindable = Bindable(false) 19 | var contacts: Bindable<[Contact]?> = Bindable(nil) 20 | var error: Bindable = Bindable(nil) 21 | 22 | init(client: HTTPClient? = nil) { 23 | self.httpClient = client ?? HTTPClient.shared 24 | } 25 | 26 | func getContacts() { 27 | isBusy.value = true 28 | httpClient.dataTask(ContactAPI.getContacts) { [weak self] (result) in 29 | guard let self = self else { 30 | return 31 | } 32 | 33 | self.isBusy.value = false 34 | switch result { 35 | case .success(let data): 36 | guard let data = data else { 37 | return 38 | } 39 | 40 | do { 41 | Contact.deleteAllContacts() 42 | let contacts = try JSONDecoder().decode([Contact].self, from: data) 43 | CoreDataManager.shared.saveContext() 44 | self.contacts.value = contacts 45 | Log.info("Contact sync successfully.") 46 | } catch { 47 | Log.error("Unable to decode Contact List", error: error) 48 | } 49 | case .failure(let error): 50 | self.error.value = GJError(error.localizedDescription) 51 | Log.error("Error in fetching Contacts", error: error) 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactListViewModel/ContactViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactViewModel.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ContactViewModel { 12 | private let contact: Contact! 13 | 14 | let name: String 15 | let imageURL: URL? 16 | let isFavorite: Bool 17 | 18 | init(contact: Contact) { 19 | self.contact = contact 20 | 21 | name = contact.fullName 22 | imageURL = URL(string: contact.profilePic ?? "") 23 | isFavorite = contact.favorite 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactDetailsTableViewCell/ContactDetailsTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsTableViewCell.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ContactDetailsTableViewCell: UITableViewCell { 12 | @IBOutlet weak var descLabel: UILabel! 13 | @IBOutlet weak var infoLabel: UILabel! 14 | 15 | static let identifier = ContactDetailsTableViewCell.name 16 | static let nib = UINib(nibName: ContactDetailsTableViewCell.name, bundle: nil) 17 | 18 | override func awakeFromNib() { 19 | super.awakeFromNib() 20 | // Initialization code 21 | } 22 | 23 | override func setSelected(_ selected: Bool, animated: Bool) { 24 | super.setSelected(selected, animated: animated) 25 | 26 | // Configure the view for the selected state 27 | } 28 | 29 | func config(metaData: ContactMetadata) { 30 | descLabel.text = metaData.desc 31 | infoLabel.text = metaData.info 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactDetailsTableViewCell/ContactDetailsTableViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewCell/ContactEditTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactEditTableViewCell.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ContactEditTableViewCellDelegate: class { 12 | func textChanged(contactMetaData: ContactMetadata, text: String) 13 | } 14 | 15 | class ContactEditTableViewCell: UITableViewCell { 16 | 17 | @IBOutlet weak var descLabel: UILabel! 18 | @IBOutlet weak var infoTextField: BindableTextField! { 19 | didSet { 20 | infoTextField.bind {[weak self] in 21 | guard let self = self else { 22 | return 23 | } 24 | self.delegate?.textChanged(contactMetaData: self.contactMetadata, text: $0) 25 | } 26 | } 27 | } 28 | 29 | var contactMetadata: ContactMetadata! 30 | weak var delegate: ContactEditTableViewCellDelegate? 31 | 32 | static let identifier = ContactEditTableViewCell.name 33 | static let nib = UINib(nibName: ContactEditTableViewCell.name, bundle: nil) 34 | 35 | override func awakeFromNib() { 36 | super.awakeFromNib() 37 | // Initialization code 38 | } 39 | 40 | override func setSelected(_ selected: Bool, animated: Bool) { 41 | super.setSelected(selected, animated: animated) 42 | 43 | // Configure the view for the selected state 44 | } 45 | 46 | func config(metaData: ContactMetadata) { 47 | contactMetadata = metaData 48 | descLabel.text = metaData.desc 49 | infoTextField.text = metaData.info 50 | infoTextField.keyboardType = metaData.keyboardType 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewHeader/ContactEditTableViewHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactEditTableViewHeader.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ContactEditTableViewHeaderDelegate: class { 12 | func takeImageButtonAction(_ sender: UIButton) 13 | } 14 | 15 | class ContactEditTableViewHeader: UIView { 16 | @IBOutlet weak var imageView: CircularImageView! 17 | @IBOutlet weak var takeImageButton: UIButton! 18 | 19 | weak var delegate: ContactEditTableViewHeaderDelegate? 20 | 21 | class func get(image: UIImage = UIImage.Contact.placeHolder) -> ContactEditTableViewHeader { 22 | guard let view = Bundle.main.loadNibNamed(ContactEditTableViewHeader.name, 23 | owner: nil, options: nil)? 24 | .first as? ContactEditTableViewHeader else { 25 | fatalError("Unable to load nib ContactEditTableViewHeader.") 26 | } 27 | view.imageView.image = image 28 | return view 29 | } 30 | 31 | @IBAction func takeImageButtonAction(_ sender: UIButton) { 32 | delegate?.takeImageButtonAction(sender) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactTableViewCell/ContactTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactTableViewCell.swift 3 | // GJAssignment 4 | // 5 | // Created by Anonymous on 17/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ContactTableViewCell: UITableViewCell { 12 | @IBOutlet weak var contactImageView: UIImageView! 13 | @IBOutlet weak var contactName: UILabel! 14 | @IBOutlet weak var favoriteImageView: UIImageView! 15 | 16 | static let identifier = ContactTableViewCell.name 17 | static let nib = UINib(nibName: ContactTableViewCell.name, bundle: nil) 18 | 19 | override func awakeFromNib() { 20 | super.awakeFromNib() 21 | } 22 | 23 | override func setSelected(_ selected: Bool, animated: Bool) { 24 | super.setSelected(selected, animated: animated) 25 | } 26 | 27 | override func layoutSubviews() { 28 | super.layoutSubviews() 29 | contactImageView.makeCircle() 30 | } 31 | 32 | func config(contact: Contact) { 33 | let viewModel = ContactViewModel(contact: contact) 34 | contactName.text = viewModel.name 35 | contactImageView.image = UIImage.Contact.placeHolder 36 | favoriteImageView.isHidden = !viewModel.isFavorite 37 | favoriteImageView.image = viewModel.isFavorite ? UIImage.Contact.showFavorite : nil 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/AddContactTests/AddContactTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddContactViewModelTest.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import GJAssignment 11 | 12 | class AddContactTests: XCTestCase { 13 | 14 | var addContactViewModel: AddContactViewModel! 15 | 16 | override func setUp() { 17 | let session = MockSession() 18 | let client = HTTPClient(session: session) 19 | addContactViewModel = AddContactViewModel(contact: nil, client: client) 20 | } 21 | 22 | override func tearDown() { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | } 25 | 26 | func testCancelBarButtonTitle() { 27 | XCTAssert(addContactViewModel.cancelBarButtonTitle == "Cancel", "Cancel button title mismatch.") 28 | } 29 | 30 | func testDoneBarButtonTitle() { 31 | XCTAssert(addContactViewModel.doneBarButtonTitle == "Done", "Done button title mismatch.") 32 | } 33 | 34 | func testAddContact() { 35 | addContactViewModel.contact.value = MockContact.getComplete() 36 | 37 | let expectation = self.expectation(description: "No response recevice from contact list API.") 38 | 39 | addContactViewModel.error.bind { (error) in 40 | XCTAssert(error == nil, error!.localizedDescription) 41 | } 42 | addContactViewModel.isContactSync.bind { (isSync) in 43 | if isSync { 44 | expectation.fulfill() 45 | } 46 | } 47 | 48 | addContactViewModel.syncContact() 49 | self.waitForExpectations(timeout: 10.0, handler: nil) 50 | } 51 | 52 | func testAddContactResponse() { 53 | addContactViewModel.contact.value = MockContact.getComplete() 54 | 55 | var isContactSync = false 56 | let expectation = self.expectation(description: "No response recevice from contact list API.") 57 | 58 | addContactViewModel.error.bind { (error) in 59 | XCTAssert(error == nil, error!.localizedDescription) 60 | } 61 | addContactViewModel.isContactSync.bind { (isSync) in 62 | isContactSync = isSync 63 | } 64 | addContactViewModel.contact.bind { (contact) in 65 | if isContactSync { 66 | XCTAssert(contact.firstName == "Anonymous", "Contact fist name mismatch.") 67 | XCTAssert(contact.lastName == "Anonymous", "Contact last name mismatch.") 68 | XCTAssert(contact.phoneNumber == "+910987654321", "Contact phone number mismatch.") 69 | XCTAssert(contact.email == "vc@gmail.com", "Contact phone number mismatch.") 70 | XCTAssert(contact.favorite == false, "Contact favorite status mismatch.") 71 | expectation.fulfill() 72 | } 73 | } 74 | 75 | addContactViewModel.syncContact() 76 | self.waitForExpectations(timeout: 10.0, handler: nil) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsErrorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsErrorTests.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import GJAssignment 11 | 12 | class ContactDetailsErrorTests: XCTestCase { 13 | 14 | var contact: Contact! 15 | var contactDetailsViewModel: ContactDetailsViewModel! 16 | 17 | override func setUp() { 18 | contact = MockContact.getPartial() 19 | 20 | let session = BadMockSession() 21 | let client = HTTPClient(session: session) 22 | contactDetailsViewModel = ContactDetailsViewModel(contact: contact, client: client) 23 | } 24 | 25 | override func tearDown() { 26 | 27 | } 28 | 29 | func testContactsAPIFailedResponse() { 30 | let expectation = self.expectation(description: "No error returns by contact API.") 31 | 32 | contactDetailsViewModel.error.bind { (error) in 33 | if error != nil { 34 | expectation.fulfill() 35 | } 36 | } 37 | 38 | contactDetailsViewModel.getContactDetails() 39 | self.waitForExpectations(timeout: 10.0, handler: nil) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsViewModelTest.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import GJAssignment 11 | 12 | class ContactDetailsTests: XCTestCase { 13 | 14 | var contact: Contact! 15 | var contactDetailsViewModel: ContactDetailsViewModel! 16 | 17 | override func setUp() { 18 | contact = MockContact.getPartial() 19 | 20 | let session = MockSession() 21 | let client = HTTPClient(session: session) 22 | contactDetailsViewModel = ContactDetailsViewModel(contact: contact, client: client) 23 | } 24 | 25 | override func tearDown() { 26 | 27 | } 28 | 29 | func testEditBarButtonTitle() { 30 | XCTAssert(contactDetailsViewModel.editBarButtonTitle == "Edit", "Contact edit button title mismatch.") 31 | } 32 | 33 | func testContactAPIResponse() { 34 | let expectation = self.expectation(description: "No response recevice from contact list API.") 35 | 36 | contactDetailsViewModel.error.bind { (error) in 37 | XCTAssert(error == nil, error!.localizedDescription) 38 | } 39 | 40 | contactDetailsViewModel.contact.bind { (contact) in 41 | if contact.phoneNumber != nil { 42 | expectation.fulfill() 43 | } 44 | } 45 | 46 | contactDetailsViewModel.getContactDetails() 47 | self.waitForExpectations(timeout: 10.0, handler: nil) 48 | } 49 | 50 | func testUpdateContactFavoriteStatus() { 51 | let expectation = self.expectation(description: "No response recevice from contact list API.") 52 | let oldStatus = contact.favorite 53 | 54 | contactDetailsViewModel.error.bind { (error) in 55 | XCTAssert(error == nil, error!.localizedDescription) 56 | } 57 | 58 | contactDetailsViewModel.contact.bind { (contact) in 59 | if contact.favorite != oldStatus { 60 | expectation.fulfill() 61 | } 62 | } 63 | 64 | contactDetailsViewModel.updateFavourite() 65 | self.waitForExpectations(timeout: 100.0, handler: nil) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsViewModelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsViewModelTests.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import GJAssignment 11 | 12 | class ContactDetailsViewModelTests: XCTestCase { 13 | 14 | var contact: Contact! 15 | var contactDetailsViewModel: ContactDetailsViewModel! 16 | 17 | override func setUp() { 18 | contact = MockContact.getComplete() 19 | 20 | let session = MockSession() 21 | let client = HTTPClient(session: session) 22 | contactDetailsViewModel = ContactDetailsViewModel(contact: contact, client: client) 23 | } 24 | 25 | override func tearDown() { 26 | 27 | } 28 | 29 | func testCheckContactViewModel() { 30 | XCTAssert(contactDetailsViewModel.name == "Anonymous", "Contact full name mismatch.") 31 | XCTAssert(contactDetailsViewModel.imageURL == URL(string: "image.png"), "Contact image url mismatch.") 32 | XCTAssert(contactDetailsViewModel.isFavorite == false, "Contact favorite status mismatch.") 33 | XCTAssert(contactDetailsViewModel.telURL == URL(string: "tel://+910987654321"), "Contact phone number url mismatch.") 34 | XCTAssert(contactDetailsViewModel.messageURL == URL(string: "sms://+910987654321"), "Contact message url mismatch.") 35 | XCTAssert(contactDetailsViewModel.mailURL == URL(string: "mailto://vc@gmail.com"), "Contact email url mismatch.") 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactListErrorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactListErrorTests.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import GJAssignment 11 | 12 | class ContactListErrorTests: XCTestCase { 13 | 14 | var contactListViewModel: ContactListViewModel! 15 | 16 | override func setUp() { 17 | let session = BadMockSession() 18 | let client = HTTPClient(session: session) 19 | contactListViewModel = ContactListViewModel(client: client) 20 | } 21 | 22 | override func tearDown() { 23 | 24 | } 25 | 26 | func testContactsAPIFailedResponse() { 27 | let expectation = self.expectation(description: "No error return by API.") 28 | 29 | contactListViewModel.error.bind { (error) in 30 | if error != nil { 31 | expectation.fulfill() 32 | } 33 | } 34 | 35 | contactListViewModel.getContacts() 36 | self.waitForExpectations(timeout: 10.0, handler: nil) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactListTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactListViewModelTest.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import CoreData 11 | @testable import GJAssignment 12 | 13 | class ContactListTests: XCTestCase { 14 | 15 | var contactListViewModel: ContactListViewModel! 16 | 17 | override func setUp() { 18 | let session = MockSession() 19 | let client = HTTPClient(session: session) 20 | contactListViewModel = ContactListViewModel(client: client) 21 | } 22 | 23 | override func tearDown() { 24 | 25 | } 26 | 27 | func testContactListTitle() { 28 | XCTAssert(contactListViewModel.title == "Contact", "Contact list title mismatch.") 29 | } 30 | 31 | func testGroupBarButtonTitle() { 32 | XCTAssert(contactListViewModel.groupBarButtonTitle == "Groups", "Contact list title mismatch.") 33 | } 34 | 35 | func testContactsAPISuccessResponse() { 36 | let expectation = self.expectation(description: "No response recevice from contact list API.") 37 | 38 | contactListViewModel.error.bind { (error) in 39 | XCTAssert(error == nil, error!.localizedDescription) 40 | } 41 | 42 | contactListViewModel.contacts.bind { (contacts) in 43 | if contacts != nil { 44 | expectation.fulfill() 45 | } 46 | } 47 | 48 | contactListViewModel.getContacts() 49 | self.waitForExpectations(timeout: 10.0, handler: nil) 50 | } 51 | 52 | func testContactsAPIResultCount() { 53 | let expectation = self.expectation(description: "Invalid number of contact returns by contact list API.") 54 | 55 | contactListViewModel.error.bind { (error) in 56 | XCTAssert(error == nil, error!.localizedDescription) 57 | } 58 | 59 | contactListViewModel.contacts.bind { (contacts) in 60 | if contacts?.count == 3 { 61 | expectation.fulfill() 62 | } 63 | } 64 | 65 | contactListViewModel.getContacts() 66 | self.waitForExpectations(timeout: 10.0, handler: nil) 67 | } 68 | 69 | func testConactsAPIObject() { 70 | let expectation = self.expectation(description: "Invalid number of contact returns by contact list API.") 71 | 72 | contactListViewModel.error.bind { (error) in 73 | XCTAssert(error == nil, error!.localizedDescription) 74 | } 75 | 76 | contactListViewModel.contacts.bind { (contacts) in 77 | if let contact = contacts?[0] { 78 | XCTAssert(contact.sectionTitle == "V", "Contact section title is incorrect.") 79 | XCTAssert(contact.fullName == "Anonymous", "Contact full name is incorrect.") 80 | XCTAssert(contact.firstName == "Anonymous", "Contact first name is incorrect.") 81 | XCTAssert(contact.lastName == "Anonymous", "Contact last name is incorrect.") 82 | XCTAssert(contact.favorite == false, "Contact favorite status is incorrect.") 83 | XCTAssert(contact.phoneNumber == nil, "Contact phone number must be nil.") 84 | XCTAssert(contact.email == nil, "Contact email must be nil.") 85 | expectation.fulfill() 86 | } 87 | } 88 | 89 | contactListViewModel.getContacts() 90 | self.waitForExpectations(timeout: 10.0, handler: nil) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactViewModelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GJAssignmentTests.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 14/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import GJAssignment 11 | 12 | class ContactViewModelTests: XCTestCase { 13 | 14 | var contact: Contact! 15 | var contactViewModel: ContactViewModel! 16 | 17 | override func setUp() { 18 | contact = MockContact.getPartial() 19 | contactViewModel = ContactViewModel(contact: contact) 20 | } 21 | 22 | override func tearDown() { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | } 25 | 26 | func testContactFullName() { 27 | XCTAssert(contactViewModel.name == "Anonymous", "Complete name mismatch.") 28 | } 29 | 30 | func testFavoriteContact() { 31 | XCTAssert(contactViewModel.isFavorite == false, "Contact was favorite but view model return contact is not favorite") 32 | } 33 | 34 | func testProfilePicURL() { 35 | let testURL = URL(string: "image.png") 36 | XCTAssert(contactViewModel.imageURL == testURL, "Contact profile URL mismatch.") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/MockContact.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockContact.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | @testable import GJAssignment 13 | 14 | struct MockContact { 15 | static func getEmpty() -> Contact { 16 | let managedObjectContext = CoreDataManager.shared.managedObjectContext 17 | guard let entity = NSEntityDescription.entity(forEntityName: Contact.name, in: managedObjectContext) else { 18 | fatalError("Unable to create Contact entity.") 19 | } 20 | 21 | let contact = Contact(entity: entity, insertInto: nil) 22 | return contact 23 | } 24 | 25 | static func getPartial() -> Contact { 26 | let contact = getEmpty() 27 | contact.id = 1 28 | contact.firstName = "Anonymous" 29 | contact.lastName = "Anonymous" 30 | contact.profilePic = "image.png" 31 | contact.favorite = false 32 | return contact 33 | } 34 | 35 | static func getComplete() -> Contact { 36 | let contact = getPartial() 37 | contact.phoneNumber = "+910987654321" 38 | contact.email = "vc@gmail.com" 39 | return contact 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "first_name": "Anonymous", 4 | "last_name": "Anonymous", 5 | "email": "vc@gmail.com", 6 | "phone_number": "+910987654321", 7 | "profile_pic": "/images/missing.png", 8 | "favorite": false, 9 | "created_at": "2019-08-17T18:45:58.243Z", 10 | "updated_at": "2019-08-17T18:45:58.243Z" 11 | } 12 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/contacts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "first_name": "Anonymous", 5 | "last_name": "Anonymous", 6 | "profile_pic": "/images/missing.png", 7 | "favorite": false, 8 | "url": "https://gojek-contacts-app.herokuapp.com/contacts/1.json" 9 | }, 10 | { 11 | "id": 2, 12 | "first_name": "Hello", 13 | "last_name": "There", 14 | "profile_pic": "/images/missing.png", 15 | "favorite": false, 16 | "url": "https://gojek-contacts-app.herokuapp.com/contacts/2.json" 17 | }, 18 | { 19 | "id": 3, 20 | "first_name": "Go", 21 | "last_name": "Jek", 22 | "profile_pic": "/images/missing.png", 23 | "favorite": true, 24 | "url": "https://gojek-contacts-app.herokuapp.com/contacts/3.json" 25 | } 26 | ] 27 | 28 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/fav_contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "first_name": "Anonymous", 4 | "last_name": "Anonymous", 5 | "email": "vc@gmail.com", 6 | "phone_number": "+910987654321", 7 | "profile_pic": "/images/missing.png", 8 | "favorite": true, 9 | "created_at": "2019-08-17T18:45:58.243Z", 10 | "updated_at": "2019-08-17T18:45:58.243Z" 11 | } 12 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/not_found.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "404", 3 | "error": "Not Found" 4 | } 5 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/BadMockSession.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BadMockSession.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | @testable import GJAssignment 11 | 12 | //BadMockSession will always return error or nil response 13 | class BadMockSession: URLSessionProtocol { 14 | func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol { 15 | 16 | if let mockRequest = MockRequest.identifyRequest(request: request) { 17 | mockRequest.badCompletionHandler(request: request, completion: completionHandler) 18 | } 19 | 20 | return MockDataTask() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/MockDataTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MokeDataTask.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | @testable import GJAssignment 11 | 12 | class MockDataTask: URLSessionDataTaskProtocol { 13 | func resume() { 14 | } 15 | 16 | func cancel() { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/MockSession.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockSession.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 18/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | @testable import GJAssignment 11 | 12 | //MokeSession or Good MockSession always return success response 13 | class MockSession: URLSessionProtocol { 14 | func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol { 15 | 16 | if let mockRequest = MockRequest.identifyRequest(request: request) { 17 | mockRequest.completionHandler(request: request, completion: completionHandler) 18 | } 19 | 20 | return MockDataTask() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentTests/UpdateContactTests/UpdateContactTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateContactTests.swift 3 | // GJAssignmentTests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import GJAssignment 11 | 12 | class UpdateContactTests: XCTestCase { 13 | 14 | var contact: Contact! 15 | var addContactViewModel: AddContactViewModel! 16 | 17 | override func setUp() { 18 | let session = MockSession() 19 | let client = HTTPClient(session: session) 20 | contact = MockContact.getComplete() 21 | addContactViewModel = AddContactViewModel(contact: contact, client: client) 22 | } 23 | 24 | override func tearDown() { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | } 27 | 28 | func testAddContact() { 29 | let expectation = self.expectation(description: "No response recevice from contact list API.") 30 | 31 | addContactViewModel.error.bind { (error) in 32 | XCTAssert(error == nil, error!.localizedDescription) 33 | } 34 | addContactViewModel.isContactSync.bind { (isSync) in 35 | if isSync { 36 | expectation.fulfill() 37 | } 38 | } 39 | 40 | addContactViewModel.syncContact() 41 | self.waitForExpectations(timeout: 10.0, handler: nil) 42 | } 43 | 44 | func testAddContactResponse() { 45 | addContactViewModel.contact.value = MockContact.getComplete() 46 | 47 | var isContactSync = false 48 | let expectation = self.expectation(description: "No response recevice from contact list API.") 49 | 50 | addContactViewModel.error.bind { (error) in 51 | XCTAssert(error == nil, error!.localizedDescription) 52 | } 53 | addContactViewModel.isContactSync.bind { (isSync) in 54 | isContactSync = isSync 55 | } 56 | addContactViewModel.contact.bind { (contact) in 57 | if isContactSync { 58 | XCTAssert(contact.firstName == "Anonymous", "Contact fist name mismatch.") 59 | XCTAssert(contact.lastName == "Anonymous", "Contact last name mismatch.") 60 | XCTAssert(contact.phoneNumber == "+910987654321", "Contact phone number mismatch.") 61 | XCTAssert(contact.email == "vc@gmail.com", "Contact phone number mismatch.") 62 | XCTAssert(contact.favorite == true, "Contact favorite status mismatch.") 63 | expectation.fulfill() 64 | } 65 | } 66 | 67 | addContactViewModel.syncContact() 68 | self.waitForExpectations(timeout: 10.0, handler: nil) 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/CustomControlTests/ImagePickerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagePickerTests.swift 3 | // GJAssignmentUITests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ImagePickerTests: XCTestCase { 12 | 13 | private var app: XCUIApplication! 14 | 15 | override func setUp() { 16 | continueAfterFailure = false 17 | app = XCUIApplication() 18 | app.launch() 19 | } 20 | 21 | override func tearDown() { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testImagePickerActionSheet() { 26 | openImagePicker() 27 | 28 | let chooseImageSheet = app.sheets["Choose Image"] 29 | XCTAssertTrue(chooseImageSheet.exists, "Action sheet not presented.") 30 | 31 | //Camera button will not be available in Simulator 32 | if !isSimulartor() { 33 | let cameraButton = chooseImageSheet.buttons["Camera"] 34 | XCTAssertTrue(cameraButton.exists, "Camera button not available") 35 | } 36 | 37 | let galleryButton = chooseImageSheet.buttons["Gallery"] 38 | XCTAssertTrue(galleryButton.exists, "Gallery button not available") 39 | 40 | let cancelButton = chooseImageSheet.buttons["Cancel"] 41 | XCTAssertTrue(cancelButton.exists, "Cancel button not available") 42 | } 43 | 44 | func testCameraImage() { 45 | openImagePicker() 46 | 47 | let chooseImageSheet = app.sheets["Choose Image"] 48 | XCTAssertTrue(chooseImageSheet.exists, "Action sheet not presented.") 49 | 50 | if isSimulartor() { 51 | return 52 | } 53 | 54 | let cameraButton = chooseImageSheet.buttons["Camera"] 55 | cameraButton.tap() 56 | 57 | let permissionAlert = app.alerts["“GJAssignment” Would Like to Access the Camera"] 58 | if permissionAlert.exists { 59 | let allowPermissionButton = permissionAlert.buttons["OK"] 60 | allowPermissionButton.tap() 61 | } 62 | 63 | let photoCaptureButton = app.buttons["PhotoCapture"] 64 | XCTAssertTrue(photoCaptureButton.exists, "Photo capture button not exist.") 65 | photoCaptureButton.tap() 66 | } 67 | 68 | private func openImagePicker() { 69 | let contactNavigationBar = app.navigationBars["Contact"] 70 | let addButton = contactNavigationBar.buttons["Add"] 71 | addButton.tap() 72 | 73 | let tablesQuery = app.tables 74 | let selectimagebuttonButton = tablesQuery.buttons["selectImageButton"] 75 | selectimagebuttonButton.tap() 76 | } 77 | 78 | private func isSimulartor() -> Bool { 79 | return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/AddContactNavigationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddContactNavigationTests.swift 3 | // GJAssignmentUITests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class AddContactNavigationTests: XCTestCase { 12 | 13 | private var app: XCUIApplication! 14 | 15 | override func setUp() { 16 | continueAfterFailure = false 17 | app = XCUIApplication() 18 | app.launch() 19 | } 20 | 21 | override func tearDown() { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testAddContactNavigationBar() { 26 | let contactNavigationBar = app.navigationBars["Contact"] 27 | let addButton = contactNavigationBar.buttons["Add"] 28 | addButton.tap() 29 | 30 | let addContactNavigationBar = app.navigationBars["GJAssignment.AddContactView"] 31 | 32 | let cancelButton = addContactNavigationBar.buttons["Cancel"] 33 | XCTAssertTrue(cancelButton.exists, "Cancel button not exists in add contact navigation bar.") 34 | 35 | let doneButton = addContactNavigationBar.buttons["Done"] 36 | XCTAssertTrue(doneButton.exists, "Done button not exists in add contact navigation bar.") 37 | } 38 | 39 | func testAddContactNavigationBarCancelButtonAction() { 40 | let contactNavigationBar = app.navigationBars["Contact"] 41 | let addButton = contactNavigationBar.buttons["Add"] 42 | addButton.tap() 43 | 44 | let addContactNavigationBar = app.navigationBars["GJAssignment.AddContactView"] 45 | 46 | let cancelButton = addContactNavigationBar.buttons["Cancel"] 47 | cancelButton.tap() 48 | 49 | XCTAssertTrue(contactNavigationBar.isHittable, "Contact list navigation bar not exist after dismising the add contact view controller.") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/ContactDetailsNavigationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsNavigationTests.swift 3 | // GJAssignmentUITests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ContactDetailsNavigationTests: XCTestCase { 12 | 13 | private var app: XCUIApplication! 14 | 15 | override func setUp() { 16 | continueAfterFailure = false 17 | app = XCUIApplication() 18 | app.launch() 19 | } 20 | 21 | override func tearDown() { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testContactDetailsNavigation() { 26 | tapContactListFirstCell() 27 | 28 | let contactNavigationBar = app.navigationBars["Contact"] 29 | let backButton = contactNavigationBar.buttons["Contact"] 30 | XCTAssertTrue(backButton.exists, "Back button not exist in navigation bar.") 31 | 32 | let editButton = contactNavigationBar.buttons["Edit"] 33 | XCTAssertTrue(editButton.exists, "Back button not exist in navigation bar.") 34 | } 35 | 36 | func testContactEditNavigationFromContactDetails() { 37 | tapContactListFirstCell() 38 | let contactNavigationBar = app.navigationBars["Contact"] 39 | 40 | let editButton = contactNavigationBar.buttons["Edit"] 41 | XCTAssertTrue(editButton.exists, "Back button not exist in navigation bar.") 42 | 43 | editButton.tap() 44 | 45 | let editNavigationBar = app.navigationBars["GJAssignment.AddContactView"] 46 | XCTAssertTrue(editNavigationBar.exists, "Edit navigation bar not exist.") 47 | } 48 | 49 | private func tapContactListFirstCell() { 50 | let myTable = app.tables.matching(identifier: "contactListTableView") 51 | let cell = myTable.cells.element(matching: .cell, identifier: "contactTableViewCell_0_0") 52 | 53 | let predicate = NSPredicate(format: "isHittable == true") 54 | let expectationEval = expectation(for: predicate, evaluatedWith: cell, handler: nil) 55 | let waiter = XCTWaiter.wait(for: [expectationEval], timeout: 30.0) 56 | XCTAssert(XCTWaiter.Result.completed == waiter, "Failed time out waiting for rate") 57 | 58 | cell.tap() 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/ContactListNavigationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationTests.swift 3 | // GJAssignmentUITests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class NavigationTests: XCTestCase { 12 | 13 | private var app: XCUIApplication! 14 | 15 | override func setUp() { 16 | continueAfterFailure = false 17 | app = XCUIApplication() 18 | app.launch() 19 | } 20 | 21 | override func tearDown() { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testContactListNavigation() { 26 | let contactNavigationBar = app.navigationBars["Contact"] 27 | XCTAssertTrue(contactNavigationBar.exists, "Contact list navigation bar not exist.") 28 | 29 | let groupButton = contactNavigationBar.buttons["Groups"] 30 | XCTAssertTrue(groupButton.exists, "Group button not exists in contact list navigation bar.") 31 | 32 | let addButton = contactNavigationBar.buttons["Add"] 33 | XCTAssertTrue(addButton.exists, "Group button not exists in contact list navigation bar.") 34 | } 35 | 36 | func testAddContactNavigationFromContactList() { 37 | let contactNavigationBar = app.navigationBars["Contact"] 38 | let addButton = contactNavigationBar.buttons["Add"] 39 | addButton.tap() 40 | 41 | let addContactNavigationBar = app.navigationBars["GJAssignment.AddContactView"] 42 | XCTAssertTrue(addContactNavigationBar.exists, "Add contact navigation bar not exist.") 43 | } 44 | 45 | func testContactDetailsNavigationFromContactList() { 46 | let myTable = app.tables.matching(identifier: "contactListTableView") 47 | let cell = myTable.cells.element(matching: .cell, identifier: "contactTableViewCell_0_0") 48 | 49 | let predicate = NSPredicate(format: "isHittable == true") 50 | let expectationEval = expectation(for: predicate, evaluatedWith: cell, handler: nil) 51 | let waiter = XCTWaiter.wait(for: [expectationEval], timeout: 30.0) 52 | XCTAssert(XCTWaiter.Result.completed == waiter, "Failed time out waiting for rate") 53 | 54 | cell.tap() 55 | 56 | let contactNavigationBar = app.navigationBars["Contact"] 57 | let backButton = contactNavigationBar.buttons["Contact"] 58 | XCTAssertTrue(contactNavigationBar.exists && backButton.exists, "Contact list navigation bar not exist.") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/ViewControllerTests/ContactDetailsControllerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactDetailsControllerTests.swift 3 | // GJAssignmentUITests 4 | // 5 | // Created by Anonymous on 19/08/19. 6 | // Copyright © 2019 Anonymous. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ContactDetailsControllerTests: XCTestCase { 12 | 13 | private var app: XCUIApplication! 14 | 15 | override func setUp() { 16 | continueAfterFailure = false 17 | app = XCUIApplication() 18 | app.launch() 19 | } 20 | 21 | override func tearDown() { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testAddContactElementsExistence() { 26 | tapContactListFirstCell() 27 | 28 | let table = app.tables.matching(identifier: "detailsTableView") 29 | let mobileNumberCell = table.cells.element(matching: .cell, identifier: "detailsTableViewCell_0") 30 | XCTAssertTrue(mobileNumberCell.exists, "Mobile number table view cell not exist.") 31 | 32 | let mobileDescLabel = mobileNumberCell.staticTexts["descLabel"] 33 | XCTAssertTrue(mobileDescLabel.exists, "Mobile number placeholder label not exist.") 34 | 35 | let mobileInfoLabel = mobileNumberCell.staticTexts["infoLabel"] 36 | XCTAssertTrue(mobileInfoLabel.exists, "Mobile number value label not exist.") 37 | 38 | let emailCell = table.cells.element(matching: .cell, identifier: "detailsTableViewCell_1") 39 | XCTAssertTrue(emailCell.exists, "Email table view cell not exist.") 40 | 41 | let emailDescLabel = emailCell.staticTexts["descLabel"] 42 | XCTAssertTrue(emailDescLabel.exists, "Email placeholder label not exist.") 43 | 44 | let emailInfoLabel = emailCell.staticTexts["infoLabel"] 45 | XCTAssertTrue(emailInfoLabel.exists, "Email value label not exist.") 46 | 47 | let contactImageView = app.otherElements.containing(.image, identifier: "contactImageView").firstMatch 48 | XCTAssertTrue(contactImageView.exists, "Contact image view not exist.") 49 | 50 | let callImageView = app.otherElements.containing(.image, identifier: "callActionImage").firstMatch 51 | XCTAssertTrue(callImageView.exists, "Call image view not exist.") 52 | 53 | let msgImageView = app.otherElements.containing(.image, identifier: "messageActionImage").firstMatch 54 | XCTAssertTrue(msgImageView.exists, "Message image view not exist.") 55 | 56 | let emailImageView = app.otherElements.containing(.image, identifier: "emailActionImage").firstMatch 57 | XCTAssertTrue(emailImageView.exists, "Email image view not exist.") 58 | 59 | let favImageView = app.otherElements.containing(.image, identifier: "favoriteActionImage").firstMatch 60 | XCTAssertTrue(favImageView.exists, "Favorite image view not exist.") 61 | } 62 | 63 | private func tapContactListFirstCell() { 64 | let myTable = app.tables.matching(identifier: "contactListTableView") 65 | let cell = myTable.cells.element(matching: .cell, identifier: "contactTableViewCell_0_0") 66 | 67 | let predicate = NSPredicate(format: "isHittable == true") 68 | let expectationEval = expectation(for: predicate, evaluatedWith: cell, handler: nil) 69 | let waiter = XCTWaiter.wait(for: [expectationEval], timeout: 30.0) 70 | XCTAssert(XCTWaiter.Result.completed == waiter, "Failed time out waiting for rate") 71 | 72 | cell.tap() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.0) 5 | addressable (2.8.0) 6 | public_suffix (>= 2.0.2, < 5.0) 7 | atomos (0.1.3) 8 | babosa (1.0.2) 9 | claide (1.0.3) 10 | colored (1.2) 11 | colored2 (3.1.2) 12 | commander-fastlane (4.4.6) 13 | highline (~> 1.7.2) 14 | declarative (0.0.10) 15 | declarative-option (0.1.0) 16 | digest-crc (0.4.1) 17 | domain_name (0.5.20190701) 18 | unf (>= 0.0.5, < 1.0.0) 19 | dotenv (2.7.5) 20 | emoji_regex (1.0.1) 21 | excon (0.71.0) 22 | faraday (0.15.4) 23 | multipart-post (>= 1.2, < 3) 24 | faraday-cookie_jar (0.0.6) 25 | faraday (>= 0.7.4) 26 | http-cookie (~> 1.0.0) 27 | faraday_middleware (0.13.1) 28 | faraday (>= 0.7.4, < 1.0) 29 | fastimage (2.1.5) 30 | fastlane (2.129.0) 31 | CFPropertyList (>= 2.3, < 4.0.0) 32 | addressable (>= 2.3, < 3.0.0) 33 | babosa (>= 1.0.2, < 2.0.0) 34 | bundler (>= 1.12.0, < 3.0.0) 35 | colored 36 | commander-fastlane (>= 4.4.6, < 5.0.0) 37 | dotenv (>= 2.1.1, < 3.0.0) 38 | emoji_regex (>= 0.1, < 2.0) 39 | excon (>= 0.45.0, < 1.0.0) 40 | faraday (~> 0.9) 41 | faraday-cookie_jar (~> 0.0.6) 42 | faraday_middleware (~> 0.9) 43 | fastimage (>= 2.1.0, < 3.0.0) 44 | gh_inspector (>= 1.1.2, < 2.0.0) 45 | google-api-client (>= 0.21.2, < 0.24.0) 46 | google-cloud-storage (>= 1.15.0, < 2.0.0) 47 | highline (>= 1.7.2, < 2.0.0) 48 | json (< 3.0.0) 49 | jwt (~> 2.1.0) 50 | mini_magick (>= 4.9.4, < 5.0.0) 51 | multi_xml (~> 0.5) 52 | multipart-post (~> 2.0.0) 53 | plist (>= 3.1.0, < 4.0.0) 54 | public_suffix (~> 2.0.0) 55 | rubyzip (>= 1.2.2, < 2.0.0) 56 | security (= 0.1.3) 57 | simctl (~> 1.6.3) 58 | slack-notifier (>= 2.0.0, < 3.0.0) 59 | terminal-notifier (>= 2.0.0, < 3.0.0) 60 | terminal-table (>= 1.4.5, < 2.0.0) 61 | tty-screen (>= 0.6.3, < 1.0.0) 62 | tty-spinner (>= 0.8.0, < 1.0.0) 63 | word_wrap (~> 1.0.0) 64 | xcodeproj (>= 1.8.1, < 2.0.0) 65 | xcpretty (~> 0.3.0) 66 | xcpretty-travis-formatter (>= 0.0.3) 67 | gh_inspector (1.1.3) 68 | google-api-client (0.23.9) 69 | addressable (~> 2.5, >= 2.5.1) 70 | googleauth (>= 0.5, < 0.7.0) 71 | httpclient (>= 2.8.1, < 3.0) 72 | mime-types (~> 3.0) 73 | representable (~> 3.0) 74 | retriable (>= 2.0, < 4.0) 75 | signet (~> 0.9) 76 | google-cloud-core (1.3.0) 77 | google-cloud-env (~> 1.0) 78 | google-cloud-env (1.2.0) 79 | faraday (~> 0.11) 80 | google-cloud-storage (1.16.0) 81 | digest-crc (~> 0.4) 82 | google-api-client (~> 0.23) 83 | google-cloud-core (~> 1.2) 84 | googleauth (>= 0.6.2, < 0.10.0) 85 | googleauth (0.6.7) 86 | faraday (~> 0.12) 87 | jwt (>= 1.4, < 3.0) 88 | memoist (~> 0.16) 89 | multi_json (~> 1.11) 90 | os (>= 0.9, < 2.0) 91 | signet (~> 0.7) 92 | highline (1.7.10) 93 | http-cookie (1.0.3) 94 | domain_name (~> 0.5) 95 | httpclient (2.8.3) 96 | json (2.3.1) 97 | jwt (2.1.0) 98 | memoist (0.16.0) 99 | mime-types (3.2.2) 100 | mime-types-data (~> 3.2015) 101 | mime-types-data (3.2019.0331) 102 | mini_magick (4.9.5) 103 | multi_json (1.13.1) 104 | multi_xml (0.6.0) 105 | multipart-post (2.0.0) 106 | nanaimo (0.2.6) 107 | naturally (2.2.0) 108 | os (1.0.1) 109 | plist (3.5.0) 110 | public_suffix (2.0.5) 111 | representable (3.0.4) 112 | declarative (< 0.1.0) 113 | declarative-option (< 0.2.0) 114 | uber (< 0.2.0) 115 | retriable (3.1.2) 116 | rouge (2.0.7) 117 | rubyzip (1.3.0) 118 | security (0.1.3) 119 | signet (0.11.0) 120 | addressable (~> 2.3) 121 | faraday (~> 0.9) 122 | jwt (>= 1.5, < 3.0) 123 | multi_json (~> 1.10) 124 | simctl (1.6.5) 125 | CFPropertyList 126 | naturally 127 | slack-notifier (2.3.2) 128 | terminal-notifier (2.0.0) 129 | terminal-table (1.8.0) 130 | unicode-display_width (~> 1.1, >= 1.1.1) 131 | tty-cursor (0.7.0) 132 | tty-screen (0.7.0) 133 | tty-spinner (0.9.1) 134 | tty-cursor (~> 0.7) 135 | uber (0.1.0) 136 | unf (0.1.4) 137 | unf_ext 138 | unf_ext (0.0.7.6) 139 | unicode-display_width (1.6.0) 140 | word_wrap (1.0.0) 141 | xcodeproj (1.12.0) 142 | CFPropertyList (>= 2.3.3, < 4.0) 143 | atomos (~> 0.1.3) 144 | claide (>= 1.0.2, < 2.0) 145 | colored2 (~> 3.1) 146 | nanaimo (~> 0.2.6) 147 | xcpretty (0.3.0) 148 | rouge (~> 2.0.7) 149 | xcpretty-travis-formatter (1.0.0) 150 | xcpretty (~> 0.2, >= 0.0.7) 151 | 152 | PLATFORMS 153 | ruby 154 | 155 | DEPENDENCIES 156 | fastlane 157 | 158 | BUNDLED WITH 159 | 1.17.3 160 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '11.0' 2 | 3 | target 'GJAssignment' do 4 | use_frameworks! 5 | 6 | pod 'MBProgressHUD', '~> 1.1.0' 7 | 8 | target 'GJAssignmentTests' do 9 | inherit! :search_paths 10 | # Pods for testing 11 | end 12 | 13 | end 14 | 15 | target 'GJAssignmentUITests' do 16 | inherit! :search_paths 17 | end 18 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MBProgressHUD (1.1.0) 3 | 4 | DEPENDENCIES: 5 | - MBProgressHUD (~> 1.1.0) 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - MBProgressHUD 10 | 11 | SPEC CHECKSUMS: 12 | MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9 13 | 14 | PODFILE CHECKSUM: ace7c7ec8272833eaeda20e1d138b7206d558901 15 | 16 | COCOAPODS: 1.7.5 17 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2009-2016 Matej Bukovinski 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MBProgressHUD (1.1.0) 3 | 4 | DEPENDENCIES: 5 | - MBProgressHUD (~> 1.1.0) 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - MBProgressHUD 10 | 11 | SPEC CHECKSUMS: 12 | MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9 13 | 14 | PODFILE CHECKSUM: ace7c7ec8272833eaeda20e1d138b7206d558901 15 | 16 | COCOAPODS: 1.7.5 17 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_MBProgressHUD : NSObject 3 | @end 4 | @implementation PodsDummy_MBProgressHUD 5 | @end 6 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "MBProgressHUD.h" 14 | 15 | FOUNDATION_EXPORT double MBProgressHUDVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char MBProgressHUDVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.modulemap: -------------------------------------------------------------------------------- 1 | framework module MBProgressHUD { 2 | umbrella header "MBProgressHUD-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "QuartzCore" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/MBProgressHUD 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## MBProgressHUD 5 | 6 | Copyright © 2009-2016 Matej Bukovinski 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | Generated by CocoaPods - https://cocoapods.org 26 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright © 2009-2016 Matej Bukovinski 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | License 37 | MIT 38 | Title 39 | MBProgressHUD 40 | Type 41 | PSGroupSpecifier 42 | 43 | 44 | FooterText 45 | Generated by CocoaPods - https://cocoapods.org 46 | Title 47 | 48 | Type 49 | PSGroupSpecifier 50 | 51 | 52 | StringsTable 53 | Acknowledgements 54 | Title 55 | Acknowledgements 56 | 57 | 58 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GJAssignment : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GJAssignment 5 | @end 6 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_GJAssignmentVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_GJAssignmentVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "MBProgressHUD" -framework "QuartzCore" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_GJAssignment { 2 | umbrella header "Pods-GJAssignment-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "MBProgressHUD" -framework "QuartzCore" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GJAssignmentTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GJAssignmentTests 5 | @end 6 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_GJAssignmentTestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_GJAssignmentTestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "MBProgressHUD" -framework "QuartzCore" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_GJAssignmentTests { 2 | umbrella header "Pods-GJAssignmentTests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "MBProgressHUD" -framework "QuartzCore" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GJAssignmentUITests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GJAssignmentUITests 5 | @end 6 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_GJAssignmentUITestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_GJAssignmentUITestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | PODS_BUILD_DIR = ${BUILD_DIR} 3 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 4 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_GJAssignmentUITests { 2 | umbrella header "Pods-GJAssignmentUITests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | PODS_BUILD_DIR = ${BUILD_DIR} 3 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 4 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/README.md: -------------------------------------------------------------------------------- 1 | ## 1. GO-JEK Assignment 2 | GO-JEK Assignment by [Anonymous](mailto://). 3 | 4 | ![](docs/Screenshot.png) 5 | 6 | ## 2. Requirements 7 | - iOS 11.0+ 8 | - [Xcode 10.3](https://download.developer.apple.com/Developer_Tools/Xcode_10.3/Xcode_10.3.xip) 9 | - [SwiftLint](https://github.com/realm/SwiftLint) 10 | - [CocoaPods](https://cocoapods.org/) 11 | - [Fastlane](https://fastlane.tools)(Optional) 12 | 13 | ## 3. Getting Start 14 | - Open `GJAssignment.xcworkspace` in Xcode 10.3 15 | - Build the project 16 | 17 | ## 4. Problem Statements 18 | [Problem Statements](docs/ProblemStatement.pdf) 19 | 20 | ## 5. Swift 21 | This project is build using Swift 5. 22 | 23 | ## 6. 3rd Party 24 | - [MBProgressHUD](https://github.com/jdg/MBProgressHUD) 25 | 26 | ​This project only using MBProgressHUD 3rd party dependency. Because less 3rd party means higher selection chances. I added this 3rd party just to demonstrate the use of the dependency manager (cocoapods). 27 | 28 | ## 7. Unit and UI Test case 29 | - Total number of test cases - **`34`** 30 | - Unit test cases - **`20`** 31 | - UI test cases - **`14`** 32 | - Code coverage - **`87.3%`** 33 | 34 | I'm not using any 3rd parting to mocking and stubs the objects. I build my own protocol based solution by creating fake `URLSession`. 35 | ![](docs/XcodeTestResult.png) 36 | ![](docs/FastlaneTestResult.png) 37 | 38 | ## 8. Architecture 39 | In this project, I'm using MVVM architecture without any Reactive 3rd party lib(like RxSwift etc.). For binding purposes, I'm using the custom binding class to bind properties and UI elements with ViewModels. These custom classes are available [here](GJAssignment/Utilities/Bindable). 40 | 41 | ## 9. Memory Management 42 | After continually using the app for 8+ mins, the app only gets memory leaks of total 224 bytes. This process includes the interaction with other apps like message app, phone app and Camera (`UIImagePickerController`). 43 | ![](docs/MemoryLeaks.png) 44 | 45 | ## 10. Extra Feature 46 | I'm using CoreData to store contacts locally. Which means you can view contacts offline but with limited functionality. Like you can't add new contact or update existing. 47 | 48 | ## 11. Fastlane 49 | - `fastlane tests` - Runs all the tests 50 | - `fastlane build` - Run all the tests and build 51 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/docs/FastlaneTestResult.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/docs/FastlaneTestResult.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/docs/MemoryLeaks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/docs/MemoryLeaks.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/docs/ProblemStatement.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/docs/ProblemStatement.pdf -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/docs/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/docs/Screenshot.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/docs/XcodeTestResult.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/GO-JEK-Assignment/docs/XcodeTestResult.png -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/fastlane/Appfile: -------------------------------------------------------------------------------- 1 | # app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app 2 | # apple_id("[[APPLE_ID]]") # Your Apple email address 3 | 4 | 5 | # For more information about the Appfile, see: 6 | # https://docs.fastlane.tools/advanced/#appfile 7 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | default_platform(:ios) 2 | 3 | platform :ios do 4 | desc "Runs all the tests" 5 | lane :tests do 6 | scan(devices:"iPhone X") 7 | end 8 | 9 | desc "Run all the tests and build" 10 | lane :build do 11 | scan(devices:"iPhone X") 12 | gym 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /Go-Jek/GO-JEK-Assignment/fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew cask install fastlane` 16 | 17 | # Available Actions 18 | ## iOS 19 | ### ios tests 20 | ``` 21 | fastlane ios tests 22 | ``` 23 | Runs all the tests 24 | ### ios build 25 | ``` 26 | fastlane ios build 27 | ``` 28 | Run all the tests and build 29 | ### ios update 30 | ``` 31 | fastlane ios update 32 | ``` 33 | Update 3rd party dependencies 34 | ### ios setup 35 | ``` 36 | fastlane ios setup 37 | ``` 38 | Setup Project 39 | 40 | ---- 41 | 42 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 43 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 44 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 45 | -------------------------------------------------------------------------------- /Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | environment/ 93 | 94 | # vscode 95 | .vscode/ 96 | 97 | # Spyder project settings 98 | .spyderproject 99 | .spyproject 100 | 101 | # Rope project settings 102 | .ropeproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | -------------------------------------------------------------------------------- /Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/Constants.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | CREATE_PARKING_LOT = 'create_parking_lot' 4 | PARK_CAR = 'park' 5 | CAR_DEPARTURE = 'leave' 6 | LOT_STATUS = 'status' 7 | SEARCH_SLOT_BY_COLOUR = 'slot_numbers_for_cars_with_colour' 8 | SEARCH_CAR_BY_COLOUR = 'registration_numbers_for_cars_with_colour' 9 | SEARCH_SLOT_BY_CAR_NUMBER = 'slot_number_for_registration_number' 10 | EXIT = 'exit' 11 | -------------------------------------------------------------------------------- /Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:alpine 2 | ADD . /app 3 | WORKDIR /app -------------------------------------------------------------------------------- /Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/ParkingLot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | from Constants import * 5 | from Utilities import (ParkingLot, create_parking_lot, park_car, car_departure, 6 | lot_status, car_by_colour, slot_by_car_number, slot_by_colour) 7 | 8 | 9 | def executeCommand(parkingLot, command): 10 | if command[0] == CREATE_PARKING_LOT: 11 | parkingLot = create_parking_lot(command[1]) 12 | elif command[0] == PARK_CAR: 13 | print(park_car(parkingLot, command[1], command[2])) 14 | elif command[0] == CAR_DEPARTURE: 15 | print(car_departure(parkingLot, command[1])) 16 | elif command[0] == LOT_STATUS: 17 | print(lot_status(parkingLot).rstrip('\n')) 18 | elif command[0] == SEARCH_SLOT_BY_CAR_NUMBER: 19 | print(slot_by_car_number(parkingLot, command[1]).rstrip(', ')) 20 | elif command[0] == SEARCH_CAR_BY_COLOUR: 21 | print(car_by_colour(parkingLot, command[1]).rstrip(', ')) 22 | elif command[0] == SEARCH_SLOT_BY_COLOUR: 23 | print(slot_by_colour(parkingLot, command[1]).rstrip(', ')) 24 | else: 25 | print('Command is not applicable') 26 | return parkingLot 27 | 28 | 29 | def commandMode(parkingLot): 30 | try: 31 | command = input().split() 32 | while command[0] != EXIT: 33 | parkingLot = executeCommand(parkingLot, command) 34 | command = input().split() 35 | except Exception as e: 36 | print(e) 37 | 38 | 39 | def fileReaderMode(parkingLot, fileName): 40 | try: 41 | with open(fileName) as file: 42 | commands = file.readlines() 43 | for command in commands: 44 | parkingLot = executeCommand( 45 | parkingLot, command.replace('\n', '').split()) 46 | except Exception as e: 47 | print(e) 48 | 49 | 50 | def main(): 51 | parkingLot = None 52 | if len(sys.argv) > 1: 53 | fileReaderMode(parkingLot, sys.argv[1]) 54 | else: 55 | commandMode(parkingLot) 56 | 57 | 58 | if __name__ == '__main__': 59 | main() 60 | -------------------------------------------------------------------------------- /Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/README.md: -------------------------------------------------------------------------------- 1 | # Go-Jek Parking Lot Assignment (Python) 2 | 3 | ## 1. Problem Statment 4 | Design a Parking lot which can hold `n` Cars. Every car been issued a ticket for a slot and the slot been assigned based on the nearest to the entry. The system should also return some queries such as: 5 | 6 | - Registration numbers of all cars of a particular colour. 7 | - Slot number in which a car with a given registration number is parked. 8 | - Slot numbers of all slots where a car of a particular colour is parked. 9 | 10 | ## 2. Solution Approach 11 | A car consist of Registration number, slot number and it's colour. Likewise our Parking Lot consist slots. For not making it too complicated, I choose a python dictionary for storing cars on slots and implemented the functionalities as accordingly. 12 | 13 | ## 3. Supported Commands 14 | 15 | - `create_parking_lot` <`n`> 16 | To create a Parking lot. Where `n` is the size of the parking lot 17 | 18 | - `park` <`registration_number`> <`colour`> 19 | To park the car in the parking lot and prints the allocated slot in the parking lot. Where `registration_number` is given registration number for the car and `colour` is given colour for the car 20 | 21 | - `leave` <`slot`> 22 | To leave the parking lot from desired slot and prints the leaving slot. given slot number. Where `slot` is given sloat number 23 | 24 | - `status` 25 | To check the status of Parking Lot 26 | 27 | - `slot_numbers_for_cars_with_colour` <`colour`> 28 | To prints the registration number of the cars for the given colour. Where `color` is given colour 29 | 30 | - `slot_number_for_registration_number` <`registration_number`> 31 | prints the slot number of the cars for the given number. Where `registration_number` is given registration number. 32 | 33 | - `registration_numbers_for_cars_with_colour` <`colour`> 34 | To prints the slot number of the cars for the given colour. Where `colour` is given colour. 35 | 36 | ## 4. Running Application 37 | #### 4.1 Running the application in File mode: 38 | 39 | ```python 40 | ./ParkingLot.py input.txt 41 | ``` 42 | 43 | #### 4.2 Running the application in Interactive mode: 44 | 45 | ```python 46 | ./ParkingLot.py 47 | ``` 48 | 49 | ## 5. Test Cases 50 | - Total number of test cases - 14 51 | - Code coverage - 86% 52 | 53 | #### 5.1 For running the tests 54 | 55 | ```python 56 | python Tests.py 57 | ``` 58 | 59 | #### 5.2 For calculating code coverage 60 | ```python 61 | coverage run Tests.py 62 | coverage report 63 | ``` 64 | ![](docs/CodeCoverage.png) 65 | 66 | ## 6. Running the application in a Docker Container 67 | 68 | #### Build the image: 69 | 70 | ```python 71 | docker build -t parkinglot:1.0 . 72 | ``` 73 | 74 | #### 6.1 Running the application in Interactive mode: 75 | 76 | ```python 77 | docker run -it parkinglot:1.0 ./ParkingLot.py 78 | ``` 79 | 80 | #### 6.2 Running the application in File mode: 81 | 82 | ```python 83 | docker run -it parkinglot:1.0 ./ParkingLot.py input.txt 84 | ``` 85 | 86 | ## 7. Possible errors 87 | 88 | When using running application in Docker, if it gives this error: 89 | 90 | ```bash 91 | docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"./ParkingLot.py\": permission denied": unknown. 92 | ``` 93 | 94 | just change the permissions by running 95 | 96 | ```bash 97 | sudo chmod +x ./ParkingLot.py 98 | ``` 99 | -------------------------------------------------------------------------------- /Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/Tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from Utilities import create_parking_lot, park_car, parking_lot_is_full, car_departure, \ 3 | car_by_colour, slot_by_car_number, slot_by_colour 4 | 5 | 6 | class TestParkingLotUtilities(unittest.TestCase): 7 | """ 8 | will test the endpoints 9 | """ 10 | 11 | def test_create_parking_lot(self): 12 | testParkingLot = create_parking_lot(str(6)) 13 | self.assertEqual(len(testParkingLot.get_slots()), 6) 14 | 15 | def test_parking_lot_is_full(self): 16 | testParkingLot = create_parking_lot(str(6)) 17 | self.assertEqual(parking_lot_is_full(testParkingLot), False) 18 | 19 | def test_park_car_lot_not_defined(self): 20 | testString = park_car(None, 'KA-01-AA-1111', 'White') 21 | self.assertEqual('Parking lot is not defined', testString) 22 | 23 | def test_park_car_lot_allocated(self): 24 | testParkingLot = create_parking_lot(str(6)) 25 | testString = park_car(testParkingLot, 'KA-01-AA-1112', 'White') 26 | self.assertEqual('Allocated slot number: 1', testString) 27 | 28 | def test_park_car_lot_full(self): 29 | testParkingLot = create_parking_lot(str(1)) 30 | testParkString = park_car(testParkingLot, 'KA-01-AA-1112', 'White') 31 | testString = park_car(testParkingLot, 'KA-01-AA-1113', 'White') 32 | self.assertEqual('Sorry, parking lot is full', testString) 33 | 34 | def test_car_departure_empty(self): 35 | testParkingLot = create_parking_lot(str(6)) 36 | testString = car_departure(testParkingLot, '1') 37 | self.assertEqual('Sorry, parking lot is empty', testString) 38 | 39 | def test_car_departure_free(self): 40 | testParkingLot = create_parking_lot(str(6)) 41 | testParkString = park_car(testParkingLot, 'KA-01-AA-1114', 'White') 42 | testString = car_departure(testParkingLot, '1') 43 | self.assertEqual('Slot number 1 is free', testString) 44 | 45 | def test_car_departure_cannot_exit(self): 46 | testParkingLot = create_parking_lot(str(6)) 47 | testParkString = park_car(testParkingLot, 'KA-01-AA-1115', 'White') 48 | testString = car_departure(testParkingLot, '7') 49 | self.assertEqual('Cannot exit slot: 7 as no such exist!', testString) 50 | 51 | def test_car_departure_slot_free(self): 52 | testParkingLot = create_parking_lot(str(6)) 53 | testParkString = park_car(testParkingLot, 'KA-01-AA-1116', 'White') 54 | testString = car_departure(testParkingLot, '2') 55 | self.assertEqual('No car at Slot number 2', testString) 56 | 57 | def test_car_departure_not_defined(self): 58 | testString = car_departure(None, '1') 59 | self.assertEqual('Parking lot is not defined', testString) 60 | 61 | def test_car_by_colour(self): 62 | testParkingLot = create_parking_lot(str(6)) 63 | testParkString = park_car(testParkingLot, 'KA-01-AA-1117', 'White') 64 | testString = car_by_colour(testParkingLot, 'White') 65 | self.assertEqual(testString, 'KA-01-AA-1117, ') 66 | 67 | def test_slot_by_car_number_not_found(self): 68 | testParkingLot = create_parking_lot(str(6)) 69 | testParkString = park_car(testParkingLot, 'KA-01-AA-1118', 'White') 70 | testString = slot_by_car_number(testParkingLot, 'KA-01-AA-1113') 71 | self.assertEqual(testString, 'Not found') 72 | 73 | def test_slot_by_car_number(self): 74 | testParkingLot = create_parking_lot(str(6)) 75 | testParkString = park_car(testParkingLot, 'KA-01-AA-1103', 'White') 76 | testString = slot_by_car_number(testParkingLot, 'KA-01-AA-1103') 77 | self.assertEqual(testString, '1, ') 78 | 79 | def test_slot_by_colour(self): 80 | testParkingLot = create_parking_lot(str(6)) 81 | testParkString = park_car(testParkingLot, 'KA-01-AA-1119', 'White') 82 | testString = slot_by_colour(testParkingLot, 'White') 83 | self.assertEqual(testString, '1, ') 84 | 85 | 86 | if __name__ == '__main__': 87 | unittest.main() 88 | -------------------------------------------------------------------------------- /Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/docs/CodeCoverage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerinsider/InterviewAssignments/ebb74bd25cdf3e1593c9b73a3be0f19c8284ea6c/Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/docs/CodeCoverage.png -------------------------------------------------------------------------------- /Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/input.txt: -------------------------------------------------------------------------------- 1 | leave 2 2 | park MH-04-AY-1111 Yellow 3 | create_parking_lot 6 4 | park HR-11-H-1234 White 5 | park HR-11-AA-9923 White 6 | park HR-11-BB-0031 Black 7 | park HR-13-DH-7237 Red 8 | park HR-15-DH-2731 Blue 9 | park HR-11-RH-3231 Black 10 | status 11 | leave 4 12 | status 13 | leave 2 14 | leave 20 15 | status 16 | park HR-01-PA-4333 White 17 | park DL-12-AA-8999 Black 18 | status 19 | registration_numbers_for_cars_with_colour White 20 | slot_numbers_for_cars_with_colour White 21 | slot_number_for_registration_number HR-11-RH-3231 22 | slot_number_for_registration_number DL-12-AA-8999 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Developer Insider 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-SEO.md: -------------------------------------------------------------------------------- 1 | # Interview Assignments 2 | List of Interview Assignments of companies directly submitted by the Interviewee. 3 | 4 | ## [GoJek Address Book Problem Assignment for iOS](/Go-Jek/GO-JEK-Assignment/) 5 | ## [GoJek Parking Lot Assignment using Python](/Go-Jek/Go-Jek-Parking-Lot-Assignment-Python/) 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interview Assignments 2 | List of Interview Assignments of companies directly submitted by the Interviewee. 3 | 4 | **Disclaimer**: These solutions are for reference purpose only. Please, do not copy-paste it. Companies are smart enough to catch if you are cheating. If your solution got rejected due to this we're not responsible for it. 5 | 6 | | Company | Assignment Title | Language | Code Coverage | Download | 7 | | -------------| ------------- | ------------- | ------------- | ------------- | 8 | | GoJek | [iOS Address Book](/Go-Jek/GO-JEK-Assignment) | Swift 5 | 87.3% | [![](https://img.shields.io/badge/download-Assignment-green?style=flat-square)](https://github.com/developerinsider/InterviewAssignments/releases/download/v1.0/GO-JEK-Assignment.zip) | 9 | | GoJek | [Parking Lot](/Go-Jek/Go-Jek-Parking-Lot-Assignment-Python) | Python | 86% | [![](https://img.shields.io/badge/download-Assignment-green?style=flat-square)](https://github.com/developerinsider/InterviewAssignments/releases/download/v1.0.1/Go-Jek-Parking-Lot-Assignment-Python.zip) | 10 | 11 | # Contribution 12 | Any contribution is more than welcome! You can contribute publiclly contribute in this repor via [pull request](https://github.com/developerinsider/InterviewAssignments/pulls). Or if you want to keep your self anonymous then you can submit your interview experience and assignment via file upload [here](https://link.developerinsider.co/InterviewFiles). 13 | 14 | # Bugs 15 | Please post any bugs to the [issue tracker](https://github.com/developerinsider/InterviewAssignments/issues) found on the project's GitHub page. Please include a description of what is not working right with your issue. You can also fix that bug and contribute via [pull request](https://github.com/developerinsider/InterviewAssignments/pulls). 16 | --------------------------------------------------------------------------------