├── 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 | 
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 | 
36 | 
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 | 
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 | 
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://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://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 |
--------------------------------------------------------------------------------